Skip to content

Commit

Permalink
Support paying burn fee to reactivate zeroed tz1 address (tezos-rewar…
Browse files Browse the repository at this point in the history
…d-distributor-organization#181)

* Fixes tezos-reward-distributor-organization#178
* fixed unit tests
* fix example yaml
* resolve float/int issue
* added burn fees to total amount for display
* don't show disclaimer when running command line tools
* add support for tzstats, checking 0 balance of receiver
* added sleeps for tzstats api
  • Loading branch information
utdrmac authored and jdsika committed Jan 19, 2020
1 parent df5c86d commit 1d487cf
Show file tree
Hide file tree
Showing 30 changed files with 408 additions and 142 deletions.
4 changes: 3 additions & 1 deletion examples/tz1boot1pK9h2BVGXdyvfQSv8kd1LQM6H889.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ owners_map : {tz1boot1pK9h2BVGXdyvfQSv8kd1LQM6H889: 0.3, KT1KLQbYFtFZ5mAEnfEMZaW
specials_map : {KT19g4JHTd3QYuYcpKFFiwViEzQ5n6ovYx1V: 7.5}
supporters_set : {KT1SenDhs9rL9fpZV3cLRXFovEBafmGqkki8}
min_delegation_amt : 1000
reactivate_zeroed : False
delegator_pays_xfer_fee : True
delegator_pays_ra_fee : True
rules_map:
KT1MMhmTkUoHez4u58XMZL7NkpU9FWY4QLn3: KT1MMhmTkUoHez4u58XMZL7NkpU9FWY4QLn0
KT1D33n8zp1bqBkViiQtLLPLEGRW9xcqihY3: KT1MMhmTkUoHez4u58XMZL7NkpU9FWY4QLn0
KT1Ao8UXNJ9Dz71Wx3m8yzYNdnNQp2peqtM0: TOE
KT1VyxJWhe9oz3v4qwTp2U6Rb17ocHGpJmW0: TOB
KT19cJWfbDNXT4azVbgTBvtLMeqweuHH8W20: TOF
mindelegation: TOB
mindelegation: TOB
16 changes: 11 additions & 5 deletions src/calc/calculate_phase0.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,19 @@ def calculate(self, reward_logs=None, total_reward_amount=None):
reward_logs = []
delegate_staking_balance = self.reward_provider_model.delegate_staking_balance
delegators_balance_dict = self.reward_provider_model.delegator_balance_dict

# calculate how rewards will be distributed
# ratio is stake/total staking balance
# total of ratios must be 1
for address, balance in delegators_balance_dict.items():
total_delegator_balance += balance

ratio = balance / delegate_staking_balance
reward_item = RewardLog(address=address, type=reward_log.TYPE_DELEGATOR, balance=balance)
for address, delegator_info in delegators_balance_dict.items():

staking_balance = delegator_info["staking_balance"]
current_balance = delegator_info["current_balance"]
total_delegator_balance += staking_balance

ratio = staking_balance / delegate_staking_balance
reward_item = RewardLog(address=address, type=reward_log.TYPE_DELEGATOR, staking_balance=staking_balance, current_balance=current_balance)
reward_item.ratio = ratio
reward_item.ratio0 = reward_item.ratio

Expand All @@ -49,7 +54,8 @@ def calculate(self, reward_logs=None, total_reward_amount=None):
reward_logs.append(reward_item)

owners_rl = RewardLog(address=reward_log.TYPE_OWNERS_PARENT, type=reward_log.TYPE_OWNERS_PARENT,
balance=delegate_staking_balance - total_delegator_balance)
staking_balance=delegate_staking_balance - total_delegator_balance,
current_balance=0)
owners_rl.ratio = (1 - ratio_sum)
owners_rl.ratio0 = owners_rl.ratio

Expand Down
10 changes: 5 additions & 5 deletions src/calc/calculate_phase1.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ def calculate(self, reward_data0, total_amount):

# exclude requested addresses from reward list
for rl0 in reward_data0:
total_balance += rl0.balance
total_balance += rl0.staking_balance
if rl0.address in self.excluded_set:
rl0.skip(desc=BY_CONFIGURATION, phase=self.phase)
rewards.append(rl0)
total_balance_excluded += rl0.balance
elif MIN_DELEGATION_KEY in self.excluded_set and rl0.balance < self.min_delegation_amount:
total_balance_excluded += rl0.staking_balance
elif MIN_DELEGATION_KEY in self.excluded_set and rl0.staking_balance < self.min_delegation_amount:
rl0.skip(desc=BY_MIN_DELEGATION, phase=self.phase)
rewards.append(rl0)
total_balance_excluded += rl0.balance
total_balance_excluded += rl0.staking_balance
else:
# ratio will be replaced with actual ratio, read below
rewards.append(rl0)
Expand All @@ -55,7 +55,7 @@ def calculate(self, reward_data0, total_amount):

# calculate new ratio using remaining balance
for rl1 in self.filterskipped(rewards):
rl1.ratio = rl1.balance / new_total_balance
rl1.ratio = rl1.staking_balance / new_total_balance
rl1.ratio1 = rl1.ratio

# total reward amount needs to be diminished at the same rate total balance diminishes
Expand Down
10 changes: 5 additions & 5 deletions src/calc/calculate_phase2.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,16 @@ def calculate(self, reward_data1, total_amount):
# exclude requested addresses from reward list
for rl1 in self.filterskipped(reward_data1):

total_balance += rl1.balance
total_balance += rl1.staking_balance

if rl1.address in self.excluded_set:
rl1.skip(desc=BY_CONFIGURATION, phase=self.phase)
rewards.append(rl1)
total_balance_excluded += rl1.balance
elif MIN_DELEGATION_KEY in self.excluded_set and rl1.balance < self.min_delegation_amount:
total_balance_excluded += rl1.staking_balance
elif MIN_DELEGATION_KEY in self.excluded_set and rl1.staking_balance < self.min_delegation_amount:
rl1.skip(desc=BY_MIN_DELEGATION, phase=self.phase)
rewards.append(rl1)
total_balance_excluded += rl1.balance
total_balance_excluded += rl1.staking_balance
else:
# ratio2 will be replaced with actual ratio, read below
rewards.append(rl1)
Expand All @@ -57,7 +57,7 @@ def calculate(self, reward_data1, total_amount):

# calculate new ratio using remaining balance
for rl2 in self.filterskipped(rewards):
rl2.ratio = rl2.balance / new_total_balance
rl2.ratio = rl2.staking_balance / new_total_balance
rl2.ratio2 = rl2.ratio

# total reward amount remains the same
Expand Down
6 changes: 3 additions & 3 deletions src/calc/calculate_phase3.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class CalculatePhase3(CalculatePhaseBase):
"""
-- Phase3 : Founders Phase --
At stage 3, Founders record is created. Founders record is later on splitted into founder records, for each founder.
At stage 3, Founders record is created. Founders record is later split into founder records, for each founder.
If any address is excluded at this stage, its reward is given to founders.
Fee rates are set at this stage.
"""
Expand Down Expand Up @@ -37,7 +37,7 @@ def calculate(self, reward_data2, total_amount):
rl2.skip(desc=BY_CONFIGURATION, phase=self.phase)
new_rewards.append(rl2)
total_excluded_ratio += rl2.ratio
elif MIN_DELEGATION_KEY in self.excluded_set and rl2.balance < self.min_delegation_amount:
elif MIN_DELEGATION_KEY in self.excluded_set and rl2.staking_balance < self.min_delegation_amount:
rl2.skip(desc=BY_MIN_DELEGATION, phase=self.phase)
new_rewards.append(rl2)
total_excluded_ratio += rl2.ratio
Expand All @@ -57,7 +57,7 @@ def calculate(self, reward_data2, total_amount):

# create founders parent record
if total_service_fee_ratio > 1e-6: # >0
rl = RewardLog(address=TYPE_FOUNDERS_PARENT, type=TYPE_FOUNDERS_PARENT, balance=0)
rl = RewardLog(address=TYPE_FOUNDERS_PARENT, type=TYPE_FOUNDERS_PARENT, staking_balance=0, current_balance=0)
rl.service_fee_rate = 0
rl.service_fee_ratio = 0
rl.ratio = total_service_fee_ratio
Expand Down
4 changes: 2 additions & 2 deletions src/calc/calculate_phase4.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def calculate(self, reward_data3, total_amount):
for rl3 in self.filterskipped(reward_data3):
if rl3.type == TYPE_FOUNDERS_PARENT:
for addr, ratio in self.founders_map.items():
rl4 = RewardLog(addr, TYPE_FOUNDER, 0)
rl4 = RewardLog(addr, TYPE_FOUNDER, 0, 0)
# new ratio is parent ratio * ratio of the founder
rl4.ratio = ratio * rl3.ratio
rl4.ratio4 = rl4.ratio
Expand All @@ -46,7 +46,7 @@ def calculate(self, reward_data3, total_amount):

elif rl3.type == TYPE_OWNERS_PARENT:
for addr, ratio in self.owners_map.items():
rl4 = RewardLog(addr, TYPE_OWNER, ratio * rl3.balance)
rl4 = RewardLog(addr, TYPE_OWNER, ratio * rl3.staking_balance, 0)
# new ratio is parent ratio * ratio of the owner
rl4.ratio = ratio * rl3.ratio
rl4.ratio4 = rl4.ratio
Expand Down
1 change: 0 additions & 1 deletion src/calc/calculate_phase5.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,4 @@ def calculate(self, reward_data4, total_amount):
if rl.address in self.addr_dest_dict:
rl.paymentaddress = self.addr_dest_dict[rl.address]


return reward_data4, total_amount
2 changes: 1 addition & 1 deletion src/calc/calculate_phase6.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def calculate(self, reward_data5, total_amount):

for addr, rl_list in payment_address_list_dict.items():
if len(rl_list) > 1:
total_balance = sum([rl.balance for rl in rl_list])
total_balance = sum([rl.staking_balance for rl in rl_list])
total_ratio = sum([rl.ratio for rl in rl_list])
total_payment_amount = sum([rl.amount for rl in rl_list])
total_service_fee_amount = sum([rl.service_fee_amount for rl in rl_list])
Expand Down
39 changes: 39 additions & 0 deletions src/calc/calculate_phase7.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from log_config import main_logger
from model import reward_log
from calc.calculate_phase_base import CalculatePhaseBase, BY_ZERO_BALANCE

logger = main_logger

class CalculatePhase7(CalculatePhaseBase):
"""
-- Phase7 : Check if current delegator balance is 0 --
At stage 7, check each delegate's current balance. If 0, and baker is not reactivating,
then mark payment as not-payable
"""

def __init__(self, reactivate_zeroed) -> None:
super().__init__()
self.reactivate_zeroed = reactivate_zeroed
self.phase = 7

def calculate(self, reward_logs):

reward_data7 = []

for delegate in reward_logs:

# If delegate's current balance is 0, and we are NOT reactivating it,
# then mark address as being skipped with a description to be included
# in the CSV payment report

if (delegate.type == reward_log.TYPE_DELEGATOR and delegate.current_balance == 0):

if self.reactivate_zeroed:
delegate.needs_activation = True
else:
delegate.skip(BY_ZERO_BALANCE, self.phase)

reward_data7.append(delegate)

return reward_data7
1 change: 1 addition & 0 deletions src/calc/calculate_phase_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

BY_CONFIGURATION = "Excluded by configuration"
BY_MIN_DELEGATION = "Excluded by min delegation"
BY_ZERO_BALANCE = "Excluded by zero balance"

class CalculatePhaseBase(ABC):

Expand Down
3 changes: 1 addition & 2 deletions src/calc/phased_payment_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def calculate(self, reward_provider_model):
logger.debug("NO REWARDS to process!")
return [], 0

assert reward_provider_model.delegate_staking_balance == sum([rl.balance for rl in rwrd_logs])
assert reward_provider_model.delegate_staking_balance == sum([rl.staking_balance for rl in rwrd_logs])
assert self.almost_equal(1, sum([rl.ratio for rl in rwrd_logs]))

# calculate phase 1
Expand Down Expand Up @@ -86,7 +86,6 @@ def calculate(self, reward_provider_model):
phase4 = CalculatePhase4(self.founders_map, self.owners_map)
rwrd_logs, total_rwrd_amnt = phase4.calculate(rwrd_logs, total_rwrd_amnt)


# calculate amounts
phase_last = CalculatePhaseFinal()
rwrd_logs, total_rwrd_amnt = phase_last.calculate(rwrd_logs, total_rwrd_amnt)
Expand Down
9 changes: 5 additions & 4 deletions src/calc/test_calculatePhase0.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,19 @@ def test_calculate(self):
phase0 = CalculatePhase0(model)
reward_data, total_rewards = phase0.calculate()

staking_balance = int(model.delegate_staking_balance)
delegate_staking_balance = int(model.delegate_staking_balance)

# total reward ratio is 1
self.assertTrue(1.0, sum(r.ratio0 for r in reward_data))

# check that ratio calculations are correct
delegators_balances = model.delegator_balance_dict
delegators_balances_dict = model.delegator_balance_dict

# check ratios
for (address, balance), reward in zip(delegators_balances.items(),reward_data):
for (address, delegator_info), reward in zip(delegators_balances_dict.items(), reward_data):
# ratio must be equal to stake/total staking balance
self.assertEqual(int(balance) / staking_balance, reward.ratio0)
delegator_staking_balance = int(delegator_info["staking_balance"])
self.assertEqual(delegator_staking_balance / delegate_staking_balance, reward.ratio0)

# last one is owners record
self.assertTrue(reward_data[-1].type == reward_log.TYPE_OWNERS_PARENT)
2 changes: 1 addition & 1 deletion src/calc/test_calculatePhase1.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def test_calculate(self):
total_reward = 1000

for i, ratio in enumerate(ratios,start=1):
rl0 = RewardLog(address="addr" + str(i), type="D", balance=total_reward * ratio)
rl0 = RewardLog(address="addr" + str(i), type="D", staking_balance=total_reward * ratio, current_balance=0)
rl0.ratio0 = ratio
rewards.append(rl0)

Expand Down
4 changes: 2 additions & 2 deletions src/calc/test_calculatePhase2.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ def test_calculate(self):
total_reward = 1000

for i, addr in enumerate(ratios, start=1):
rl0 = RewardLog(address="addr" + str(i), type="D", balance=total_reward * ratios[addr])
rl0 = RewardLog(address="addr" + str(i), type="D", staking_balance=total_reward * ratios[addr], current_balance=0)
rl0.ratio1 = ratios[addr]
rewards.append(rl0)

rewards.append(RewardLog("addrdummy", "D", 0).skip("skipped for testing", 2))
rewards.append(RewardLog("addrdummy", "D", 0, 0).skip("skipped for testing", 2))

excluded_set = {"addr1"}

Expand Down
8 changes: 4 additions & 4 deletions src/calc/test_calculatePhase3.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ def test_calculate(self):
total_reward = 1000

for i, ratio in enumerate(ratios, start=1):
rl0 = RewardLog(address="addr" + str(i), type="D", balance=total_reward * ratio)
rl0 = RewardLog(address="addr" + str(i), type="D", staking_balance=total_reward * ratio, current_balance=0)
rl0.ratio = ratio
rl0.ratio2 = ratio
rewards.append(rl0)

rewards.append(RewardLog("addrdummy", "D", 0).skip("skipped for testing", 2))
rewards.append(RewardLog("addrdummy", "D", 0, 0).skip("skipped for testing", 2))

excluded_set = {"addr1"}

Expand Down Expand Up @@ -58,12 +58,12 @@ def test_calculate_sepecials(self):
total_reward = 1000

for i, ratio in enumerate(ratios, start=1):
rl0 = RewardLog(address="addr" + str(i), type="D", balance=total_reward * ratio)
rl0 = RewardLog(address="addr" + str(i), type="D", staking_balance=total_reward * ratio, current_balance=0)
rl0.ratio = ratio
rl0.ratio2 = ratio
rewards.append(rl0)

rewards.append(RewardLog("addrdummy", "D", 0).skip("skipped for testing", 2))
rewards.append(RewardLog("addrdummy", "D", 0, 0).skip("skipped for testing", 2))

excluded_set = {"addr1"}
supporters_set = {"addr2"}
Expand Down
5 changes: 2 additions & 3 deletions src/calc/test_calculatePhase4.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,20 @@ def test_calculate(self):
total_reward = 1000

for i, ratio in enumerate(ratios, start=1):
rl0 = RewardLog(address="addr" + str(i), type="D", balance=total_reward * ratio)
rl0 = RewardLog(address="addr" + str(i), type="D", staking_balance=total_reward * ratio, current_balance=0)
rl0.ratio = ratio
rl0.ratio3 = ratio
rewards.append(rl0)

rewards[0].type = TYPE_OWNERS_PARENT
rewards[1].type = TYPE_FOUNDERS_PARENT

rewards.append(RewardLog("addrdummy", "D", 0).skip("skipped for testing", 3))
rewards.append(RewardLog("addrdummy", "D", 0, 0).skip("skipped for testing", 3))

founders_map = {"addr1": 0.4, "addr2": 0.6}
owners_map = {"addr1": 0.6, "addr2": 0.4}

phase4 = CalculatePhase4(founders_map, owners_map)

new_rewards, new_total_reward = phase4.calculate(rewards, total_reward)

# new_total_reward = total_reward
Expand Down
4 changes: 2 additions & 2 deletions src/calc/test_calculatePhase5.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ def test_calculate(self):
total_reward = 1000

for i, ratio in enumerate(ratios, start=1):
rl0 = RewardLog(address="addr" + str(i), type="D", balance=total_reward * ratio)
rl0 = RewardLog(address="addr" + str(i), type="D", staking_balance=total_reward * ratio, current_balance=0)
rl0.ratio = ratio
rl0.ratio4 = ratio
rewards.append(rl0)

rewards.append(RewardLog("addrdummy","D",0).skip("skipped for testing",4))
rewards.append(RewardLog("addrdummy", "D" , 0, 0).skip("skipped for testing",4))

phase5 = CalculatePhase5({"addr2":"addr1"})

Expand Down
3 changes: 2 additions & 1 deletion src/cli/cmd_manager.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os
from subprocess import STDOUT, check_output, TimeoutExpired, CalledProcessError

from log_config import main_logger
from util.client_utils import clear_terminal_chars

Expand All @@ -26,6 +26,7 @@ def execute(self, cmd, verbose_override=None, timeout=None):
logger.debug("--> Verbose : Command is |{}|".format(cmd))

try:
os.environ["TEZOS_CLIENT_UNSAFE_DISABLE_DISCLAIMER"] = "Y"
output = check_output(cmd, shell=True, stderr=STDOUT, timeout=timeout, encoding='utf8')
except TimeoutExpired as e:
logger.info("Command timed out")
Expand Down
Loading

0 comments on commit 1d487cf

Please sign in to comment.