Skip to content
This repository has been archived by the owner on Dec 15, 2021. It is now read-only.

Commit

Permalink
IS-902: add field total elected delegated snapshot (#379)
Browse files Browse the repository at this point in the history
  • Loading branch information
cow-hs authored Nov 5, 2019
1 parent bbe9b3a commit 7005f27
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 38 deletions.
1 change: 1 addition & 0 deletions iconservice/icon_constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ def _generate_next_value_(self, start, count, last_values):
FOUR = auto()
IISS = auto()
DECENTRALIZATION = auto()
IS_1_5_16 = auto()

LATEST = auto()

Expand Down
6 changes: 6 additions & 0 deletions iconservice/icon_service_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ def open(self, conf: 'IconConfig'):
conf[ConfigKey.IPC_TIMEOUT],
conf[ConfigKey.ICON_RC_DIR_PATH])

self._post_open_component_context(context)

self._load_builtin_scores(
context, Address.from_string(conf[ConfigKey.BUILTIN_SCORE_OWNER]))
self._init_global_value_by_governance_score(context)
Expand Down Expand Up @@ -276,6 +278,10 @@ def _close_component_context(cls, context: 'IconScoreContext'):
IconScoreContext.storage.meta.close(context)
IconScoreContext.storage.rc.close()

@classmethod
def _post_open_component_context(cls, context: 'IconScoreContext'):
IconScoreContext.engine.prep.load_term(context)

@classmethod
def get_ready_future(cls):
return IconScoreContext.engine.iiss.get_ready_future()
Expand Down
2 changes: 1 addition & 1 deletion iconservice/iconscore/icon_score_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ def _update_elected_preps_in_term(self, dirty_prep: 'PRep'):
# Just in case, reset the P-Rep grade one to CANDIDATE
dirty_prep.grade = PRepGrade.CANDIDATE

self._term.update_preps([dirty_prep])
self._term.update_preps(self.revision, [dirty_prep])

Logger.info(tag=self.TAG, msg=f"Invalid main and sub prep: {dirty_prep}")

