Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • appimage
  • check_uniq_node_by_endpoints
  • dev
  • feature/agent_architecture
  • feature/backend
  • fix_ci
  • fix_ci_osx
  • fix_dbus_error
  • fix_gitlab
  • fix_travis#1105
  • fixappveyor
  • gitlab
  • landscape
  • master
  • pyinstaller
  • pyqt5.6
  • qt5.7
  • qtwebengine
  • sakia020
  • translations
  • 0.1.0
  • 0.10.0
  • 0.10.1
  • 0.10.2
  • 0.11.0
  • 0.11.1
  • 0.11.2
  • 0.11.3
  • 0.11.4
  • 0.11.4.post1
  • 0.11.5
  • 0.12.0
  • 0.12.1dev1
  • 0.12.1dev2
  • 0.12.1dev3
  • 0.12.1dev4
  • 0.12.dev1
  • 0.12.dev2
  • 0.12.dev3
  • 0.12.dev4
  • 0.12.dev5
  • 0.12rc1
  • 0.2.0
  • 0.2.1
  • 0.20.0
  • 0.20.0dev1
  • 0.20.0dev10
  • 0.20.0dev12
  • 0.20.0dev13
  • 0.20.0dev14
  • 0.20.0dev15
  • 0.20.0dev16
  • 0.20.0dev2
  • 0.20.0dev3
  • 0.20.0dev3-test1
  • 0.20.0dev4
  • 0.20.0dev5
  • 0.20.0dev6
  • 0.20.0dev7
  • 0.20.0dev8
  • 0.20.0dev9
  • 0.20.1
  • 0.20.10
  • 0.20.11
  • 0.20.13
  • 0.20.2
  • 0.20.3
  • 0.20.4
  • 0.20.5
  • 0.20.6
  • 0.20.7
  • 0.20.8
  • 0.20.9
  • 0.3.0
  • 0.30.0
  • 0.30.0beta
  • 0.30.0beta1
  • 0.30.0beta2
  • 0.30.0beta3
  • 0.30.0beta4
  • 0.30.0beta5
  • 0.30.0beta6
  • 0.30.0beta7
  • 0.30.0beta8
  • 0.30.0beta9
  • 0.30.1
  • 0.30.10
  • 0.30.11
  • 0.30.12
  • 0.30.13
  • 0.30.14
  • 0.30.2
  • 0.30.3
  • 0.30.4
  • 0.30.5
  • 0.30.6
  • 0.30.7
  • 0.30.8
  • 0.30.9
  • 0.31.0
  • 0.31.0.post1
  • 0.31.1
  • 0.31.2
  • 0.31.3
  • 0.31.4
  • 0.31.5
  • 0.31.6
  • 0.32.0
  • 0.32.0-linux
  • 0.32.0RC1
  • 0.32.0RC2
  • 0.32.0RC3
  • 0.32.0RC4
  • 0.32.0RC5
  • 0.32.0RC6
  • 0.32.1
  • 0.32.10
  • 0.32.10post1
  • 0.32.2
  • 0.32.3
120 results

Target

Select target project
  • cebash/sakia
  • santiago/sakia
  • jonas/sakia
3 results
Select Git revision
  • Docker
  • Docker-debian9
  • check_uniq_node_by_endpoints
  • dev
  • feature/agent_architecture
  • feature/backend
  • fix_ci
  • fix_ci_osx
  • fix_dbus_error
  • fix_travis#1105
  • fixappveyor
  • gitlab
  • landscape
  • master
  • pyinstaller
  • pyqt5.6
  • qt5.7
  • qtwebengine
  • sakia020
  • translations
  • 0.1.0
  • 0.10.0
  • 0.10.1
  • 0.10.2
  • 0.11.0
  • 0.11.1
  • 0.11.2
  • 0.11.3
  • 0.11.4
  • 0.11.4.post1
  • 0.11.5
  • 0.12.0
  • 0.12.1dev1
  • 0.12.1dev2
  • 0.12.1dev3
  • 0.12.1dev4
  • 0.12.dev1
  • 0.12.dev2
  • 0.12.dev3
  • 0.12.dev4
  • 0.12.dev5
  • 0.12rc1
  • 0.2.0
  • 0.2.1
  • 0.20.0
  • 0.20.0dev1
  • 0.20.0dev10
  • 0.20.0dev12
  • 0.20.0dev13
  • 0.20.0dev14
  • 0.20.0dev15
  • 0.20.0dev16
  • 0.20.0dev2
  • 0.20.0dev3
  • 0.20.0dev3-test1
  • 0.20.0dev4
  • 0.20.0dev5
  • 0.20.0dev6
  • 0.20.0dev7
  • 0.20.0dev8
  • 0.20.0dev9
  • 0.20.1
  • 0.20.10
  • 0.20.11
  • 0.20.13
  • 0.20.2
  • 0.20.3
  • 0.20.4
  • 0.20.5
  • 0.20.6
  • 0.20.7
  • 0.20.8
  • 0.20.9
  • 0.3.0
  • 0.30.0
  • 0.30.0beta
  • 0.30.0beta1
  • 0.30.0beta2
  • 0.30.0beta3
  • 0.30.0beta4
  • 0.30.0beta5
  • 0.30.0beta6
  • 0.30.0beta7
  • 0.30.0beta8
  • 0.30.0beta9
  • 0.30.1
  • 0.30.10
  • 0.30.11
  • 0.30.12
  • 0.30.13
  • 0.30.14
  • 0.30.2
  • 0.30.3
  • 0.30.4
  • 0.30.5
  • 0.30.6
  • 0.30.7
  • 0.30.8
  • 0.30.9
  • 0.31.0
  • 0.31.0.post1
  • 0.31.1
  • 0.31.2
  • 0.31.3
  • 0.31.4
  • 0.31.5
  • 0.31.6
  • 0.32.0
  • 0.32.0-linux
  • 0.32.0RC1
  • 0.32.0RC2
  • 0.32.0RC3
  • 0.32.0RC4
  • 0.32.0RC5
  • 0.32.0RC6
  • 0.32.1
  • 0.32.10
  • 0.32.10post1
  • 0.32.2
  • 0.32.3
