Created
November 15, 2023 12:49
-
-
Save ydm/3892ff5b0384178ccd4a94a69337a201 to your computer and use it in GitHub Desktop.
Block attestations analysis
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "code", | |
| "execution_count": 330, | |
| "id": "fba2c098-3d8f-4fea-a6ea-7b4317b8fa0a", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "from functools import cache\n", | |
| "from typing import List, Optional, Sequence\n", | |
| "\n", | |
| "import pandas as pd\n", | |
| "import requests\n", | |
| "from eth_utils import to_bytes\n", | |
| "\n", | |
| "\n", | |
| "pd.set_option('display.max_rows', 512)\n", | |
| "\n", | |
| "\n", | |
| "TARGET = 7500800" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 331, | |
| "id": "c00a267a-09d0-4f58-a452-4e634e405433", | |
| "metadata": { | |
| "jupyter": { | |
| "source_hidden": true | |
| }, | |
| "tags": [] | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# +---------+\n", | |
| "# | BitList |\n", | |
| "# +---------+\n", | |
| "\n", | |
| "def bits_copy_length(a: bytearray, b: bytearray) -> bytearray:\n", | |
| " # Performs `a.len = b.len`, assumes `a` has no length set.\n", | |
| " return bits_set(a, bits_len(b))\n", | |
| "\n", | |
| "\n", | |
| "def bits_from_int(i: int) -> bytearray:\n", | |
| " return bytearray(reversed(to_bytes(i)))\n", | |
| "\n", | |
| "\n", | |
| "def bits_from_hex(s: str) -> bytearray:\n", | |
| " return bytearray(to_bytes(hexstr=s))\n", | |
| "\n", | |
| "\n", | |
| "def bits_get(bits: bytearray, index: int) -> bool:\n", | |
| " return bool(bits[index // 8] & (1 << (index % 8)))\n", | |
| "\n", | |
| "\n", | |
| "def bits_len(bits: bytearray) -> int:\n", | |
| " '''\n", | |
| " Returns the length of the bitmask in O(N) time.\n", | |
| " '''\n", | |
| " n = min(8 * len(bits), 2048)\n", | |
| " for index in range(n - 1, -1, -1):\n", | |
| " if bits_get(bits, index):\n", | |
| " return index\n", | |
| " raise ValueError()\n", | |
| "\n", | |
| "\n", | |
| "def bits_popcount(bits: bytearray) -> int:\n", | |
| " return sum(bits_to_list(bits))\n", | |
| "\n", | |
| "\n", | |
| "def bits_set(bits: bytearray, i: int) -> bytearray:\n", | |
| " copy = bytearray(bits)\n", | |
| " j = i // 8\n", | |
| " while len(copy) <= j:\n", | |
| " copy += b'\\x00'\n", | |
| " copy[j] |= 1 << (i % 8)\n", | |
| " return copy\n", | |
| "\n", | |
| "\n", | |
| "def bits_to_list(bits: bytearray) -> List[bool]:\n", | |
| " return [bits_get(bits, i) for i in range(bits_len(bits))]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 332, | |
| "id": "a690f1cf-966d-4af1-93ce-f7a49d5867d2", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "def get(x, *keys, cast=None):\n", | |
| " for k in keys:\n", | |
| " x = x[k]\n", | |
| " return cast(x) if cast is not None else x\n", | |
| "\n", | |
| "\n", | |
| "@cache\n", | |
| "def get_slot_by_hash(h):\n", | |
| " return get(\n", | |
| " requests.get(f'http://127.0.0.1:5052/eth/v1/beacon/headers/{h}').json(),\n", | |
| " 'data', 'header', 'message', 'slot',\n", | |
| " cast=int,\n", | |
| " )\n", | |
| "\n", | |
| "\n", | |
| "def popcount(s):\n", | |
| " return bits_popcount(bits_from_hex(s))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 446, | |
| "id": "8999714e-f9fa-46c7-b94b-9c8fb959cc8d", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "# Get all attestations.\n", | |
| "xs = []\n", | |
| "for block in range(TARGET-512, TARGET+512):\n", | |
| " body = requests.get(f'http://127.0.0.1:5052/eth/v2/beacon/blocks/{block}').json()\n", | |
| " if body.get('code') == 404:\n", | |
| " continue\n", | |
| " attestations = get(body, 'data', 'message', 'body', 'attestations')\n", | |
| " for a in attestations:\n", | |
| " slot = get(a, 'data', 'slot', cast=int)\n", | |
| " if slot < (TARGET-32) or (TARGET) <= slot:\n", | |
| " continue\n", | |
| " row = {\n", | |
| " 'inclusion': block,\n", | |
| " 'slot': slot,\n", | |
| " 'ghost': get_slot_by_hash(get(a, 'data', 'beacon_block_root')),\n", | |
| " 'source_epoch': get(a, 'data', 'source', 'epoch'),\n", | |
| " 'target_epoch': get(a, 'data', 'target', 'epoch'),\n", | |
| " 'source_slot': get_slot_by_hash(get(a, 'data', 'source', 'root')),\n", | |
| " 'target_slot': get_slot_by_hash(get(a, 'data', 'target', 'root')),\n", | |
| " 'votes': popcount(get(a, 'aggregation_bits'))\n", | |
| " }\n", | |
| " xs.append(row)\n", | |
| "xs = pd.DataFrame(xs)\n", | |
| "xs['inclusion_delta'] = xs['inclusion'].sub(xs['slot'])\n", | |
| "xs['ghost_delta'] = xs['slot'].sub(xs['ghost'])" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 447, | |
| "id": "2c0d4c61-32b6-497f-885d-ae604191fdf7", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "884741" | |
| ] | |
| }, | |
| "execution_count": 447, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "# Total number of votes.\n", | |
| "xs['votes'].sum()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 448, | |
| "id": "a541f054-b5dd-42ee-ad61-3adcd6bb2749", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "slot\n", | |
| "7500768 28205\n", | |
| "7500769 27741\n", | |
| "7500770 28175\n", | |
| "7500771 27974\n", | |
| "7500772 27782\n", | |
| "7500773 27270\n", | |
| "7500774 27829\n", | |
| "7500775 26505\n", | |
| "7500776 26895\n", | |
| "7500777 26716\n", | |
| "7500778 27694\n", | |
| "7500779 26461\n", | |
| "7500780 28012\n", | |
| "7500781 30369\n", | |
| "7500782 26931\n", | |
| "7500783 26513\n", | |
| "7500784 26499\n", | |
| "7500785 27329\n", | |
| "7500786 36309\n", | |
| "7500787 26907\n", | |
| "7500788 26512\n", | |
| "7500789 27021\n", | |
| "7500790 26492\n", | |
| "7500791 26493\n", | |
| "7500792 26862\n", | |
| "7500793 28581\n", | |
| "7500794 26512\n", | |
| "7500795 28716\n", | |
| "7500796 28611\n", | |
| "7500797 26499\n", | |
| "7500798 26508\n", | |
| "7500799 27818\n", | |
| "Name: votes, dtype: int64" | |
| ] | |
| }, | |
| "execution_count": 448, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "# Votes, aggregated in each slot.\n", | |
| "xs.groupby('slot')['votes'].sum()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 455, | |
| "id": "e3734bc1-a6b3-4949-9f5d-578749c4a5d3", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "inclusion_delta\n", | |
| "1 860346\n", | |
| "2 4380\n", | |
| "3 3880\n", | |
| "4 13208\n", | |
| "5 1112\n", | |
| "6 101\n", | |
| "7 1679\n", | |
| "8 1\n", | |
| "9 2\n", | |
| "10 24\n", | |
| "12 2\n", | |
| "13 2\n", | |
| "18 1\n", | |
| "20 2\n", | |
| "22 1\n", | |
| "Name: votes, dtype: int64" | |
| ] | |
| }, | |
| "execution_count": 455, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "xs.groupby('inclusion_delta')['votes'].sum().sort_index()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 454, | |
| "id": "b49f3849-d2ac-43e2-a5c4-acab69e8e8bb", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "ghost_delta\n", | |
| "0 862604\n", | |
| "1 21945\n", | |
| "2 124\n", | |
| "3 13\n", | |
| "4 6\n", | |
| "5 3\n", | |
| "6 3\n", | |
| "7 3\n", | |
| "8 5\n", | |
| "9 8\n", | |
| "10 5\n", | |
| "11 2\n", | |
| "12 1\n", | |
| "14 1\n", | |
| "15 1\n", | |
| "16 7\n", | |
| "17 3\n", | |
| "18 4\n", | |
| "19 1\n", | |
| "20 1\n", | |
| "21 1\n", | |
| "Name: votes, dtype: int64" | |
| ] | |
| }, | |
| "execution_count": 454, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "xs.groupby('ghost_delta')['votes'].sum().sort_index()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 456, | |
| "id": "dee90b8e-a032-4efd-809c-aecd55a3a3e2", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "# Easier to work with the standard case.\n", | |
| "xs = xs[xs['inclusion_delta'].eq(1)]\n", | |
| "xs = xs[xs['ghost_delta'].eq(0)]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 391, | |
| "id": "db48bbe0-7102-4603-9476-88d6c7a615e2", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "# xs = xs[['slot', 'inclusion']].drop_duplicates().set_index('slot')\n", | |
| "# xs = xs.pivot_table(\n", | |
| "# index='slot',\n", | |
| "# columns=xs.groupby('slot').cumcount(),\n", | |
| "# values='inclusion',\n", | |
| "# aggfunc='first',\n", | |
| "# )\n", | |
| "# xs.apply(lambda x: x.sub(x.index))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 462, | |
| "id": "592d71b4-5833-4af1-b67c-292e15552a3a", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "xs['epoch'] = xs['slot'].div(32).astype(int)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 467, | |
| "id": "7d8389e0-b400-4a53-941b-78c0ce90b07c", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "source_slot target_slot\n", | |
| "7500736 7500768 840175\n", | |
| "Name: votes, dtype: int64" | |
| ] | |
| }, | |
| "execution_count": 467, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "xs.groupby(['source_slot', 'target_slot'])['votes'].sum()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 471, | |
| "id": "7c069b66-7d5b-42d8-9f5a-07f7a7d62c8a", | |
| "metadata": {}, | |
| "outputs": [ | |
| { | |
| "data": { | |
| "text/plain": [ | |
| "slot\n", | |
| "7500768 26752\n", | |
| "7500769 27321\n", | |
| "7500770 28102\n", | |
| "7500771 26396\n", | |
| "7500772 26463\n", | |
| "7500773 26875\n", | |
| "7500774 26803\n", | |
| "7500775 26478\n", | |
| "7500776 25843\n", | |
| "7500777 26706\n", | |
| "7500778 26803\n", | |
| "7500779 26418\n", | |
| "7500780 27949\n", | |
| "7500781 10351\n", | |
| "7500782 26353\n", | |
| "7500783 26420\n", | |
| "7500784 26488\n", | |
| "7500785 26864\n", | |
| "7500786 31808\n", | |
| "7500787 26470\n", | |
| "7500788 26451\n", | |
| "7500789 27011\n", | |
| "7500790 26485\n", | |
| "7500791 26487\n", | |
| "7500792 26854\n", | |
| "7500793 22751\n", | |
| "7500794 26478\n", | |
| "7500795 26465\n", | |
| "7500796 26789\n", | |
| "7500797 26479\n", | |
| "7500798 26468\n", | |
| "7500799 27794\n", | |
| "Name: votes, dtype: int64" | |
| ] | |
| }, | |
| "execution_count": 471, | |
| "metadata": {}, | |
| "output_type": "execute_result" | |
| } | |
| ], | |
| "source": [ | |
| "xs.groupby('slot')['votes'].sum()" | |
| ] | |
| } | |
| ], | |
| "metadata": { | |
| "kernelspec": { | |
| "display_name": "Python 3 (ipykernel)", | |
| "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.10.10" | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 5 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment