lockdrop-simulation/tests/test_unlock_schedule.py
2025-08-04 14:10:17 +05:30

112 lines
5.2 KiB
Python

import unittest
from datetime import datetime
from tabulate import tabulate
from base_test import BaseAllocationTest, SECONDS_PER_YEAR, BLOCK_DURATION_SECONDS
class UnlockScheduleTest(BaseAllocationTest):
"""Test unlock schedule calculations"""
def test_unlock_schedule_calculation(self):
"""Test unlock schedule calculations for all lock periods"""
print("\nUNLOCK SCHEDULE CALCULATIONS")
genesis_timestamp = self._get_genesis_time_from_api()
if not genesis_timestamp:
self.skipTest("Could not retrieve genesis time")
# Test first star and galaxy from each lock period (limit to avoid too many API calls)
test_points = []
for lock_period in sorted(self.points_by_duration.keys()):
duration_data = self.points_by_duration[lock_period]
if duration_data['star']:
star_data = duration_data['star']
zenith_addr = star_data['zenith_address']
if zenith_addr:
test_points.append((star_data['point'], zenith_addr, lock_period, star_data['block_timestamp']))
if duration_data['galaxy']:
galaxy_data = duration_data['galaxy']
zenith_addr = galaxy_data['zenith_address']
if zenith_addr:
test_points.append((galaxy_data['point'], zenith_addr, lock_period, galaxy_data['block_timestamp']))
# Collect data for tables
unlock_blocks_data = []
initial_unlock_data = []
for point, zenith_addr, lock_period, start_timestamp in test_points:
with self.subTest(point=point):
# Get allocation with unlock schedule from zenithd
allocation_data = self._get_point_allocation_from_api(zenith_addr, point)
self.assertIsNotNone(allocation_data, f"No allocation data for {point}")
unlock_schedule = allocation_data.get('unlock_schedule')
self.assertIsNotNone(unlock_schedule, f"No unlock_schedule for {point}")
# Use start timestamp from watcher events
# Calculate time difference and pregenesis blocks
time_diff_seconds = genesis_timestamp - start_timestamp
pregenesis_blocks = time_diff_seconds // BLOCK_DURATION_SECONDS
# Calculate total lock duration in blocks (years * seconds_per_year / block_time)
total_lock_blocks = lock_period * SECONDS_PER_YEAR // BLOCK_DURATION_SECONDS
# Calculate expected remaining blocks
expected_unlock_blocks = total_lock_blocks - pregenesis_blocks
api_unlock_blocks = int(unlock_schedule['unlock_blocks'])
blocks_diff = api_unlock_blocks - expected_unlock_blocks
# Convert start timestamp to ISO format
start_time_iso = datetime.fromtimestamp(start_timestamp).isoformat()
unlock_blocks_data.append([
point,
f"{lock_period} years",
start_time_iso,
f"{expected_unlock_blocks:,}",
f"{api_unlock_blocks:,}",
f"{blocks_diff:+,}" if blocks_diff != 0 else "0"
])
self.assertEqual(expected_unlock_blocks, api_unlock_blocks,
f"Unlock blocks mismatch for {point}: "
f"Expected={expected_unlock_blocks}, "
f"zenithd={api_unlock_blocks}, "
f"Diff={blocks_diff}")
# Calculate expected initial unlock amount
total_allocation = int(allocation_data['allocated_amount']['amount'])
expected_initial_unlock = (pregenesis_blocks * total_allocation) // total_lock_blocks
api_initial_unlock = int(unlock_schedule['initial_unlock_amount']['amount'])
unlock_diff = api_initial_unlock - expected_initial_unlock
initial_unlock_data.append([
point,
f"{lock_period} years",
start_time_iso,
f"{expected_initial_unlock:,}",
f"{api_initial_unlock:,}",
f"{unlock_diff:+,}" if unlock_diff != 0 else "0"
])
self.assertEqual(expected_initial_unlock, api_initial_unlock,
f"Initial unlock amount mismatch for {point}: "
f"Expected={expected_initial_unlock:,} $sZ, "
f"zenithd={api_initial_unlock:,} $sZ, "
f"Diff={unlock_diff:+,} $sZ")
# Print tables
print("\nUnlock Blocks Comparison:")
unlock_blocks_headers = ["Point", "Lock Period", "Start Time", "Expected Blocks", "zenithd Blocks", "Difference"]
print(tabulate(unlock_blocks_data, headers=unlock_blocks_headers, tablefmt="grid"))
print("\nInitial Unlock Amounts Comparison:")
initial_unlock_headers = ["Point", "Lock Period", "Start Time", "Expected ($sZ)", "zenithd ($sZ)", "Difference"]
print(tabulate(initial_unlock_data, headers=initial_unlock_headers, tablefmt="grid"))
if __name__ == "__main__":
unittest.main(verbosity=2)