120 results
Show changes
Showing
with 603 additions and 432 deletions
......@@ -5,45 +5,45 @@ from duniterpy.documents import block_uid, BlockUID
@attr.s(hash=False)
class BlockchainParameters:
# The decimal percent growth of the UD every [dt] period
c = attr.ib(convert=float, default=0, cmp=False, hash=False)
c = attr.ib(converter=float, default=0, cmp=False, hash=False)
# Time period between two UD in seconds
dt = attr.ib(convert=int, default=0, cmp=False, hash=False)
dt = attr.ib(converter=int, default=0, cmp=False, hash=False)
# UD(0), i.e. initial Universal Dividend
ud0 = attr.ib(convert=int, default=0, cmp=False, hash=False)
ud0 = attr.ib(converter=int, default=0, cmp=False, hash=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, hash=False)
sig_period = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Maximum quantity of active certifications made by member
sig_stock = attr.ib(convert=int, default=0, cmp=False, hash=False)
# Maximum age of a active signature (in seconds)
sig_validity = attr.ib(convert=int, default=0, cmp=False, hash=False)
# Minimum quantity of signatures to be part of the WoT
sig_qty = attr.ib(convert=int, default=0, cmp=False, hash=False)
sig_stock = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Maximum validity time of an active certification (in seconds)
sig_validity = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Minimum quantity of certifications to be part of the WoT
sig_qty = attr.ib(converter=int, default=0, cmp=False, hash=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, hash=False)
sig_window = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Maximum delay in seconds an identity can wait before being expired for non-writing
idty_window = attr.ib(convert=int, default=0, cmp=False, hash=False)
idty_window = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Maximum delay in seconds a membership can wait before being expired for non-writing
ms_window = attr.ib(convert=int, default=0, cmp=False, hash=False)
ms_window = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Minimum decimal percent of sentries to reach to match the distance rule
xpercent = attr.ib(convert=float, default=0, cmp=False, hash=False)
# Maximum age of an active membership( in seconds)
ms_validity = attr.ib(convert=int, default=0, cmp=False, hash=False)
xpercent = attr.ib(converter=float, default=0, cmp=False, hash=False)
# Maximum validity time of an active membership (in seconds)
ms_validity = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Maximum distance between each WoT member and a newcomer
step_max = attr.ib(convert=int, default=0, cmp=False, hash=False)
step_max = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Number of blocks used for calculating median time
median_time_blocks = attr.ib(convert=int, default=0, cmp=False, hash=False)
median_time_blocks = attr.ib(converter=int, default=0, cmp=False, hash=False)
# The average time for writing 1 block (wished time) in seconds
avg_gen_time = attr.ib(convert=int, default=0, cmp=False, hash=False)
avg_gen_time = attr.ib(converter=int, default=0, cmp=False, hash=False)
# The number of blocks required to evaluate again PoWMin value
dt_diff_eval = attr.ib(convert=int, default=0, cmp=False, hash=False)
dt_diff_eval = attr.ib(converter=int, default=0, cmp=False, hash=False)
# The decimal percent of previous issuers to reach for personalized difficulty
percent_rot = attr.ib(convert=float, default=0, cmp=False, hash=False)
percent_rot = attr.ib(converter=float, default=0, cmp=False, hash=False)
# The first UD time
ud_time_0 = attr.ib(convert=int, default=0, cmp=False, hash=False)
ud_time_0 = attr.ib(converter=int, default=0, cmp=False, hash=False)
# The first UD reavallued
ud_reeval_time_0 = attr.ib(convert=int, default=0, cmp=False, hash=False)
ud_reeval_time_0 = attr.ib(converter=int, default=0, cmp=False, hash=False)
# The dt recomputation of the ud
dt_reeval = attr.ib(convert=int, default=0, cmp=False, hash=False)
dt_reeval = attr.ib(converter=int, default=0, cmp=False, hash=False)
@attr.s(hash=True)
......@@ -51,32 +51,32 @@ class Blockchain:
# Parameters in block 0
parameters = attr.ib(default=BlockchainParameters(), cmp=False, hash=False)
# block number and hash
current_buid = attr.ib(convert=block_uid, default=BlockUID.empty())
current_buid = attr.ib(converter=block_uid, default=BlockUID.empty())
# Number of members
current_members_count = attr.ib(convert=int, default=0, cmp=False, hash=False)
current_members_count = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Current monetary mass in units
current_mass = attr.ib(convert=int, default=0, cmp=False, hash=False)
current_mass = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Median time in seconds
median_time = attr.ib(convert=int, default=0, cmp=False, hash=False)
median_time = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Last members count
last_mass = attr.ib(convert=int, default=0, cmp=False, hash=False)
last_mass = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Last members count
last_members_count = attr.ib(convert=int, default=0, cmp=False, hash=False)
last_members_count = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Last UD amount in units (multiply by 10^base)
last_ud = attr.ib(convert=int, default=1, cmp=False, hash=False)
last_ud = attr.ib(converter=int, default=1, cmp=False, hash=False)
# Last UD base
last_ud_base = attr.ib(convert=int, default=0, cmp=False, hash=False)
last_ud_base = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Last UD base
last_ud_time = attr.ib(convert=int, default=0, cmp=False, hash=False)
last_ud_time = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Previous monetary mass in units
previous_mass = attr.ib(convert=int, default=0, cmp=False, hash=False)
previous_mass = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Previous members count
previous_members_count = attr.ib(convert=int, default=0, cmp=False, hash=False)
previous_members_count = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Previous UD amount in units (multiply by 10^base)
previous_ud = attr.ib(convert=int, default=0, cmp=False, hash=False)
previous_ud = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Previous UD base
previous_ud_base = attr.ib(convert=int, default=0, cmp=False, hash=False)
previous_ud_base = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Previous UD base
previous_ud_time = attr.ib(convert=int, default=0, cmp=False, hash=False)
previous_ud_time = attr.ib(converter=int, default=0, cmp=False, hash=False)
# Currency name
currency = attr.ib(convert=str, default="", cmp=False, hash=False)
currency = attr.ib(converter=str, default="", cmp=False, hash=False)
......@@ -4,10 +4,10 @@ from duniterpy.documents import block_uid, BlockUID
@attr.s(hash=True)
class Certification:
currency = attr.ib(convert=str)
certifier = attr.ib(convert=str)
certified = attr.ib(convert=str)
block = attr.ib(convert=int)
timestamp = attr.ib(convert=int, cmp=False)
signature = attr.ib(convert=str, cmp=False, hash=False)
written_on = attr.ib(convert=int, default=-1, cmp=False, hash=False)
currency = attr.ib(converter=str)
certifier = attr.ib(converter=str)
certified = attr.ib(converter=str)
block = attr.ib(converter=int)
timestamp = attr.ib(converter=int, cmp=False)
signature = attr.ib(converter=str, cmp=False, hash=False)
written_on = attr.ib(converter=int, default=-1, cmp=False, hash=False)
import attr
from duniterpy.documents import block_uid, BlockUID
from duniterpy.key import ScryptParams
from duniterpy.key.scrypt_params import ScryptParams, SCRYPT_PARAMS
@attr.s(hash=True)
......@@ -10,15 +10,18 @@ class Connection:
It is defined by the currency name, and the key informations
used to connect to it. If the user is using an identity, it is defined here too.
"""
currency = attr.ib(convert=str)
pubkey = attr.ib(convert=str)
uid = attr.ib(convert=str, default="", cmp=False, hash=False)
scrypt_N = attr.ib(convert=int, default=4096, cmp=False, hash=False)
scrypt_r = attr.ib(convert=int, default=16, cmp=False, hash=False)
scrypt_p = attr.ib(convert=int, default=1, cmp=False, hash=False)
blockstamp = attr.ib(convert=block_uid, default=BlockUID.empty(), cmp=False, hash=False)
salt = attr.ib(convert=str, init=False, default="", cmp=False, hash=False)
password = attr.ib(init=False, convert=str, default="", cmp=False, hash=False)
currency = attr.ib(converter=str)
pubkey = attr.ib(converter=str)
uid = attr.ib(converter=str, default="", cmp=False, hash=False)
scrypt_N = attr.ib(converter=int, default=SCRYPT_PARAMS["N"], cmp=False, hash=False)
scrypt_r = attr.ib(converter=int, default=SCRYPT_PARAMS["r"], cmp=False, hash=False)
scrypt_p = attr.ib(converter=int, default=SCRYPT_PARAMS["p"], cmp=False, hash=False)
blockstamp = attr.ib(
converter=block_uid, default=BlockUID.empty(), cmp=False, hash=False
)
salt = attr.ib(converter=str, init=False, default="", cmp=False, hash=False)
password = attr.ib(init=False, converter=str, default="", cmp=False, hash=False)
def is_identity(self):
return self.uid is not ""
......
......@@ -8,14 +8,14 @@ class Contact:
"""
A contact in the network currency
"""
re_displayed_text = re.compile("([\w\s\d]+) < ((?![OIl])[1-9A-Za-z]{42,45}) >")
currency = attr.ib(convert=str)
name = attr.ib(convert=str)
pubkey = attr.ib(convert=str)
fields = attr.ib(convert=attrs_tuple_of_str, default="")
contact_id = attr.ib(convert=int, default=-1)
currency = attr.ib(converter=str)
name = attr.ib(converter=str)
pubkey = attr.ib(converter=str)
fields = attr.ib(converter=attrs_tuple_of_str, default="")
contact_id = attr.ib(converter=int, default=-1)
def displayed_text(self):
return self.name + " < " + self.pubkey + " > "
......@@ -3,9 +3,9 @@ import attr
@attr.s(hash=True)
class Dividend:
currency = attr.ib(convert=str, cmp=True, hash=True)
pubkey = attr.ib(convert=str, cmp=True, hash=True)
block_number = attr.ib(convert=int, cmp=True, hash=True)
timestamp = attr.ib(convert=int)
amount = attr.ib(convert=int, cmp=False, hash=False)
base = attr.ib(convert=int, cmp=False, hash=False)
currency = attr.ib(converter=str, cmp=True, hash=True)
pubkey = attr.ib(converter=str, cmp=True, hash=True)
block_number = attr.ib(converter=int, cmp=True, hash=True)
timestamp = attr.ib(converter=int)
amount = attr.ib(converter=int, cmp=False, hash=False)
base = attr.ib(converter=int, cmp=False, hash=False)
......@@ -5,22 +5,35 @@ from duniterpy.documents import Identity as IdentityDoc
@attr.s(hash=True)
class Identity:
currency = attr.ib(convert=str)
pubkey = attr.ib(convert=str)
uid = attr.ib(convert=str, default="")
blockstamp = attr.ib(convert=block_uid, default=BlockUID.empty())
signature = attr.ib(convert=str, default="", cmp=False, hash=False)
currency = attr.ib(converter=str)
pubkey = attr.ib(converter=str)
uid = attr.ib(converter=str, default="")
blockstamp = attr.ib(converter=block_uid, default=BlockUID.empty())
signature = attr.ib(converter=str, default="", cmp=False, hash=False)
# Mediantime of the block referenced by blockstamp
timestamp = attr.ib(convert=int, default=0, cmp=False, hash=False)
written = attr.ib(convert=bool, default=False, cmp=False, hash=False)
revoked_on = attr.ib(convert=int, default=0, cmp=False, hash=False)
outdistanced = attr.ib(convert=bool, default=True, cmp=False, hash=False)
member = attr.ib(validator=attr.validators.instance_of(bool), default=False, cmp=False, hash=False)
membership_buid = attr.ib(convert=block_uid, default=BlockUID.empty(), cmp=False, hash=False)
membership_timestamp = attr.ib(convert=int, default=0, cmp=False, hash=False)
membership_type = attr.ib(convert=str, default='', validator=lambda s, a, t: t in ('', 'IN', 'OUT'), cmp=False, hash=False)
membership_written_on = attr.ib(convert=int, default=0, cmp=False, hash=False)
sentry = attr.ib(convert=bool, default=False, cmp=False, hash=False)
timestamp = attr.ib(converter=int, default=0, cmp=False, hash=False)
written = attr.ib(converter=bool, default=False, cmp=False, hash=False)
revoked_on = attr.ib(converter=int, default=0, cmp=False, hash=False)
outdistanced = attr.ib(converter=bool, default=True, cmp=False, hash=False)
member = attr.ib(
validator=attr.validators.instance_of(bool),
default=False,
cmp=False,
hash=False,
)
membership_buid = attr.ib(
converter=block_uid, default=BlockUID.empty(), cmp=False, hash=False
)
membership_timestamp = attr.ib(converter=int, default=0, cmp=False, hash=False)
membership_written_on = attr.ib(converter=int, default=0, cmp=False, hash=False)
membership_type = attr.ib(
converter=str,
default="",
validator=lambda s, a, t: t in ("", "IN", "OUT"),
cmp=False,
hash=False,
)
sentry = attr.ib(converter=bool, default=False, cmp=False, hash=False)
def document(self):
"""
......@@ -29,7 +42,9 @@ class Identity:
:return: the document
:rtype: duniterpy.documents.Identity
"""
return IdentityDoc(10, self.currency, self.pubkey, self.uid, self.blockstamp, self.signature)
return IdentityDoc(
10, self.currency, self.pubkey, self.uid, self.blockstamp, self.signature
)
def is_obsolete(self, sig_window, current_time):
expired = self.timestamp + sig_window <= current_time
......
import attr
from duniterpy.documents import block_uid, endpoint
from duniterpy.documents import block_uid
from duniterpy.api.endpoint import endpoint
from sakia.helpers import attrs_tuple_of_str
def _tuple_of_endpoints(value):
if isinstance(value, tuple):
if isinstance(value, tuple) or isinstance(value, list):
return value
elif isinstance(value, list):
l = [endpoint(e) for e in value]
return tuple(l)
elif isinstance(value, str):
if value:
list_of_str = value.split('\n')
list_of_str = value.split("\n")
conv = []
for s in list_of_str:
conv.append(endpoint(s))
......@@ -27,48 +25,53 @@ class Node:
"""
A node can have multiple states :
- ONLINE : The node is available for requests
- OFFLINE: The node is disconnected
- ONLINE <= 3: The node is available for requests
- OFFLINE > 3: The node is disconnected
- DESYNCED: The node is online but is desynced from the network
- CORRUPTED : The node is corrupted, some weird behaviour is going on
"""
MERKLE_EMPTY_ROOT = "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
ONLINE = 1
OFFLINE = 2
CORRUPTED = 4
MERKLE_EMPTY_ROOT = (
"01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
)
FAILURE_THRESHOLD = 3
def online(self):
return self.state <= Node.FAILURE_THRESHOLD
# The currency handled by this node
currency = attr.ib(convert=str)
currency = attr.ib(converter=str)
# The pubkey of the node
pubkey = attr.ib(convert=str)
pubkey = attr.ib(converter=str)
# The endpoints of the node, in a list of Endpoint objects format
endpoints = attr.ib(convert=_tuple_of_endpoints, cmp=False, hash=False)
endpoints = attr.ib(converter=_tuple_of_endpoints, cmp=False, hash=False)
# The previous block uid in /blockchain/current
peer_blockstamp = attr.ib(convert=block_uid, cmp=False, hash=False)
peer_blockstamp = attr.ib(converter=block_uid, cmp=False, hash=False)
# The uid of the owner of node
uid = attr.ib(convert=str, cmp=False, default="", hash=False)
uid = attr.ib(converter=str, cmp=False, default="", hash=False)
# The current block uid in /blockchain/current
current_buid = attr.ib(convert=block_uid, cmp=False, default=None, hash=False)
current_buid = attr.ib(converter=block_uid, cmp=False, default=None, hash=False)
# The current block time in /blockchain/current
current_ts = attr.ib(convert=int, cmp=False, default=0, hash=False)
current_ts = attr.ib(converter=int, cmp=False, default=0, hash=False)
# The previous block uid in /blockchain/current
previous_buid = attr.ib(convert=block_uid, cmp=False, default=None, hash=False)
previous_buid = attr.ib(converter=block_uid, cmp=False, default=None, hash=False)
# The state of the node in Sakia
state = attr.ib(convert=int, cmp=False, default=OFFLINE, hash=False)
state = attr.ib(converter=int, cmp=False, default=0, hash=False)
# The version of the software in /node/summary
software = attr.ib(convert=str, cmp=False, default="", hash=False)
software = attr.ib(converter=str, cmp=False, default="", hash=False)
# The version of the software in /node/summary
version = attr.ib(convert=str, cmp=False, default="", hash=False)
version = attr.ib(converter=str, cmp=False, default="", hash=False)
# Root of the merkle peers tree, default = sha256 of empty string
merkle_peers_root = attr.ib(convert=str, cmp=False,
default=MERKLE_EMPTY_ROOT, hash=False)
merkle_peers_root = attr.ib(
converter=str, cmp=False, default=MERKLE_EMPTY_ROOT, hash=False
)
# Leaves of the merkle peers tree
merkle_peers_leaves = attr.ib(convert=attrs_tuple_of_str, cmp=False, default=tuple(), hash=False)
merkle_peers_leaves = attr.ib(
converter=attrs_tuple_of_str, cmp=False, default=tuple(), hash=False
)
# Define if this node is a root node in Sakia
root = attr.ib(convert=bool, cmp=False, default=False, hash=False)
root = attr.ib(converter=bool, cmp=False, default=False, hash=False)
# If this node is a member or not
member = attr.ib(convert=bool, cmp=False, default=False, hash=False)
member = attr.ib(converter=bool, cmp=False, default=False, hash=False)
# If this node is a member or not
last_state_change = attr.ib(convert=int, cmp=False, default=False, hash=False)
last_state_change = attr.ib(converter=int, cmp=False, default=False, hash=False)
......@@ -3,10 +3,16 @@ import attr
@attr.s(hash=True)
class Source:
currency = attr.ib(convert=str)
pubkey = attr.ib(convert=str)
identifier = attr.ib(convert=str)
noffset = attr.ib(convert=int)
type = attr.ib(convert=str, validator=lambda i, a, s: s == 'T' or s == 'D')
amount = attr.ib(convert=int, hash=False)
base = attr.ib(convert=int, hash=False)
TYPE_TRANSACTION = "T"
TYPE_DIVIDEND = "D"
currency = attr.ib(converter=str)
pubkey = attr.ib(converter=str)
identifier = attr.ib(converter=str)
noffset = attr.ib(converter=int)
type = attr.ib(converter=str, validator=lambda i, a, s: s == "T" or s == "D")
amount = attr.ib(converter=int, hash=False)
base = attr.ib(converter=int, hash=False)
conditions = attr.ib(converter=str, hash=False)
used_by = attr.ib(hash=False, default=None)
import attr
from duniterpy.documents import block_uid
import hashlib
from duniterpy.documents import block_uid, BlockUID
from duniterpy.documents import Transaction as TransactionDoc
from duniterpy.documents.transaction import reduce_base
from sakia.helpers import attrs_tuple_of_str
import math
STOPLINE_HASH = hashlib.sha256("STOPLINE".encode("UTF-8")).hexdigest()
def parse_transaction_doc(tx_doc, pubkey, block_number, mediantime, txid):
"""
Parse a transaction
......@@ -16,14 +20,17 @@ def parse_transaction_doc(tx_doc, pubkey, block_number, mediantime, txid):
:param int txid: The latest txid
:return: the found transaction
"""
receivers = [o.conditions.left.pubkey for o in tx_doc.outputs
if o.conditions.left.pubkey != tx_doc.issuers[0]]
receivers = [
o.condition.left.pubkey
for o in tx_doc.outputs
if o.condition.left.pubkey != tx_doc.issuers[0]
]
in_issuers = len([i for i in tx_doc.issuers
if i == pubkey]) > 0
in_issuers = len([i for i in tx_doc.issuers if i == pubkey]) > 0
in_outputs = len([o for o in tx_doc.outputs
if o.conditions.left.pubkey == pubkey]) > 0
in_outputs = (
len([o for o in tx_doc.outputs if o.condition.left.pubkey == pubkey]) > 0
)
if len(receivers) == 0 and in_issuers:
receivers = [tx_doc.issuers[0]]
......@@ -36,16 +43,14 @@ def parse_transaction_doc(tx_doc, pubkey, block_number, mediantime, txid):
elif in_issuers or in_outputs:
# If the wallet pubkey is in the issuers we sent this transaction
if in_issuers:
outputs = [o for o in tx_doc.outputs
if o.conditions.left.pubkey != pubkey]
outputs = [o for o in tx_doc.outputs if o.condition.left.pubkey != pubkey]
amount = 0
for o in outputs:
amount += o.amount * math.pow(10, o.base)
# If we are not in the issuers,
# maybe we are in the recipients of this transaction
else:
outputs = [o for o in tx_doc.outputs
if o.conditions.left.pubkey == pubkey]
outputs = [o for o in tx_doc.outputs if o.condition.left.pubkey == pubkey]
amount = 0
for o in outputs:
amount += o.amount * math.pow(10, o.base)
......@@ -53,7 +58,8 @@ def parse_transaction_doc(tx_doc, pubkey, block_number, mediantime, txid):
else:
return None
transaction = Transaction(currency=tx_doc.currency,
transaction = Transaction(
currency=tx_doc.currency,
pubkey=pubkey,
sha_hash=tx_doc.sha_hash,
written_block=block_number,
......@@ -67,7 +73,35 @@ def parse_transaction_doc(tx_doc, pubkey, block_number, mediantime, txid):
comment=tx_doc.comment,
txid=txid,
state=Transaction.VALIDATED,
raw=tx_doc.signed_raw())
raw=tx_doc.signed_raw(),
)
return transaction
STOPLINE_HASH = hashlib.sha256("STOPLINE".encode("UTF-8")).hexdigest()
def build_stopline(currency, pubkey, block_number, mediantime):
"""
Used to insert a line of ignored tx in the history
"""
transaction = Transaction(
currency=currency,
pubkey=pubkey,
sha_hash=STOPLINE_HASH,
written_block=block_number,
blockstamp=BlockUID(block_number, BlockUID.empty().sha_hash),
timestamp=mediantime,
signatures="",
issuers="",
receivers="",
amount=0,
amount_base=0,
comment="",
txid=0,
state=Transaction.VALIDATED,
raw="",
)
return transaction
......@@ -77,41 +111,45 @@ class Transaction:
Transaction entity
:param str currency: the currency of the transaction
:param str pubkey: the pubkey of the issuer
:param str sha_hash: the hash of the transaction
:param int written_block: the number of the block
:param duniterpy.documents.BlockUID blockstamp: the blockstamp of the transaction
:param int timestamp: the timestamp of the transaction
:param str signature: the signature
:param str issuer: the pubkey of the issuer
:param str receiver: the pubkey of the receiver
:param str signatures: the signature
:param tuple issuers: tuple of pubkey of the issuers
:param tuple receivers: tuple of pubkey of the receivers
:param int amount: the amount
:param int amount_base: the amount base
:param str comment: a comment
:param str txid: the transaction id to sort transctions
:param int txid: the transaction id to sort transctions
:param int state: the state of the transaction
:param bool local: is the transaction local
:param str raw: the raw string of the transaction
"""
TO_SEND = 0
AWAITING = 1
VALIDATED = 4
REFUSED = 8
DROPPED = 16
currency = attr.ib(convert=str, cmp=True, hash=True)
pubkey = attr.ib(convert=str, cmp=True, hash=True)
sha_hash = attr.ib(convert=str, cmp=True, hash=True)
written_block = attr.ib(convert=int, cmp=False)
blockstamp = attr.ib(convert=block_uid, cmp=False)
timestamp = attr.ib(convert=int, cmp=False)
signatures = attr.ib(convert=attrs_tuple_of_str, cmp=False)
issuers = attr.ib(convert=attrs_tuple_of_str, cmp=False)
receivers = attr.ib(convert=attrs_tuple_of_str, cmp=False)
amount = attr.ib(convert=int, cmp=False)
amount_base = attr.ib(convert=int, cmp=False)
comment = attr.ib(convert=str, cmp=False)
txid = attr.ib(convert=int, cmp=False)
state = attr.ib(convert=int, cmp=False)
local = attr.ib(convert=bool, cmp=False, default=False)
raw = attr.ib(convert=str, cmp=False, default="")
currency = attr.ib(converter=str, cmp=True, hash=True)
pubkey = attr.ib(converter=str, cmp=True, hash=True)
sha_hash = attr.ib(converter=str, cmp=True, hash=True)
written_block = attr.ib(converter=int, cmp=False)
blockstamp = attr.ib(converter=block_uid, cmp=False)
timestamp = attr.ib(converter=int, cmp=False)
signatures = attr.ib(converter=attrs_tuple_of_str, cmp=False)
issuers = attr.ib(converter=attrs_tuple_of_str, cmp=False)
receivers = attr.ib(converter=attrs_tuple_of_str, cmp=False)
amount = attr.ib(converter=int, cmp=False)
amount_base = attr.ib(converter=int, cmp=False)
comment = attr.ib(converter=str, cmp=False)
txid = attr.ib(converter=int, cmp=False)
state = attr.ib(converter=int, cmp=False)
local = attr.ib(converter=bool, cmp=False, default=False)
raw = attr.ib(converter=str, cmp=False, default="")
def txdoc(self):
"""
......
......@@ -6,27 +6,30 @@ class UserParameters:
"""
The user parameters entity
"""
profile_name = attr.ib(convert=str, default="Default Profile")
lang = attr.ib(convert=str, default="en")
referential = attr.ib(convert=int, default=0)
expert_mode = attr.ib(convert=bool, default=False)
digits_after_comma = attr.ib(convert=int, default=2)
maximized = attr.ib(convert=bool, default=False)
notifications = attr.ib(convert=bool, default=True)
enable_proxy = attr.ib(convert=bool, default=False)
proxy_type = attr.ib(convert=int, default=0)
proxy_address = attr.ib(convert=str, default="")
proxy_port = attr.ib(convert=int, default=8080)
proxy_user = attr.ib(convert=str, default="")
proxy_password = attr.ib(convert=str, default="")
dark_theme = attr.ib(convert=bool, default=False)
profile_name = attr.ib(converter=str, default="Default Profile")
lang = attr.ib(converter=str, default="en")
referential = attr.ib(converter=int, default=0)
expert_mode = attr.ib(converter=bool, default=False)
digits_after_comma = attr.ib(converter=int, default=2)
maximized = attr.ib(converter=bool, default=False)
notifications = attr.ib(converter=bool, default=True)
enable_proxy = attr.ib(converter=bool, default=False)
proxy_type = attr.ib(converter=int, default=0)
proxy_address = attr.ib(converter=str, default="")
proxy_port = attr.ib(converter=int, default=8080)
proxy_user = attr.ib(converter=str, default="")
proxy_password = attr.ib(converter=str, default="")
dark_theme = attr.ib(converter=bool, default=False)
def proxy(self):
if self.enable_proxy is True:
if self.proxy_user and self.proxy_password:
return "http://{:}:{:}@{:}:{:}".format(self.proxy_user,
return "http://{:}:{:}@{:}:{:}".format(
self.proxy_user,
self.proxy_password,
self.proxy_address,
self.proxy_port)
self.proxy_port,
)
else:
return "http://{0}:{1}".format(self.proxy_address, self.proxy_port)
......@@ -10,8 +10,9 @@ class AppDataFile:
"""
The repository for AppData
"""
_file = attr.ib()
_logger = attr.ib(default=attr.Factory(lambda: logging.getLogger('sakia')))
_logger = attr.ib(default=attr.Factory(lambda: logging.getLogger("sakia")))
filename = "appdata.json"
@classmethod
......@@ -23,7 +24,7 @@ class AppDataFile:
Commit a app_data to the database
:param sakia.data.entities.AppData app_data: the app_data to commit
"""
with open(self._file, 'w') as outfile:
with open(self._file, "w") as outfile:
json.dump(attr.asdict(app_data), outfile, indent=4)
def load_or_init(self):
......@@ -32,7 +33,7 @@ class AppDataFile:
:param sakia.data.entities.AppData app_data: the app_data to update
"""
try:
with open(self._file, 'r') as json_data:
with open(self._file, "r") as json_data:
app_data = AppData(**json.load(json_data))
except OSError:
app_data = AppData()
......
......@@ -12,10 +12,11 @@ class PluginsDirectory:
"""
The repository for UserParameters
"""
_path = attr.ib()
plugins = attr.ib(default=[])
with_plugin = attr.ib(default=None)
_logger = attr.ib(default=attr.Factory(lambda: logging.getLogger('sakia')))
_logger = attr.ib(default=attr.Factory(lambda: logging.getLogger("sakia")))
@classmethod
def in_config_path(cls, config_path, profile_name="Default Profile"):
......@@ -35,29 +36,38 @@ class PluginsDirectory:
module_name = os.path.splitext(os.path.basename(file))[0]
try:
plugin_module = importlib.import_module(module_name)
self.plugins.append(Plugin(plugin_module.PLUGIN_NAME,
self.plugins.append(
Plugin(
plugin_module.PLUGIN_NAME,
plugin_module.PLUGIN_DESCRIPTION,
plugin_module.PLUGIN_VERSION,
True,
plugin_module,
file))
file,
)
)
except ImportError as e:
self.plugins.append(Plugin(module_name, "", "",
False, None, file))
self.plugins.append(
Plugin(module_name, "", "", False, None, file)
)
self._logger.debug(str(e) + " with sys.path " + str(sys.path))
if with_plugin:
sys.path.append(with_plugin)
module_name = os.path.splitext(os.path.basename(with_plugin))[0]
try:
plugin_module = importlib.import_module(module_name)
self.with_plugin = Plugin(plugin_module.PLUGIN_NAME,
self.with_plugin = Plugin(
plugin_module.PLUGIN_NAME,
plugin_module.PLUGIN_DESCRIPTION,
plugin_module.PLUGIN_VERSION,
True,
plugin_module,
with_plugin)
with_plugin,
)
except ImportError as e:
self.with_plugin = Plugin(module_name, "", "", False, None, with_plugin)
self.with_plugin = Plugin(
module_name, "", "", False, None, with_plugin
)
self._logger.debug(str(e) + " with sys.path " + str(sys.path))
except OSError as e:
self._logger.debug(str(e))
......
import attr
import json
import os
import logging
@attr.s(frozen=True)
class RootServersFile:
"""
The repository for RootServers
"""
_file = attr.ib()
_logger = attr.ib(default=attr.Factory(lambda: logging.getLogger("sakia")))
filename = "root_servers.yml"
@classmethod
def in_config_path(cls, config_path, profile_name):
if not os.path.exists(os.path.join(config_path, profile_name)):
os.makedirs(os.path.join(config_path, profile_name))
return cls(os.path.join(config_path, profile_name, RootServersFile.filename))
def load_or_init(self, profile_name):
"""
Update root_servers constant
:param sakia.data.entities.UserParameters user_parameters: the user_parameters to update
"""
try:
with open(self._file, "r") as json_data:
user_parameters = UserParameters(**json.load(json_data))
user_parameters.profile_name = profile_name
except (OSError, json.decoder.JSONDecodeError):
user_parameters = UserParameters(profile_name=profile_name)
return user_parameters
......@@ -10,8 +10,9 @@ class UserParametersFile:
"""
The repository for UserParameters
"""
_file = attr.ib()
_logger = attr.ib(default=attr.Factory(lambda: logging.getLogger('sakia')))
_logger = attr.ib(default=attr.Factory(lambda: logging.getLogger("sakia")))
filename = "parameters.json"
@classmethod
......@@ -27,7 +28,7 @@ class UserParametersFile:
"""
if not os.path.exists(os.path.abspath(os.path.join(self._file, os.pardir))):
os.makedirs(os.path.abspath(os.path.join(self._file, os.pardir)))
with open(self._file, 'w') as outfile:
with open(self._file, "w") as outfile:
json.dump(attr.asdict(user_parameters), outfile, indent=4)
return user_parameters
......@@ -37,7 +38,7 @@ class UserParametersFile:
:param sakia.data.entities.UserParameters user_parameters: the user_parameters to update
"""
try:
with open(self._file, 'r') as json_data:
with open(self._file, "r") as json_data:
user_parameters = UserParameters(**json.load(json_data))
user_parameters.profile_name = profile_name
except (OSError, json.decoder.JSONDecodeError):
......
......@@ -2,10 +2,11 @@ import logging
import time
import networkx
from sakia.data.processors import ConnectionsProcessor
from PyQt5.QtCore import QLocale, QDateTime, QObject, QT_TRANSLATE_NOOP
from PyQt5.QtCore import QLocale, QDateTime, QObject, QCoreApplication
from sakia.errors import NoPeerAvailable
from .constants import EdgeStatus, NodeStatus
from sakia.constants import MAX_CONFIRMATIONS
import asyncio
def sentry_display(identity):
......@@ -13,7 +14,7 @@ def sentry_display(identity):
sentry_symbol = ""
if identity.sentry:
sentry_symbol = ""
sentry_text = QT_TRANSLATE_NOOP("BaseGraph", "(sentry)") + " "
sentry_text = QCoreApplication.translate("BaseGraph", "(sentry)") + " "
return sentry_symbol, sentry_text
......@@ -50,7 +51,7 @@ class BaseGraph(QObject):
else:
return EdgeStatus.STRONG
async def node_status(self, node_identity):
def node_status(self, node_identity):
"""
Return the status of the node depending
:param sakia.core.registry.Identity node_identity: The identity of the node
......@@ -59,23 +60,6 @@ class BaseGraph(QObject):
"""
# new node
node_status = NodeStatus.NEUTRAL
node_identity = await self.identities_service.load_requirements(node_identity)
if node_identity.pubkey in self._connections_processor.pubkeys():
node_status += NodeStatus.HIGHLIGHTED
if node_identity.member is False:
node_status += NodeStatus.OUT
return node_status
def offline_node_status(self, node_identity):
"""
Return the status of the node depending on its requirements. No network request.
:param sakia.core.registry.Identity node_identity: The identity of the node
:param sakia.core.registry.Identity account_identity: The identity of the account displayed
:return: HIGHLIGHTED if node_identity is account_identity and OUT if the node_identity is not a member
:rtype: sakia.core.graph.constants.NodeStatus
"""
# new node
node_status = NodeStatus.NEUTRAL
if node_identity.pubkey in self._connections_processor.pubkeys():
node_status += NodeStatus.HIGHLIGHTED
if node_identity.member is False:
......@@ -90,7 +74,9 @@ class BaseGraph(QObject):
:rtype: str
"""
if block_number >= 0:
current_confirmations = min(max(self.blockchain_service.current_buid().number - block_number, 0), 6)
current_confirmations = min(
max(self.blockchain_service.current_buid().number - block_number, 0), 6
)
else:
current_confirmations = 0
......@@ -99,32 +85,35 @@ class BaseGraph(QObject):
return "{0}/{1}".format(current_confirmations, MAX_CONFIRMATIONS)
else:
confirmation = current_confirmations / MAX_CONFIRMATIONS * 100
return "{0} %".format(QLocale().toString(float(confirmation), 'f', 0))
return "{0} %".format(QLocale().toString(float(confirmation), "f", 0))
return None
def add_certifier_node(self, certifier, identity, certification, node_status):
sentry_symbol, sentry_text = sentry_display(certifier)
name_text = certifier.uid if certifier.uid else certifier.pubkey[:12]
metadata = {
'text': sentry_symbol + name_text,
'tooltip': sentry_text + certifier.pubkey,
'identity': certifier,
'status': node_status
"text": sentry_symbol + name_text,
"tooltip": sentry_text + certifier.pubkey,
"identity": certifier,
"status": node_status,
}
self.nx_graph.add_node(certifier.pubkey, attr_dict=metadata)
arc_status = self.arc_status(certification.timestamp)
sig_validity = self.blockchain_service.parameters().sig_validity
expiration = self.blockchain_service.adjusted_ts(certification.timestamp + sig_validity)
expiration = self.blockchain_service.adjusted_ts(
certification.timestamp + sig_validity
)
arc = {
'status': arc_status,
'tooltip': QLocale.toString(
"status": arc_status,
"tooltip": QLocale.toString(
QLocale(),
QDateTime.fromTime_t(expiration).date(),
QLocale.dateFormat(QLocale(), QLocale.ShortFormat)
) + " BAT",
'cert_time': certification.timestamp,
'confirmation_text': self.confirmation_text(certification.written_on)
QLocale.dateFormat(QLocale(), QLocale.ShortFormat),
)
+ " BAT",
"cert_time": certification.timestamp,
"confirmation_text": self.confirmation_text(certification.written_on),
}
self.nx_graph.add_edge(certifier.pubkey, identity.pubkey, attr_dict=arc)
......@@ -132,58 +121,33 @@ class BaseGraph(QObject):
sentry_symbol, sentry_text = sentry_display(certified)
name_text = certified.uid if certified.uid else certified.pubkey[:12]
metadata = {
'text': sentry_symbol + name_text,
'tooltip': sentry_text + certified.pubkey,
'identity': certified,
'status': node_status
"text": sentry_symbol + name_text,
"tooltip": sentry_text + certified.pubkey,
"identity": certified,
"status": node_status,
}
self.nx_graph.add_node(certified.pubkey, attr_dict=metadata)
arc_status = self.arc_status(certification.timestamp)
sig_validity = self.blockchain_service.parameters().sig_validity
expiration = self.blockchain_service.adjusted_ts(certification.timestamp + sig_validity)
expiration = self.blockchain_service.adjusted_ts(
certification.timestamp + sig_validity
)
arc = {
'status': arc_status,
'tooltip': QLocale.toString(
"status": arc_status,
"tooltip": QLocale.toString(
QLocale(),
QDateTime.fromTime_t(expiration).date(),
QLocale.dateFormat(QLocale(), QLocale.ShortFormat)
) + " BAT",
'cert_time': certification.timestamp,
'confirmation_text': self.confirmation_text(certification.written_on)
QLocale.dateFormat(QLocale(), QLocale.ShortFormat),
)
+ " BAT",
"cert_time": certification.timestamp,
"confirmation_text": self.confirmation_text(certification.written_on),
}
self.nx_graph.add_edge(identity.pubkey, certified.pubkey, attr_dict=arc)
def add_offline_certifier_list(self, certifier_list, identity):
"""
Add list of certifiers to graph
:param List[sakia.data.entities.Certification] certifier_list: List of certified from api
:param sakia.data.entities.Identity identity: identity instance which is certified
:return:
"""
# add certifiers of uid
for certification in tuple(certifier_list):
certifier = self.identities_service.get_identity(certification.certifier)
if certifier:
node_status = self.offline_node_status(certifier)
self.add_certifier_node(certifier, identity, certification, node_status)
def add_offline_certified_list(self, certified_list, identity):
"""
Add list of certified from api to graph
:param List[sakia.data.entities.Certification] certified_list: List of certified from api
:param identity identity: identity instance which is certifier
:return:
"""
# add certified by uid
for certification in tuple(certified_list):
certified = self.identities_service.get_identity(certification.certified)
if certified:
node_status = self.offline_node_status(certified)
self.add_certified_node(identity, certified, certification, node_status)
async def add_certifier_list(self, certifier_list, identity):
def add_certifier_list(self, certifier_list, identity):
"""
Add list of certifiers to graph
:param List[sakia.data.entities.Certification] certifier_list: List of certified from api
......@@ -192,17 +156,18 @@ class BaseGraph(QObject):
"""
try:
# add certifiers of uid
for certification in tuple(certifier_list):
certifier = self.identities_service.get_identity(certification.certifier)
for certification in certifier_list:
certifier = self.identities_service.get_identity(
certification.certifier
)
if not certifier:
certifier = await self.identities_service.find_from_pubkey(certification.certifier)
self.identities_service.insert_or_update_identity(certifier)
node_status = await self.node_status(certifier)
certifier = certifier_list[certification]
node_status = self.node_status(certifier)
self.add_certifier_node(certifier, identity, certification, node_status)
except NoPeerAvailable as e:
logging.debug(str(e))
async def add_certified_list(self, certified_list, identity):
def add_certified_list(self, certified_list, identity):
"""
Add list of certified from api to graph
:param List[sakia.data.entities.Certification] certified_list: List of certified from api
......@@ -212,11 +177,12 @@ class BaseGraph(QObject):
try:
# add certified by uid
for certification in tuple(certified_list):
certified = self.identities_service.get_identity(certification.certified)
certified = self.identities_service.get_identity(
certification.certified
)
if not certified:
certified = await self.identities_service.find_from_pubkey(certification.certified)
self.identities_service.insert_or_update_identity(certified)
node_status = await self.node_status(certified)
certified = certified_list[certification]
node_status = self.node_status(certified)
self.add_certified_node(identity, certified, certification, node_status)
except NoPeerAvailable as e:
......@@ -233,9 +199,9 @@ class BaseGraph(QObject):
sentry_symbol, sentry_text = sentry_display(identity)
name_text = identity.uid
metadata = {
'text': sentry_symbol + name_text,
'tooltip': sentry_text + identity.pubkey,
'status': status,
'identity': identity
"text": sentry_symbol + name_text,
"tooltip": sentry_text + identity.pubkey,
"status": status,
"identity": identity,
}
self.nx_graph.add_node(identity.pubkey, attr_dict=metadata)
......@@ -18,36 +18,48 @@ class WoTGraph(BaseGraph):
async def initialize(self, center_identity):
self.nx_graph.clear()
node_status = await self.node_status(center_identity)
node_status = self.node_status(center_identity)
self.add_identity(center_identity, node_status)
# create Identity from node metadata
certifier_coro = asyncio.ensure_future(self.identities_service.load_certifiers_of(center_identity))
certified_coro = asyncio.ensure_future(self.identities_service.load_certified_by(center_identity))
certifier_coro = self.identities_service.load_certifiers_of(center_identity)
certified_coro = self.identities_service.load_certified_by(center_identity)
certifier_list, certified_list = await asyncio.gather(*[certifier_coro, certified_coro])
certifier_list, certified_list = await asyncio.gather(
*[certifier_coro, certified_coro]
)
certified_list, certified_list = await self.identities_service.load_certs_in_lookup(center_identity,
(
certifier_list,
certified_list)
certified_list,
) = await self.identities_service.load_certs_in_lookup(
center_identity, certifier_list, certified_list
)
# populate graph with certifiers-of
certifier_coro = asyncio.ensure_future(self.add_certifier_list(certifier_list, center_identity))
self.add_certifier_list(certifier_list, center_identity)
# populate graph with certified-by
certified_coro = asyncio.ensure_future(self.add_certified_list(certified_list, center_identity))
await asyncio.gather(*[certifier_coro, certified_coro], return_exceptions=True)
await asyncio.sleep(0)
self.add_certified_list(certified_list, center_identity)
def offline_init(self, center_identity):
node_status = self.offline_node_status(center_identity)
node_status = self.node_status(center_identity)
self.add_identity(center_identity, node_status)
# populate graph with certifiers-of
certifier_list = self.identities_service.certifications_received(center_identity.pubkey)
self.add_offline_certifier_list(certifier_list, center_identity)
certifier_list = self.identities_service.certifications_received(
center_identity.pubkey
)
certifier_list = {
c: self.identities_service.get_identity(c.certifier) for c in certifier_list
}
self.add_certifier_list(certifier_list, center_identity)
# populate graph with certified-by
certified_list = self.identities_service.certifications_sent(center_identity.pubkey)
self.add_offline_certified_list(certified_list, center_identity)
certified_list = self.identities_service.certifications_sent(
center_identity.pubkey
)
certified_list = {
c: self.identities_service.get_identity(c.certified) for c in certified_list
}
self.add_certified_list(certified_list, center_identity)
......@@ -7,5 +7,3 @@ from .sources import SourcesProcessor
from .transactions import TransactionsProcessor
from .dividends import DividendsProcessor
from .contacts import ContactsProcessor
......@@ -2,11 +2,11 @@ import attr
import sqlite3
import logging
from sakia.errors import NoPeerAvailable
from ..entities import Blockchain, BlockchainParameters
from ..entities import Blockchain
from .nodes import NodesProcessor
from ..connectors import BmaConnector
from duniterpy.api import bma, errors
from duniterpy.documents import Block, BMAEndpoint
from duniterpy.documents import Block
import asyncio
......@@ -14,7 +14,7 @@ import asyncio
class BlockchainProcessor:
_repo = attr.ib() # :type sakia.data.repositories.BlockchainsRepo
_bma_connector = attr.ib() # :type sakia.data.connectors.bma.BmaConnector
_logger = attr.ib(default=attr.Factory(lambda: logging.getLogger('sakia')))
_logger = attr.ib(default=attr.Factory(lambda: logging.getLogger("sakia")))
@classmethod
def instanciate(cls, app):
......@@ -23,8 +23,10 @@ class BlockchainProcessor:
:param sakia.app.Application app: the app
:rtype: sakia.data.processors.BlockchainProcessor
"""
return cls(app.db.blockchains_repo,
BmaConnector(NodesProcessor(app.db.nodes_repo), app.parameters))
return cls(
app.db.blockchains_repo,
BmaConnector(NodesProcessor(app.db.nodes_repo), app.parameters),
)
def initialized(self, currency):
return self._repo.get_one(currency=currency) is not None
......@@ -40,10 +42,12 @@ class BlockchainProcessor:
try:
if not udblocks:
udblocks = await self._bma_connector.get(currency, bma.blockchain.ud)
udblocks = udblocks['result']['blocks']
udblocks = udblocks["result"]["blocks"]
ud_block_number = next(b for b in udblocks if b <= block_number)
block = await self._bma_connector.get(currency, bma.blockchain.block, {'number': ud_block_number})
return block['dividend'], block['unitbase']
block = await self._bma_connector.get(
currency, bma.blockchain.block, {"number": ud_block_number}
)
return block["dividend"], block["unitbase"]
except StopIteration:
self._logger.debug("No dividend generated before {0}".format(block_number))
except NoPeerAvailable as e:
......@@ -61,9 +65,11 @@ class BlockchainProcessor:
async def timestamp(self, currency, block_number):
try:
block = await self._bma_connector.get(currency, bma.blockchain.block, {'number': block_number})
block = await self._bma_connector.get(
currency, bma.blockchain.block, {"number": block_number}
)
if block:
return block['medianTime']
return block["medianTime"]
except NoPeerAvailable as e:
self._logger.debug(str(e))
except errors.DuniterError as e:
......@@ -73,6 +79,26 @@ class BlockchainProcessor:
raise
return 0
def rounded_timestamp(self, currency, block_number):
parameters = self.parameters(currency)
current_time = self.time(currency)
current_block = self.current_buid(currency)
diff_blocks = block_number - current_block.number
diff_time = diff_blocks * parameters.avg_gen_time
return current_time + diff_time
def block_number_30days_ago(self, currency, blockstamp):
"""
When refreshing data, we only request the last 30 days
This method computes the block number 30 days ago
:param currency:
:return:
"""
avg_blocks_per_month = int(
30 * 24 * 3600 / self.parameters(currency).avg_gen_time
)
return blockstamp.number - avg_blocks_per_month
def current_buid(self, currency):
"""
Get the local current blockuid
......@@ -179,9 +205,13 @@ class BlockchainProcessor:
:param int number:
:rtype: duniterpy.documents.Block
"""
block = await self._bma_connector.get(currency, bma.blockchain.block, req_args={'number': number})
block = await self._bma_connector.get(
currency, bma.blockchain.block, req_args={"number": number}
)
if block:
block_doc = Block.from_signed_raw("{0}{1}\n".format(block['raw'], block['signature']))
block_doc = Block.from_signed_raw(
"{0}{1}\n".format(block["raw"], block["signature"])
)
return block_doc
async def new_blocks_with_identities(self, currency):
......@@ -191,11 +221,13 @@ class BlockchainProcessor:
"""
with_identities = []
future_requests = []
for req in (bma.blockchain.joiners,
for req in (
bma.blockchain.joiners,
bma.blockchain.leavers,
bma.blockchain.actives,
bma.blockchain.excluded,
bma.blockchain.newcomers):
bma.blockchain.newcomers,
):
future_requests.append(self._bma_connector.get(currency, req))
results = await asyncio.gather(*future_requests)
......@@ -222,22 +254,6 @@ class BlockchainProcessor:
local_current_buid = self.current_buid(currency)
return sorted([b for b in with_money if b > local_current_buid.number])
async def next_blocks(self, start, filter, currency):
"""
Get blocks from the network
:param List[int] numbers: list of blocks numbers to get
:return: the list of block documents
:rtype: List[duniterpy.documents.Block]
"""
blocks = []
blocks_data = await self._bma_connector.get(currency, bma.blockchain.blocks, req_args={'count': 100,
'start': start})
for data in blocks_data:
if data['number'] in filter or data['number'] == start+99:
blocks.append(Block.from_signed_raw(data["raw"] + data["signature"] + "\n"))
return blocks
async def initialize_blockchain(self, currency):
"""
Initialize blockchain for a given currency if no source exists locally
......@@ -247,128 +263,192 @@ class BlockchainProcessor:
blockchain = Blockchain(currency=currency)
self._logger.debug("Requesting blockchain parameters")
try:
parameters = await self._bma_connector.get(currency, bma.blockchain.parameters)
blockchain.parameters.ms_validity = parameters['msValidity']
blockchain.parameters.avg_gen_time = parameters['avgGenTime']
blockchain.parameters.c = parameters['c']
blockchain.parameters.dt = parameters['dt']
blockchain.parameters.dt_diff_eval = parameters['dtDiffEval']
blockchain.parameters.median_time_blocks = parameters['medianTimeBlocks']
blockchain.parameters.percent_rot = parameters['percentRot']
blockchain.parameters.idty_window = parameters['idtyWindow']
blockchain.parameters.ms_window = parameters['msWindow']
blockchain.parameters.sig_window = parameters['sigWindow']
blockchain.parameters.sig_period = parameters['sigPeriod']
blockchain.parameters.sig_qty = parameters['sigQty']
blockchain.parameters.sig_stock = parameters['sigStock']
blockchain.parameters.sig_validity = parameters['sigValidity']
blockchain.parameters.sig_qty = parameters['sigQty']
blockchain.parameters.sig_period = parameters['sigPeriod']
blockchain.parameters.ud0 = parameters['ud0']
blockchain.parameters.ud_time_0 = parameters['udTime0']
blockchain.parameters.dt_reeval = parameters['dtReeval']
blockchain.parameters.ud_reeval_time_0 = parameters['udReevalTime0']
blockchain.parameters.xpercent = parameters['xpercent']
parameters = await self._bma_connector.get(
currency, bma.blockchain.parameters
)
blockchain.parameters.c = parameters["c"]
blockchain.parameters.dt = parameters["dt"]
blockchain.parameters.ud0 = parameters["ud0"]
blockchain.parameters.sig_period = parameters["sigPeriod"]
blockchain.parameters.sig_stock = parameters["sigStock"]
blockchain.parameters.sig_window = parameters["sigWindow"]
blockchain.parameters.sig_validity = parameters["sigValidity"]
blockchain.parameters.sig_qty = parameters["sigQty"]
# todo: support parameter sigReplay
# blockchain.parameters.sig_replay = parameters["sigReplay"]
blockchain.parameters.idty_window = parameters["idtyWindow"]
blockchain.parameters.ms_window = parameters["msWindow"]
# todo: support parameter msPeriod
# blockchain.parameters.ms_period = parameters["msPeriod"]
blockchain.parameters.xpercent = parameters["xpercent"]
blockchain.parameters.ms_validity = parameters["msValidity"]
blockchain.parameters.step_max = parameters["stepMax"]
blockchain.parameters.median_time_blocks = parameters[
"medianTimeBlocks"
]
blockchain.parameters.avg_gen_time = parameters["avgGenTime"]
blockchain.parameters.dt_diff_eval = parameters["dtDiffEval"]
blockchain.parameters.percent_rot = parameters["percentRot"]
blockchain.parameters.ud_time_0 = parameters["udTime0"]
blockchain.parameters.ud_reeval_time_0 = parameters["udReevalTime0"]
blockchain.parameters.dt_reeval = parameters["dtReeval"]
except errors.DuniterError as e:
raise
self._logger.debug("Requesting current block")
try:
current_block = await self._bma_connector.get(currency, bma.blockchain.current)
signed_raw = "{0}{1}\n".format(current_block['raw'], current_block['signature'])
current_block = await self._bma_connector.get(
currency, bma.blockchain.current
)
signed_raw = "{0}{1}\n".format(
current_block["raw"], current_block["signature"]
)
block = Block.from_signed_raw(signed_raw)
blockchain.current_buid = block.blockUID
blockchain.median_time = block.mediantime
blockchain.current_members_count = block.members_count
blockchain.current_mass = current_block['monetaryMass']
blockchain.current_mass = current_block["monetaryMass"]
except errors.DuniterError as e:
if e.ucode != errors.NO_CURRENT_BLOCK:
raise
await self.refresh_dividend_data(currency, blockchain)
try:
self._repo.insert(blockchain)
except sqlite3.IntegrityError:
self._repo.update(blockchain)
async def refresh_dividend_data(self, currency, blockchain):
self._logger.debug("Requesting blocks with dividend")
with_ud = await self._bma_connector.get(currency, bma.blockchain.ud)
blocks_with_ud = with_ud['result']['blocks']
blocks_with_ud = with_ud["result"]["blocks"]
if len(blocks_with_ud) > 0:
self._logger.debug("Requesting last block with dividend")
try:
nb_previous_reevaluations = int((blockchain.median_time - blockchain.parameters.ud_reeval_time_0)
/ blockchain.parameters.dt_reeval)
nb_previous_reevaluations = int(
(blockchain.median_time - blockchain.parameters.ud_reeval_time_0)
/ blockchain.parameters.dt_reeval
)
self._logger.debug(
"nb_previous_reevaluations = {}".format(nb_previous_reevaluations)
)
last_reeval_offset = (blockchain.median_time -
(blockchain.parameters.ud_reeval_time_0 +
nb_previous_reevaluations * blockchain.parameters.dt_reeval)
last_reeval_offset = blockchain.median_time - (
blockchain.parameters.ud_reeval_time_0
+ nb_previous_reevaluations * blockchain.parameters.dt_reeval
)
self._logger.debug("last_reeval_offset = {}".format(last_reeval_offset))
# todo: improve this method or use a future API method returning reevaluation block numbers...
previous_dt_reeval_block_index = int(
(
(nb_previous_reevaluations - 1)
* (blockchain.parameters.dt_reeval / blockchain.parameters.dt)
)
+ (blockchain.parameters.dt_reeval / 2 / blockchain.parameters.dt)
)
self._logger.debug(
" previous previous_dt_reeval_block_index = {}".format(
previous_dt_reeval_block_index
)
)
dt_reeval_block_target = max(blockchain.current_buid.number - int(last_reeval_offset
/ blockchain.parameters.avg_gen_time),
0)
try:
last_ud_reeval_block_number = [b for b in blocks_with_ud if b <= dt_reeval_block_target][-1]
last_ud_reeval_block_number = blocks_with_ud[-1]
except IndexError:
last_ud_reeval_block_number = 0
self._logger.debug(
"last_ud_reeval_block_number = {}".format(
last_ud_reeval_block_number
)
)
if last_ud_reeval_block_number:
block_with_ud = await self._bma_connector.get(currency, bma.blockchain.block,
req_args={'number': last_ud_reeval_block_number})
self._logger.debug(
"Requesting last block with dividend reevaluation..."
)
block_with_ud = await self._bma_connector.get(
currency,
bma.blockchain.block,
req_args={"number": last_ud_reeval_block_number},
)
if block_with_ud:
blockchain.last_members_count = block_with_ud['membersCount']
blockchain.last_ud = block_with_ud['dividend']
blockchain.last_ud_base = block_with_ud['unitbase']
blockchain.last_ud_time = block_with_ud['medianTime']
blockchain.last_mass = block_with_ud['monetaryMass']
self._logger.debug("Refresh last UD reevaluation info in DB")
blockchain.last_members_count = block_with_ud["membersCount"]
blockchain.last_ud = block_with_ud["dividend"]
blockchain.last_ud_base = block_with_ud["unitbase"]
blockchain.last_ud_time = block_with_ud["medianTime"]
blockchain.last_mass = block_with_ud["monetaryMass"]
self._logger.debug("Requesting previous block with dividend")
dt_reeval_block_target = max(dt_reeval_block_target - int(blockchain.parameters.dt_reeval
/ blockchain.parameters.avg_gen_time),
0)
try:
previous_ud_reeval_block_number = [b for b in blocks_with_ud if b <= dt_reeval_block_target][-1]
previous_ud_reeval_block_number = blocks_with_ud[
previous_dt_reeval_block_index
]
except IndexError:
previous_ud_reeval_block_number = min(blocks_with_ud)
block_with_ud = await self._bma_connector.get(currency, bma.blockchain.block,
req_args={'number': previous_ud_reeval_block_number})
blockchain.previous_mass = block_with_ud['monetaryMass']
blockchain.previous_members_count = block_with_ud['membersCount']
blockchain.previous_ud = block_with_ud['dividend']
blockchain.previous_ud_base = block_with_ud['unitbase']
blockchain.previous_ud_time = block_with_ud['medianTime']
self._logger.debug(
"previous_ud_reeval_block_number = {}".format(
previous_ud_reeval_block_number
)
)
self._logger.debug("Refresh previous UD reevaluation info in DB")
block_with_ud = await self._bma_connector.get(
currency,
bma.blockchain.block,
req_args={"number": previous_ud_reeval_block_number},
)
blockchain.previous_mass = block_with_ud["monetaryMass"]
blockchain.previous_members_count = block_with_ud["membersCount"]
blockchain.previous_ud = block_with_ud["dividend"]
blockchain.previous_ud_base = block_with_ud["unitbase"]
blockchain.previous_ud_time = block_with_ud["medianTime"]
except errors.DuniterError as e:
if e.ucode != errors.NO_CURRENT_BLOCK:
raise
try:
self._repo.insert(blockchain)
except sqlite3.IntegrityError:
self._repo.update(blockchain)
def handle_new_blocks(self, currency, blocks):
async def handle_new_blocks(self, currency, network_blockstamp):
"""
Initialize blockchain for a given currency if no source exists locally
:param List[duniterpy.documents.Block] blocks
:param str currency:
:param BlockUID network_blockstamp: the blockstamp of the network
"""
self._logger.debug("Requesting current block")
try:
current_block = await self._bma_connector.get(
currency,
bma.blockchain.block,
req_args={"number": network_blockstamp.number},
)
signed_raw = "{0}{1}\n".format(
current_block["raw"], current_block["signature"]
)
block = Block.from_signed_raw(signed_raw)
blockchain = self._repo.get_one(currency=currency)
for block in sorted(blocks):
if blockchain.current_buid < block.blockUID:
blockchain.current_buid = block.blockUID
blockchain.median_time = block.mediantime
blockchain.current_members_count = block.members_count
if block.ud:
blockchain.current_mass += (block.ud * 10**block.unit_base) * block.members_count
if block.ud and blockchain.last_ud_time + blockchain.parameters.dt_reeval >= block.mediantime:
blockchain.previous_mass = blockchain.last_mass
blockchain.previous_members_count = blockchain.last_members_count
blockchain.previous_ud = blockchain.last_ud
blockchain.previous_ud_base = blockchain.last_ud_base
blockchain.previous_ud_time = blockchain.last_ud_time
blockchain.last_members_count = block.members_count
blockchain.last_ud = block.ud
blockchain.last_ud_base = block.unit_base
blockchain.last_ud_time = block.mediantime
if (
blockchain.last_ud_time + blockchain.parameters.dt
<= blockchain.median_time
):
await self.refresh_dividend_data(currency, blockchain)
self._repo.update(blockchain)
except errors.DuniterError as e:
if e.ucode != errors.NO_CURRENT_BLOCK:
raise
def remove_blockchain(self, currency):
self._repo.drop(self._repo.get_one(currency=currency))