From b77ce90476c71f17e4f18e4b947d15a0ed7b3092 Mon Sep 17 00:00:00 2001
From: Vincent Texier <vit@free.fr>
Date: Wed, 1 May 2024 17:08:38 +0200
Subject: [PATCH] [fix] fix expiresOn date on smith page (in epoch/session
 index) BC

add epoch duration in currency entity and DB
add current epoch index in node entity and DB
add docker gdev command in README.md
---
 README.md                                     |  12 +
 tikka/adapters/network/currency.py            |  22 ++
 tikka/adapters/network/nodes.py               |   9 +
 .../assets/migrations/00001.init_tables.sql   |   6 +-
 tikka/adapters/repository/nodes.py            |   1 +
 tikka/domains/application.py                  |   4 +-
 ...ck_time.py => blockchain_units_to_time.py} |  32 +-
 tikka/domains/currencies.py                   |   5 +
 tikka/domains/entities/currency.py            |   1 +
 tikka/domains/entities/node.py                |   1 +
 tikka/domains/nodes.py                        |   1 +
 tikka/interfaces/adapters/network/currency.py |   9 +
 tikka/interfaces/adapters/repository/nodes.py |   1 +
 .../pyqt/resources/gui/widgets/connection.ui  | 161 +++++----
 .../pyqt/resources/gui/widgets/currency.ui    | 321 ++++++++++--------
 tikka/slots/pyqt/widgets/connection.py        |   2 +
 tikka/slots/pyqt/widgets/currency.py          |  16 +-
 tikka/slots/pyqt/widgets/smith.py             |  16 +-
 18 files changed, 397 insertions(+), 223 deletions(-)
 rename tikka/domains/{block_time.py => blockchain_units_to_time.py} (67%)

diff --git a/README.md b/README.md
index 85f45a17..13bdb635 100644
--- a/README.md
+++ b/README.md
@@ -178,3 +178,15 @@ Install `gitlab-runner` to test .gitlab-ci.yml.
 To test on a local docker image, specify pull policy:
 
     gitlab-runner exec docker --docker-pull-policy never tests
+
+### Test on a local gdev node
+
+Run a gdev local node:
+
+    docker run -p 9944:9944 duniter/duniter-v2s-gdev
+
+Set connexion to url `ws://localhost:9944`.
+
+Use Alice, Bob, Charlie, Dave, Eve and Ferdie accounts derivation with the Dev mnemonic:
+
+    bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice
diff --git a/tikka/adapters/network/currency.py b/tikka/adapters/network/currency.py
index f127c0a1..aee3f57b 100644
--- a/tikka/adapters/network/currency.py
+++ b/tikka/adapters/network/currency.py
@@ -134,3 +134,25 @@ class NetworkCurrency(NetworkCurrencyInterface):
             return None
 
         return result.value
+
+    def get_epoch_duration(self) -> Optional[int]:
+        __doc__ = (  # pylint: disable=redefined-builtin, unused-variable
+            NetworkCurrencyInterface.get_expected_block_time.__doc__
+        )
+        if not self.connections.is_connected():
+            return None
+        if self.connections.rpc.client is None:
+            return None
+
+        try:
+            result = self.connections.rpc.client.get_constant("Babe", "EpochDuration")
+        except Exception as exception:
+            logging.exception(exception)
+            return None
+
+        epoch_duration_in_blocks = result.value
+        block_duration_in_ms = self.get_expected_block_time()
+        if block_duration_in_ms is None:
+            return None
+
+        return epoch_duration_in_blocks * block_duration_in_ms
diff --git a/tikka/adapters/network/nodes.py b/tikka/adapters/network/nodes.py
index dd72b818..62bd5274 100644
--- a/tikka/adapters/network/nodes.py
+++ b/tikka/adapters/network/nodes.py
@@ -49,10 +49,19 @@ class NetworkNodes(NetworkNodesInterface):
             logging.exception(exception)
             return None
 
+        try:
+            current_epoch_result = self.connections.rpc.client.query(
+                "Babe", "EpochIndex"
+            )
+        except Exception as exception:
+            logging.exception(exception)
+            return None
+
         return Node(
             self.connections.rpc.client.url,
             peer_id=peer_id,
             block=current_block,
             software=self.connections.rpc.client.name,
             software_version=self.connections.rpc.client.version,
+            epoch_index=current_epoch_result.value,
         )