Expand Down
23 changes: 15 additions & 8 deletions iconservice/iiss/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from ..base.type_converter import TypeConverter
from ..base.type_converter_templates import ConstantKeys, ParamType
from ..icon_constant import IISS_MAX_DELEGATIONS, ISCORE_EXCHANGE_RATE, IISS_MAX_REWARD_RATE, \
IconScoreContextType, IISS_LOG_TAG, RCCalculateResult, INVALID_CLAIM_TX
IconScoreContextType, IISS_LOG_TAG, RCCalculateResult, INVALID_CLAIM_TX, Revision
from ..iconscore.icon_score_context import IconScoreContext
from ..iconscore.icon_score_event_log import EventLogEmitter
from ..icx import Intent
Expand Down Expand Up @@ -719,11 +719,11 @@ def update_db(self,
start_term_block: int = context.engine.prep.term.start_block_height
# New P-Rep Term is started
if start_term_block == context.block.height:
self._put_preps_to_rc_db(context)
self._put_preps_to_rc_db(context, context.revision)
self._put_gv_to_rc_db(context, version)

if term is not None and term.is_in_term(context.block.height):
self._put_preps_to_rc_db(context, term)
self._put_preps_to_rc_db(context, context.revision, term)

return rc_state_hash

Expand Down Expand Up @@ -853,7 +853,7 @@ def put_block_produce_info_to_rc_db(cls,
context.storage.rc.put(rc_block_batch, data)

@classmethod
def _put_preps_to_rc_db(cls, context: 'IconScoreContext', term: Optional['Term'] = None):
def _put_preps_to_rc_db(cls, context: 'IconScoreContext', revision: int, term: Optional['Term'] = None):
# If term is not None, it is the term which has been changed in term
assert context.is_decentralized()

Expand All @@ -863,13 +863,20 @@ def _put_preps_to_rc_db(cls, context: 'IconScoreContext', term: Optional['Term']
else:
block_height: int = context.block.height

Logger.debug(
if revision < Revision.IS_1_5_16.value:
total_elected_prep_delegated: int = term.total_elected_prep_delegated_snapshot
else:
total_elected_prep_delegated: int = term.total_elected_prep_delegated

Logger.info(
tag=cls.TAG,
msg="_put_preps_for_rc_db() start: "
f"total_elected_prep_delegated={term.total_elected_prep_delegated}")
msg=f"put_preps_for_rc_db"
f"block_height={block_height}"
f"total_elected_prep_delegated={term.total_elected_prep_delegated}"
f"total_elected_prep_delegated_snapshot={total_elected_prep_delegated}")

data: 'PRepsData' = RewardCalcDataCreator.create_prep_data(block_height,
term.total_elected_prep_delegated,
total_elected_prep_delegated,
term.preps)
context.storage.rc.put(context.rc_block_batch, data)

Expand Down
31 changes: 30 additions & 1 deletion iconservice/iiss/reward_calc/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from typing import TYPE_CHECKING, Optional, Tuple

from iconcommons import Logger
from ..reward_calc.msg_data import Header, TxData
from ..reward_calc.msg_data import Header, TxData, PRepsData
from ...base.exception import DatabaseException
from ...database.db import KeyValueDatabase
from ...icon_constant import (
Expand Down Expand Up @@ -84,6 +84,7 @@ def open(self, context: IconScoreContext, path: str):
self._path = path

self._db = self.create_current_db(path)

self._db_iiss_tx_index = self._load_last_transaction_index()
Logger.info(tag=IISS_LOG_TAG, msg=f"last_transaction_index on open={self._db_iiss_tx_index}")

Expand Down Expand Up @@ -290,3 +291,31 @@ def scan_rc_db(cls, rc_data_path: str) -> Tuple[str, str, str]:
f"iiss_rc_db={iiss_rc_db_path}")

return current_rc_db_path, standby_rc_db_path, iiss_rc_db_path

def get_total_elected_prep_delegated_snapshot(self) -> int:
"""
this method calculates about origin preps delegated amount.
preps who are determied on new term are included on new RC DB when it created first.
so you can get origin preps data in first element.
"""

db = self._db.get_sub_db(PRepsData.PREFIX)
preps: Optional[list] = None
for k, v in db.iterator():
data: 'PRepsData' = PRepsData.from_bytes(k, v)
preps = data.prep_list
break

if not preps:
return -1

ret = 0
if preps:
for info in preps:
ret += info.value

Logger.info(tag=IISS_LOG_TAG,
msg=f"get_total_elected_prep_delegated_snapshot load: {ret}")

return ret
2 changes: 0 additions & 2 deletions iconservice/iiss/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ class Storage(StorageBase):
CALC_END_BLOCK_HEIGHT_KEY: bytes = PREFIX + b'cebh'
CALC_PERIOD_KEY: bytes = PREFIX + b'pk'

TOTAL_PREP_DELEGATED_KEY: bytes = PREFIX + b'tpd'

def __init__(self, db: 'ContextDatabase'):
super().__init__(db)
self._meta_data: Optional['IISSMetaData'] = None
Expand Down
41 changes: 31 additions & 10 deletions iconservice/prep/data/term.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def __init__(self,
self._total_supply = total_supply
self._total_delegated = total_delegated
self._total_elected_prep_delegated = 0
self._total_elected_prep_delegated_snapshot: int = 0

self._main_preps: List['PRepSnapshot'] = []
self._sub_preps: List['PRepSnapshot'] = []
Expand Down Expand Up @@ -115,6 +116,7 @@ def __str__(self) -> str:
f"total_supply={self._total_supply} " \
f"total_delegated={self._total_delegated} " \
f"total_elected_prep_delegated={self._total_elected_prep_delegated} " \
f"_total_elected_prep_delegated_snapshot={self._total_elected_prep_delegated_snapshot} " \
f"root_hash={bytes_to_hex(self._merkle_root_hash)}"

def __contains__(self, address: 'Address') -> bool:
Expand All @@ -136,6 +138,7 @@ def __eq__(self, other: 'Term') -> bool:
and self._total_supply == other._total_supply \
and self._total_delegated == other._total_delegated \
and self._total_elected_prep_delegated == other._total_elected_prep_delegated \
and self._total_elected_prep_delegated_snapshot == other._total_elected_prep_delegated_snapshot \
and self._main_preps == other._main_preps \
and self._sub_preps == other._sub_preps \
and self._preps_dict == other._preps_dict \
Expand Down Expand Up @@ -196,6 +199,14 @@ def total_elected_prep_delegated(self) -> int:
"""
return self._total_elected_prep_delegated

@property
def total_elected_prep_delegated_snapshot(self) -> int:
"""The sum of delegated amount which only main and sub P-Reps got on start Term
:return:
"""
return self._total_elected_prep_delegated_snapshot

@property
def main_preps(self) -> List['PRepSnapshot']:
return self._main_preps
Expand Down Expand Up @@ -257,10 +268,12 @@ def set_preps(self,

self._preps_dict[snapshot.address] = snapshot

self._total_elected_prep_delegated_snapshot = total_elected_prep_delegated
self._total_elected_prep_delegated = total_elected_prep_delegated

self._generate_root_hash()

def update_preps(self, invalid_elected_preps: Iterable['PRep']):
def update_preps(self, revision: int, invalid_elected_preps: Iterable['PRep']):
"""Update main and sub P-Reps with invalid elected P-Reps
:param invalid_elected_preps:
Expand All @@ -270,17 +283,17 @@ def update_preps(self, invalid_elected_preps: Iterable['PRep']):
self._check_access_permission()

for prep in invalid_elected_preps:
if self._remove_invalid_main_prep(prep) >= 0:
if self._remove_invalid_main_prep(revision, prep) >= 0:
continue
if self._remove_invalid_sub_prep(prep) >= 0:
if self._remove_invalid_sub_prep(revision, prep) >= 0:
continue

raise AssertionError(f"{prep.address} not in elected P-Reps: {self}")

if self.is_dirty():
self._generate_root_hash()

def _remove_invalid_main_prep(self, invalid_prep: 'PRep') -> int:
def _remove_invalid_main_prep(self, revision: int, invalid_prep: 'PRep') -> int:
"""Replace an invalid main P-Rep with the top-ordered sub P-Rep
:param invalid_prep: an invalid main P-Rep
Expand All @@ -306,13 +319,13 @@ def _remove_invalid_main_prep(self, invalid_prep: 'PRep') -> int:
msg=f"Replace a main P-Rep: "
f"index={index} {address} -> {self._main_preps[index].address}")

self._reduce_total_elected_prep_delegated(invalid_prep, invalid_prep_snapshot.delegated)
self._reduce_total_elected_prep_delegated(revision, invalid_prep, invalid_prep_snapshot.delegated)
del self._preps_dict[address]

self._flag |= _Flag.DIRTY
return index

def _remove_invalid_sub_prep(self, invalid_prep: 'PRep') -> int:
def _remove_invalid_sub_prep(self, revision: int, invalid_prep: 'PRep') -> int:
"""Remove an invalid sub P-Rep from self._sub_preps
:param invalid_prep: an invalid sub P-Rep
Expand All @@ -323,20 +336,21 @@ def _remove_invalid_sub_prep(self, invalid_prep: 'PRep') -> int:

if index >= 0:
invalid_prep_snapshot = self._sub_preps.pop(index)
self._reduce_total_elected_prep_delegated(invalid_prep, invalid_prep_snapshot.delegated)
self._reduce_total_elected_prep_delegated(revision, invalid_prep, invalid_prep_snapshot.delegated)
del self._preps_dict[invalid_prep.address]

self._flag |= _Flag.DIRTY

return index

def _reduce_total_elected_prep_delegated(self, invalid_prep: 'PRep', delegated: int):
def _reduce_total_elected_prep_delegated(self, revision: int, invalid_prep: 'PRep', delegated: int):
"""Reduce total_elected_prep_delegated by the delegated amount of the given invalid P-Rep
:param invalid_prep:
:param delegated:
:return:
"""

# The P-Rep that gets BLOCK_VALIDATION penalty cannot serve as a main or sub P-Rep during this term,
# but its B2 reward should be provided continuously
if invalid_prep.status != PRepStatus.ACTIVE \
Expand All @@ -357,7 +371,6 @@ def _index_of_prep(cls, preps: List['PRepSnapshot'], address: 'Address') -> int:
return -1

def _generate_root_hash(self):

def _gen(snapshots: Iterable['PRepSnapshot']) -> bytes:
for snapshot in snapshots:
yield snapshot.address.to_bytes_including_prefix()
Expand All @@ -366,7 +379,7 @@ def _gen(snapshots: Iterable['PRepSnapshot']) -> bytes:
RootHashGenerator.generate_root_hash(values=_gen(self._main_preps), do_hash=True)

@classmethod
def from_list(cls, data: List) -> 'Term':
def from_list(cls, data: List, total_elected_prep_delegated_from_rc: int) -> 'Term':
assert data[0] == cls._VERSION
sequence: int = data[1]
start_block_height: int = data[2]
Expand All @@ -393,6 +406,14 @@ def from_list(cls, data: List) -> 'Term':
total_elected_prep_delegated += delegated

term._total_elected_prep_delegated = total_elected_prep_delegated

if total_elected_prep_delegated_from_rc < 0:
Logger.warning(tag=cls.TAG,
msg=f"total_elected_prep_delegated_from_rc < 0")
term._total_elected_prep_delegated_snapshot = total_elected_prep_delegated
else:
term._total_elected_prep_delegated_snapshot = total_elected_prep_delegated_from_rc

term._generate_root_hash()

return term
Expand Down
5 changes: 4 additions & 1 deletion iconservice/prep/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,14 @@ def open(self,
block_validation_penalty_threshold)

self._load_preps(context)
self.term: Optional['Term'] = context.storage.prep.get_term(context)
self._initial_irep = irep

context.engine.iiss.add_listener(self)

def load_term(self,
context: 'IconScoreContext'):
self.term: Optional['Term'] = context.storage.prep.get_term(context)

def _init_penalty_imposer(self,
penalty_grace_period: int,
low_productivity_penalty_threshold: int,
Expand Down
10 changes: 7 additions & 3 deletions iconservice/prep/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@

from typing import TYPE_CHECKING, Optional, Iterable

from .data.prep import PRep
from iconcommons import Logger
from .data import Term
from .data.prep import PRep
from ..base.ComponentBase import StorageBase
from ..base.exception import InvalidParamsException
from ..utils.msgpack_for_db import MsgPackForDB
Expand Down Expand Up @@ -93,8 +94,11 @@ def put_term(self, context: 'IconScoreContext', term: 'Term'):
value: bytes = MsgPackForDB.dumps(term.to_list())
self._db.put(context, self.TERM_KEY, value)

def get_term(self, context: 'IconScoreContext') -> Optional['Term']:
def get_term(self,
context: 'IconScoreContext') -> Optional['Term']:

value: bytes = self._db.get(context, self.TERM_KEY)
if value:
total_elected_prep_delegated_snapshot: int = context.storage.rc.get_total_elected_prep_delegated_snapshot()
data: list = MsgPackForDB.loads(value)
return Term.from_list(data)
return Term.from_list(data, total_elected_prep_delegated_snapshot)
Loading

0 comments on commit 7005f27

Please sign in to comment.