Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
node_modules
emit_map.json
emit_map_migration.json
*.csv
*copy*
*.hex
347 changes: 347 additions & 0 deletions get_emit_migration.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,347 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import bittensor\n",
"import pandas as pd\n",
"import json\n",
"import substrateinterface as pysub\n",
"\n",
"from typing import List, Tuple, Optional, Dict\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"WS_URL = \"wss://archive.chain.opentensor.ai:443\"\n",
"sub = bittensor.subtensor(WS_URL)\n",
"BLOCK_TIME = 12\n",
"\n",
"BLOCKS_PER_HOUR = 3600 / BLOCK_TIME"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"START_BLOCK = 3_791_351 # First upgrade block\n",
"END_BLOCK = 3_811_908 # NEW Fix block"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# TODO: Fill in with ss58 addresses\n",
"# Note: this script assumes this *coldkey* was not swapped in the period between START_BLOCK and END_BLOCK\n",
"OWNER_KEY = \"OWNER KEY\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Assume only ever ONE hotkey swap happened\n",
"owned_hotkeys_start = sub.query_subtensor(\"OwnedHotkeys\", START_BLOCK, params=[OWNER_KEY])\n",
"owned_hotkeys_end = sub.query_subtensor(\"OwnedHotkeys\", END_BLOCK, params=[OWNER_KEY])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"all_w_pending = []\n",
"for hk in owned_hotkeys_start:\n",
" pending_em = sub.query_subtensor(\"PendingdHotkeyEmission\", START_BLOCK + 10, params=[hk.value])\n",
" if pending_em:\n",
" all_w_pending.append((hk.value, pending_em.value))\n",
"\n",
"all_w_pending = sorted(all_w_pending, key=lambda x: x[1], reverse=True)\n",
"START_HOTKEY = all_w_pending[0][0]\n",
"print(f\"Start hotkey: {START_HOTKEY}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"all_w_pending = []\n",
"for hk in owned_hotkeys_end:\n",
" pending_em = sub.query_subtensor(\"PendingdHotkeyEmission\", END_BLOCK - 120, params=[hk.value])\n",
" if pending_em:\n",
" all_w_pending.append((hk.value, pending_em.value))\n",
"\n",
"all_w_pending = sorted(all_w_pending, key=lambda x: x[1], reverse=True)\n",
"END_HOTKEY = all_w_pending[0][0]\n",
"print(f\"End hotkey: {END_HOTKEY}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ZERO_KEY = bittensor.u8_key_to_ss58([0]*32)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Find where swap happened\n",
"def find_swap(curr_hk: str) -> Optional[int]:\n",
" curr_block = END_BLOCK\n",
" while curr_block > START_BLOCK:\n",
" # Get last emission drain\n",
" last_emission_drain = sub.query_subtensor(\"LastHotkeyEmissionDrain\", curr_block, params=[curr_hk])\n",
" if not last_emission_drain:\n",
" print(f\"No last emission drain found at block {curr_block}\")\n",
" break\n",
"\n",
" # If the start hotkey, check for swap happened\n",
" zero_key_stake = sub.query_subtensor(\"Stake\", last_emission_drain.value - 1, params=[curr_hk, ZERO_KEY])\n",
" if zero_key_stake.value == 0: # Swap happened before this tempo ran\n",
" print(f\"Hotkey {curr_hk} swapped AFTER block {curr_block}\")\n",
" return last_emission_drain.value # This is first tempo after swap\n",
"\n",
" curr_block = last_emission_drain.value - 1\n",
" \n",
" return None"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"target_hk = START_HOTKEY\n",
"\n",
"swap_block = find_swap(target_hk)\n",
"if swap_block is None:\n",
" print(\"No swap found\")\n",
" exit(1)\n",
"\n",
"print(f\"Swap happened before block {swap_block}\")\n",
"\n",
"# Verify the swap had not yet occurred the block *before* the swap_block\n",
"null_stake_now = sub.query_subtensor(\"Stake\", swap_block - 1, params=[target_hk, ZERO_KEY])\n",
"if null_stake_now.value > 0:\n",
" print(f\"Swap already happened at block {swap_block - 1}\")\n",
" raise ValueError(\"Swap already happened\")\n",
"\n",
"print(f\"Swap not yet happened at block {swap_block - 1}\")\n",
"\n",
"target_block = swap_block\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
" \n",
"TO_EMIT = {}\n",
"EMISSIONS = {}\n",
"\n",
"# Get the pending emission\n",
"pending_emission = sub.query_subtensor(\"PendingdHotkeyEmission\", target_block - 1, params=[target_hk])\n",
"EMISSIONS[target_block] = pending_emission.value\n",
"if not pending_emission:\n",
" print(f\"No pending emission found at block {target_block}\")\n",
" raise Exception(\"No pending emission found\")\n",
"\n",
"TAKE = sub.query_subtensor(\"Delegates\", target_block - 1, params=[target_hk])\n",
"if not TAKE:\n",
" print(f\"No delegate found at block {target_block - 1}\")\n",
" raise Exception(\"No delegate found\")\n",
"\n",
"TAKE = TAKE.value / (2**16-1) # normalize to 1.0\n",
"\n",
"hk_take = pending_emission.value * TAKE\n",
"to_emit_from_pending = pending_emission.value - hk_take\n",
"\n",
"# Find start of this tempo\n",
"## Get the last emission drain\n",
"last_emission_drain = sub.query_subtensor(\"LastHotkeyEmissionDrain\", target_block - 1, params=[target_hk]).value\n",
"if last_emission_drain == 0:\n",
" last_emission_drain = START_BLOCK\n",
"\n",
"## Set the start of this tempo\n",
"start_of_tempo = last_emission_drain\n",
"\n",
"\n",
"# Get the stake map\n",
"stake_map = sub.query_map_subtensor(\"Stake\", start_of_tempo, params=[target_hk])\n",
"if not stake_map:\n",
" print(f\"No stake map found at block {start_of_tempo}\")\n",
" raise Exception(\"No stake map found\")\n",
"\n",
"stake_dict = {ck.value: st.value for ck, st in stake_map}\n",
"\n",
"stake_sum = sum(stake_dict.values())\n",
"TO_EMIT_THIS_TEMPO = {}\n",
"for ck, st in stake_dict.items():\n",
" TO_EMIT_THIS_TEMPO[ck] = 0\n",
" proportion = st / stake_sum\n",
" owed_emission = proportion * to_emit_from_pending\n",
" # Track for below assertion\n",
" TO_EMIT_THIS_TEMPO[ck] += owed_emission\n",
" \n",
" if ck not in TO_EMIT:\n",
" TO_EMIT[ck] = 0\n",
" TO_EMIT[ck] += owed_emission\n",
"\n",
"# Verify that the sum of the emissions is less than the total to emit\n",
"# Using an epsilon of 1_000 RAO\n",
"assert sum(TO_EMIT_THIS_TEMPO.values()) <= to_emit_from_pending + 1_000\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"stake_to_distribute = sum(TO_EMIT.values())\n",
"emissions_since = sum(EMISSIONS.values())\n",
"owner_key_em = TO_EMIT.get(OWNER_KEY)\n",
"vali_take = emissions_since - stake_to_distribute\n",
"\n",
"stake_to_distribute_no_owner = stake_to_distribute - owner_key_em\n",
"\n",
"null_stake_after = sub.query_subtensor(\"Stake\", target_block, params=[target_hk, ZERO_KEY])\n",
"if not null_stake_after:\n",
" print(f\"No null stake found at block {target_block}\")\n",
" raise Exception(\"No null stake found\")\n",
"\n",
"assert null_stake_after.value == emissions_since, f\"Expected {emissions_since}, got {null_stake_after.value}\"\n",
"\n",
"\n",
"print(f\"Total emissions earned: {emissions_since/1e9} TAO\")\n",
"print(f\"Stake on the null account: {null_stake_after.value/1e9} TAO\")\n",
"print(f\"Validator take: {vali_take/1e9} TAO -> {vali_take/emissions_since*100:.2f}%\")\n",
"print(f\"Earned by owner key: {owner_key_em/1e9} TAO\")\n",
"print(f\"Stake to distribute (minus owner key): {stake_to_distribute_no_owner/1e9} TAO\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Add the vali take to the owner key's emission\n",
"TO_EMIT[OWNER_KEY] += vali_take\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"to_emit_formatted = {\n",
" \"address\": [],\n",
" \"amount\": []\n",
"}\n",
"total_in_csv = 0\n",
"for coldkey, amount in TO_EMIT.items():\n",
" in_tao = round(amount/1e9, 9) # make into TAO, keep 9 decimal places\n",
" total_in_csv += in_tao\n",
" \n",
" if in_tao == 0:\n",
" continue # Skip coldkey if owed nothing\n",
" to_emit_formatted[\"address\"].append(coldkey)\n",
" to_emit_formatted[\"amount\"].append(in_tao) \n",
"print(f\"Total to emit recorded in CSV: {total_in_csv} TAO\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def to_csv(emission_map: Dict[str, int], filename: str):\n",
" df = pd.DataFrame.from_dict(emission_map)\n",
" df.to_csv(filename, index=False)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"to_csv(to_emit_formatted, \"emit_map_migration.csv\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def to_json(emission_map: Dict[str, int], filename: str):\n",
" with open(filename, \"w\") as f:\n",
" json.dump(emission_map, f)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"TO_EMIT_COPY = {k: int(round(v/1e9, 9)*1e9) for k, v in TO_EMIT.items() if v > 0}\n",
"\n",
"to_json(TO_EMIT_COPY, \"emit_map_migration.json\")\n",
"print(\"Done\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "finney",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.15"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Loading