diff --git a/tikka/adapters/repository/assets/migrations/00001.init_tables.sql b/tikka/adapters/repository/assets/migrations/00001.init_tables.sql
index 8551e0b2..997c4b0a 100644
--- a/tikka/adapters/repository/assets/migrations/00001.init_tables.sql
+++ b/tikka/adapters/repository/assets/migrations/00001.init_tables.sql
@@ -7,7 +7,8 @@ create table if not exists currency(
     universal_dividend integer default null,
     monetary_mass integer default null,
     members_count integer default null,
-    block_duration integer default 6000
+    block_duration integer default 6000,
+    epoch_duration integer default 3600000
 );
 
 create table nodes (
@@ -16,7 +17,8 @@ create table nodes (
         block integer default null,
         software varchar(255) default null,
         software_version varchar(255) default null,
-        session_keys varchar(65535) default null
+        session_keys varchar(65535) default null,
+        epoch_index integer default null
 );
 
 create table if not exists accounts(
diff --git a/tikka/adapters/repository/nodes.py b/tikka/adapters/repository/nodes.py
index 2cc0b8b4..59e6c50e 100644
--- a/tikka/adapters/repository/nodes.py
+++ b/tikka/adapters/repository/nodes.py
@@ -28,6 +28,7 @@ SQL_COLUMNS = {
     NodesRepositoryInterface.COLUMN_SOFTWARE: "software",
     NodesRepositoryInterface.COLUMN_SOFTWARE_VERSION: "software_version",
     NodesRepositoryInterface.COLUMN_SESSION_KEYS: "session_keys",
+    NodesRepositoryInterface.COLUMN_EPOCH_INDEX: "epoch_index",
 }
 
 DEFAULT_LIST_OFFSET = 0
diff --git a/tikka/domains/application.py b/tikka/domains/application.py
index 79c0b0db..4e1afefc 100644
--- a/tikka/domains/application.py
+++ b/tikka/domains/application.py
@@ -44,7 +44,7 @@ from tikka.adapters.repository.wallets import Sqlite3WalletsRepository
 from tikka.domains.accounts import Accounts
 from tikka.domains.amounts import Amounts
 from tikka.domains.authorities import Authorities
-from tikka.domains.block_time import BlockTime
+from tikka.domains.blockchain_units_to_time import BlockchainUnitsToTime
 from tikka.domains.categories import Categories
 from tikka.domains.config import Config
 from tikka.domains.connections import Connections
@@ -155,7 +155,7 @@ class Application:
             self.currencies,
             self.event_dispatcher,
         )
-        self.block_time = BlockTime(self.currencies, self.nodes)
+        self.block_time = BlockchainUnitsToTime(self.currencies, self.nodes)
         self.smiths = Smiths(smiths_repository, NetworkSmiths(self.connections))
         self.authorities = Authorities(self.nodes, NetworkAuthorities(self.connections))
         self.categories = Categories(
diff --git a/tikka/domains/block_time.py b/tikka/domains/blockchain_units_to_time.py
similarity index 67%
rename from tikka/domains/block_time.py
rename to tikka/domains/blockchain_units_to_time.py
index 34a6cdca..fab6d52f 100644
--- a/tikka/domains/block_time.py
+++ b/tikka/domains/blockchain_units_to_time.py
@@ -20,7 +20,7 @@ from tikka.domains.currencies import Currencies
 from tikka.domains.nodes import Nodes
 
 
-class BlockTime:
+class BlockchainUnitsToTime:
     def __init__(self, currencies: Currencies, nodes: Nodes):
         """
         Initialize BlockTime domain instance
@@ -60,3 +60,33 @@ class BlockTime:
             )
 
         return block_time
+
+    def get_datetime_from_epoch(self, epoch_index: int) -> Optional[datetime]:
+        """
+        Return a datetime object from an epoch index
+
+        :param epoch_index: Epoch number
+        :return:
+        """
+        # network access to update current block number, can be slow...
+        self.nodes.network_fetch_current_node()
+
+        current_time = datetime.now()
+        node = self.nodes.get(self.nodes.get_current_url())
+        if node is None:
+            return None
+        currency = self.currencies.get_current()
+        current_epoch_index = node.epoch_index
+        if current_epoch_index is None:
+            return None
+        epoch_diff = epoch_index - current_epoch_index
+        if epoch_diff < 0:
+            block_time = current_time - timedelta(
+                milliseconds=abs(epoch_diff) * currency.epoch_duration
+            )
+        else:
+            block_time = current_time + timedelta(
+                milliseconds=abs(epoch_diff) * currency.epoch_duration
+            )
+
+        return block_time
diff --git a/tikka/domains/currencies.py b/tikka/domains/currencies.py
index 45e9aaca..482ed72d 100644
--- a/tikka/domains/currencies.py
+++ b/tikka/domains/currencies.py
@@ -147,5 +147,10 @@ class Currencies:
             currency.block_duration = expected_block_time
             updated = True
 
+        epoch_duration = self.network.get_epoch_duration()
+        if epoch_duration is not None:
+            currency.epoch_duration = epoch_duration
+            updated = True
+
         if updated is True:
             self.currency_repository.update(currency)
diff --git a/tikka/domains/entities/currency.py b/tikka/domains/entities/currency.py
index 1af6808e..bc9bc5c4 100644
--- a/tikka/domains/entities/currency.py
+++ b/tikka/domains/entities/currency.py
@@ -28,3 +28,4 @@ class Currency:
     monetary_mass: Optional[int] = None
     members_count: Optional[int] = None
     block_duration: int = 6000
+    epoch_duration: int = 3600000
diff --git a/tikka/domains/entities/node.py b/tikka/domains/entities/node.py
index f8d889c6..b1a57d46 100644
--- a/tikka/domains/entities/node.py
+++ b/tikka/domains/entities/node.py
@@ -27,6 +27,7 @@ class Node:
     software: Optional[str] = None
     software_version: Optional[str] = None
     session_keys: Optional[str] = None
+    epoch_index: Optional[int] = None
 
     def __str__(self):
         """
diff --git a/tikka/domains/nodes.py b/tikka/domains/nodes.py
index 84d7b08b..df63342b 100644
--- a/tikka/domains/nodes.py
+++ b/tikka/domains/nodes.py
@@ -118,6 +118,7 @@ class Nodes:
             current_node.peer_id = network_node.peer_id
             current_node.software = network_node.software
             current_node.software_version = network_node.software_version
+            current_node.epoch_index = network_node.epoch_index
 
             self.repository.update(current_node)
 
diff --git a/tikka/interfaces/adapters/network/currency.py b/tikka/interfaces/adapters/network/currency.py
index 89db1b35..bec7ef3e 100644
--- a/tikka/interfaces/adapters/network/currency.py
+++ b/tikka/interfaces/adapters/network/currency.py
@@ -87,3 +87,12 @@ class NetworkCurrencyInterface(abc.ABC):
         :return:
         """
         raise NotImplementedError
+
+    @abc.abstractmethod
+    def get_epoch_duration(self) -> Optional[int]:
+        """
+        Return the expected session time in ms
+
+        :return:
+        """
+        raise NotImplementedError
diff --git a/tikka/interfaces/adapters/repository/nodes.py b/tikka/interfaces/adapters/repository/nodes.py
index 5454577e..e8f34207 100644
--- a/tikka/interfaces/adapters/repository/nodes.py
+++ b/tikka/interfaces/adapters/repository/nodes.py
@@ -30,6 +30,7 @@ class NodesRepositoryInterface(abc.ABC):
     COLUMN_SOFTWARE = "software"
     COLUMN_SOFTWARE_VERSION = "software_version"
     COLUMN_SESSION_KEYS = "session_keys"
+    COLUMN_EPOCH_INDEX = "epoch_index"
 
     DEFAULT_LIST_OFFSET = 0
     DEFAULT_LIST_LIMIT = 1000
diff --git a/tikka/slots/pyqt/resources/gui/widgets/connection.ui b/tikka/slots/pyqt/resources/gui/widgets/connection.ui
index 0ffe1752..6dff90f6 100644
--- a/tikka/slots/pyqt/resources/gui/widgets/connection.ui
+++ b/tikka/slots/pyqt/resources/gui/widgets/connection.ui
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>724</width>
-    <height>139</height>
+    <height>168</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -16,51 +16,26 @@
   <layout class="QVBoxLayout" name="verticalLayout_2">
    <item>
     <layout class="QGridLayout" name="gridLayout">
-     <item row="0" column="1">
-      <widget class="QComboBox" name="urlsComboBox">
-       <property name="sizePolicy">
-        <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
-         <horstretch>0</horstretch>
-         <verstretch>0</verstretch>
-        </sizepolicy>
-       </property>
-       <property name="toolTip">
-        <string/>
-       </property>
-       <property name="editable">
-        <bool>false</bool>
-       </property>
-       <property name="minimumContentsLength">
-        <number>0</number>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="2">
-      <widget class="QPushButton" name="connectButton">
-       <property name="toolTip">
-        <string>Connection to entry point</string>
-       </property>
+     <item row="1" column="1">
+      <widget class="QLabel" name="softwareValueLabel">
        <property name="text">
-        <string>Connect</string>
+        <string/>
        </property>
       </widget>
      </item>
-     <item row="0" column="3">
-      <widget class="QLabel" name="connectionStatusLabel">
-       <property name="maximumSize">
-        <size>
-         <width>16</width>
-         <height>16</height>
-        </size>
+     <item row="0" column="0">
+      <widget class="QLabel" name="serverLabel">
+       <property name="font">
+        <font>
+         <weight>75</weight>
+         <bold>true</bold>
+        </font>
        </property>
        <property name="text">
-        <string/>
-       </property>
-       <property name="pixmap">
-        <pixmap resource="../../icons/tikka.slots.pyqt.resources.icons.index.qrc">:/icons/disconnected</pixmap>
+        <string>Server</string>
        </property>
-       <property name="scaledContents">
-        <bool>true</bool>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
        </property>
       </widget>
      </item>
@@ -78,13 +53,6 @@
        </property>
       </widget>
      </item>
-     <item row="2" column="1">
-      <widget class="QLabel" name="versionValueLabel">
-       <property name="text">
-        <string/>
-       </property>
-      </widget>
-     </item>
      <item row="3" column="0">
       <widget class="QLabel" name="peerIDLabel">
        <property name="font">
@@ -101,8 +69,34 @@
        </property>
       </widget>
      </item>
-     <item row="0" column="0">
-      <widget class="QLabel" name="serverLabel">
+     <item row="0" column="1">
+      <widget class="QComboBox" name="urlsComboBox">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string/>
+       </property>
+       <property name="editable">
+        <bool>false</bool>
+       </property>
+       <property name="minimumContentsLength">
+        <number>0</number>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="1">
+      <widget class="QLabel" name="blockValueLabel">
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="versionLabel">
        <property name="font">
         <font>
          <weight>75</weight>
@@ -110,29 +104,25 @@
         </font>
        </property>
        <property name="text">
-        <string>Server</string>
+        <string>Version</string>
        </property>
        <property name="alignment">
         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
        </property>
       </widget>
      </item>
-     <item row="3" column="1">
-      <widget class="QLabel" name="peerIDValueLabel">
-       <property name="text">
-        <string/>
+     <item row="0" column="2">
+      <widget class="QPushButton" name="connectButton">
+       <property name="toolTip">
+        <string>Connection to entry point</string>
        </property>
-      </widget>
-     </item>
-     <item row="1" column="1">
-      <widget class="QLabel" name="softwareValueLabel">
        <property name="text">
-        <string/>
+        <string>Connect</string>
        </property>
       </widget>
      </item>
-     <item row="1" column="0">
-      <widget class="QLabel" name="softwareLabel">
+     <item row="4" column="0">
+      <widget class="QLabel" name="blockLabel">
        <property name="font">
         <font>
          <weight>75</weight>
@@ -140,15 +130,15 @@
         </font>
        </property>
        <property name="text">
-        <string>Software</string>
+        <string>Block</string>
        </property>
        <property name="alignment">
         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
        </property>
       </widget>
      </item>
-     <item row="2" column="0">
-      <widget class="QLabel" name="versionLabel">
+     <item row="1" column="0">
+      <widget class="QLabel" name="softwareLabel">
        <property name="font">
         <font>
          <weight>75</weight>
@@ -156,15 +146,48 @@
         </font>
        </property>
        <property name="text">
-        <string>Version</string>
+        <string>Software</string>
        </property>
        <property name="alignment">
         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
        </property>
       </widget>
      </item>
-     <item row="4" column="0">
-      <widget class="QLabel" name="blockLabel">
+     <item row="3" column="1">
+      <widget class="QLabel" name="peerIDValueLabel">
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLabel" name="versionValueLabel">
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="3">
+      <widget class="QLabel" name="connectionStatusLabel">
+       <property name="maximumSize">
+        <size>
+         <width>16</width>
+         <height>16</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="pixmap">
+        <pixmap resource="../../icons/tikka.slots.pyqt.resources.icons.index.qrc">:/icons/disconnected</pixmap>
+       </property>
+       <property name="scaledContents">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="0">
+      <widget class="QLabel" name="epochLabel">
        <property name="font">
         <font>
          <weight>75</weight>
@@ -172,15 +195,15 @@
         </font>
        </property>
        <property name="text">
-        <string>Block</string>
+        <string>Epoch</string>
        </property>
        <property name="alignment">
         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
        </property>
       </widget>
      </item>
-     <item row="4" column="1">
-      <widget class="QLabel" name="blockValueLabel">
+     <item row="5" column="1">
+      <widget class="QLabel" name="epochValueLabel">
        <property name="text">
         <string/>
        </property>
diff --git a/tikka/slots/pyqt/resources/gui/widgets/currency.ui b/tikka/slots/pyqt/resources/gui/widgets/currency.ui
index f6f2e60c..b18ea800 100644
--- a/tikka/slots/pyqt/resources/gui/widgets/currency.ui
+++ b/tikka/slots/pyqt/resources/gui/widgets/currency.ui
@@ -6,136 +6,18 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>383</width>
-    <height>169</height>
+    <width>724</width>
+    <height>475</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>Currency parameters</string>
   </property>
-  <layout class="QGridLayout" name="gridLayout">
-   <item row="1" column="1">
-    <widget class="QLabel" name="nameValueLabel">
-     <property name="text">
-      <string/>
-     </property>
-    </widget>
-   </item>
-   <item row="4" column="0">
-    <widget class="QLabel" name="monetaryMassLabel">
-     <property name="font">
-      <font>
-       <weight>75</weight>
-       <bold>true</bold>
-      </font>
-     </property>
-     <property name="text">
-      <string>Monetary Mass</string>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-     </property>
-    </widget>
-   </item>
-   <item row="3" column="1">
-    <widget class="QLabel" name="universalDividendValueLabel">
-     <property name="text">
-      <string/>
-     </property>
-    </widget>
-   </item>
-   <item row="3" column="0">
-    <widget class="QLabel" name="universalDividendLabel">
-     <property name="font">
-      <font>
-       <weight>75</weight>
-       <bold>true</bold>
-      </font>
-     </property>
-     <property name="text">
-      <string>Universal Dividend</string>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-     </property>
-    </widget>
-   </item>
-   <item row="5" column="1">
-    <widget class="QLabel" name="membersValueLabel">
-     <property name="text">
-      <string/>
-     </property>
-    </widget>
-   </item>
-   <item row="5" column="0">
-    <widget class="QLabel" name="membersLabel">
-     <property name="font">
-      <font>
-       <weight>75</weight>
-       <bold>true</bold>
-      </font>
-     </property>
-     <property name="text">
-      <string>Members</string>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-     </property>
-    </widget>
-   </item>
-   <item row="4" column="1">
-    <widget class="QLabel" name="monetaryMassValueLabel">
-     <property name="text">
-      <string/>
-     </property>
-    </widget>
-   </item>
-   <item row="1" column="0">
-    <widget class="QLabel" name="nameLabel">
-     <property name="font">
-      <font>
-       <weight>75</weight>
-       <bold>true</bold>
-      </font>
-     </property>
-     <property name="text">
-      <string>Name</string>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-     </property>
-    </widget>
-   </item>
-   <item row="6" column="0">
-    <spacer name="verticalSpacer">
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>20</width>
-       <height>40</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
-   <item row="1" column="2">
-    <spacer name="horizontalSpacer">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>40</width>
-       <height>20</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
-   <item row="0" column="2">
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item alignment="Qt::AlignRight">
     <widget class="QPushButton" name="refreshButton">
      <property name="sizePolicy">
-      <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+      <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
        <horstretch>0</horstretch>
        <verstretch>0</verstretch>
       </sizepolicy>
@@ -149,27 +31,186 @@
      </property>
     </widget>
    </item>
-   <item row="2" column="0">
-    <widget class="QLabel" name="symbolLabel">
-     <property name="font">
-      <font>
-       <weight>75</weight>
-       <bold>true</bold>
-      </font>
-     </property>
-     <property name="text">
-      <string>Symbol</string>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Currency</string>
      </property>
      <property name="alignment">
-      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-     </property>
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+     <layout class="QFormLayout" name="formLayout_2">
+      <property name="labelAlignment">
+       <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+      </property>
+      <item row="0" column="0">
+       <widget class="QLabel" name="nameLabel">
+        <property name="font">
+         <font>
+          <weight>75</weight>
+          <bold>true</bold>
+         </font>
+        </property>
+        <property name="text">
+         <string>Name</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="universalDividendLabel">
+        <property name="font">
+         <font>
+          <weight>75</weight>
+          <bold>true</bold>
+         </font>
+        </property>
+        <property name="text">
+         <string>Universal Dividend</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="symbolLabel">
+        <property name="font">
+         <font>
+          <weight>75</weight>
+          <bold>true</bold>
+         </font>
+        </property>
+        <property name="text">
+         <string>Symbol</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QLabel" name="monetaryMassLabel">
+        <property name="font">
+         <font>
+          <weight>75</weight>
+          <bold>true</bold>
+         </font>
+        </property>
+        <property name="text">
+         <string>Monetary Mass</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="0">
+       <widget class="QLabel" name="membersLabel">
+        <property name="font">
+         <font>
+          <weight>75</weight>
+          <bold>true</bold>
+         </font>
+        </property>
+        <property name="text">
+         <string>Members</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="1">
+       <widget class="QLabel" name="membersValueLabel">
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QLabel" name="universalDividendValueLabel">
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1">
+       <widget class="QLabel" name="monetaryMassValueLabel">
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QLabel" name="symbolValueLabel">
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QLabel" name="nameValueLabel">
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+      </item>
+     </layout>
     </widget>
    </item>
-   <item row="2" column="1">
-    <widget class="QLabel" name="symbolValueLabel">
-     <property name="text">
-      <string/>
-     </property>
+   <item>
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>Blockchain</string>
+     </property>
+     <layout class="QFormLayout" name="formLayout_3">
+      <item row="0" column="0">
+       <widget class="QLabel" name="expectedBlockDurationLabel">
+        <property name="font">
+         <font>
+          <weight>75</weight>
+          <bold>true</bold>
+         </font>
+        </property>
+        <property name="text">
+         <string>Expected Block duration</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QLabel" name="expectedBlockDurationValueLabel">
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="expectedEpochDurationLabel">
+        <property name="font">
+         <font>
+          <weight>75</weight>
+          <bold>true</bold>
+         </font>
+        </property>
+        <property name="text">
+         <string>Expected Epoch duration</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QLabel" name="expectedEpochDurationValueLabel">
+        <property name="text">
+         <string/>
+        </property>
+       </widget>
+      </item>
+     </layout>
     </widget>
    </item>
   </layout>
diff --git a/tikka/slots/pyqt/widgets/connection.py b/tikka/slots/pyqt/widgets/connection.py
index 0ffb22c9..4b1a5b90 100644
--- a/tikka/slots/pyqt/widgets/connection.py
+++ b/tikka/slots/pyqt/widgets/connection.py
@@ -188,11 +188,13 @@ class ConnectionWidget(QWidget, Ui_ConnectionWidget):
                 self.versionValueLabel.setText("")
                 self.peerIDValueLabel.setText("")
                 self.blockValueLabel.setText("")
+                self.epochValueLabel.setText("")
             else:
                 self.softwareValueLabel.setText(node.software)
                 self.versionValueLabel.setText(node.software_version)
                 self.peerIDValueLabel.setText(node.peer_id)
                 self.blockValueLabel.setText(str(node.block))
+                self.epochValueLabel.setText(str(node.epoch_index))
 
     def _on_currency_event(self, _):
         """
diff --git a/tikka/slots/pyqt/widgets/currency.py b/tikka/slots/pyqt/widgets/currency.py
index e6615967..7c5a37ba 100644
--- a/tikka/slots/pyqt/widgets/currency.py
+++ b/tikka/slots/pyqt/widgets/currency.py
@@ -12,7 +12,7 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
+import datetime
 import sys
 from typing import Optional
 
@@ -106,6 +106,7 @@ class CurrencyWidget(QWidget, Ui_currencyWidget):
         else:
             selected_amount = self.application.amounts.get_amount(AMOUNT_UNIT_KEY)
 
+        # Currency parameters
         currency = self.application.currencies.get_current()
         self.nameValueLabel.setText(currency.name)
         self.symbolValueLabel.setText(currency.token_symbol)
@@ -130,6 +131,19 @@ class CurrencyWidget(QWidget, Ui_currencyWidget):
             else self.locale().toString(currency.members_count)
         )
 
+        # Blockchain parameters
+        self.expectedBlockDurationValueLabel.setText(
+            ""
+            if currency.block_duration is None
+            else str(datetime.timedelta(milliseconds=currency.block_duration))
+        )
+
+        self.expectedEpochDurationValueLabel.setText(
+            ""
+            if currency.epoch_duration is None
+            else str(datetime.timedelta(milliseconds=currency.epoch_duration))
+        )
+
     def _on_refresh_button_clicked_event(self):
         """
         Triggered when user click on refresh button
diff --git a/tikka/slots/pyqt/widgets/smith.py b/tikka/slots/pyqt/widgets/smith.py
index a50f93f3..d7b810d9 100644
--- a/tikka/slots/pyqt/widgets/smith.py
+++ b/tikka/slots/pyqt/widgets/smith.py
@@ -786,18 +786,18 @@ class SmithWidget(QWidget, Ui_SmithWidget):
 
             status_string = display_smith_status[self.smith.status.value]
             if self.smith.expire_on is not None:
-                datetime_from_block = (
-                    self.application.block_time.get_datetime_from_block(
+                datetime_from_epoch_index = (
+                    self.application.block_time.get_datetime_from_epoch(
                         self.smith.expire_on
                     )
                 )
-                if datetime_from_block is not None:
+                if datetime_from_epoch_index is not None:
                     expire_on_localized_datetime_string = self.locale().toString(
-                        datetime_from_block,
+                        datetime_from_epoch_index,
                         QLocale.dateTimeFormat(self.locale(), QLocale.ShortFormat),
                     )
                     self.membershipValueLabel.setToolTip(
-                        self._("block #{block}").format(block=self.smith.expire_on)
+                        self._("epoch #{epoch}").format(epoch=self.smith.expire_on)
                     )
                     status_string = (
                         f"{status_string} ({expire_on_localized_datetime_string})"
@@ -826,14 +826,14 @@ class SmithWidget(QWidget, Ui_SmithWidget):
                 )
                 address_item = QTableWidgetItem(str(smith_certification.issuer_address))
 
-                datetime_from_block = (
+                datetime_from_epoch_index = (
                     self.application.block_time.get_datetime_from_block(
                         smith_certification.expire_on_block
                     )
                 )
-                if datetime_from_block is not None:
+                if datetime_from_epoch_index is not None:
                     expire_on_localized_datetime_string = self.locale().toString(
-                        datetime_from_block,
+                        datetime_from_epoch_index,
                         QLocale.dateFormat(self.locale(), QLocale.ShortFormat),
                     )
                     expire_on_item = QTableWidgetItem(
-- 
GitLab