logos-blockchain-pocs/cryptarchia/longest-chain-cryptarchia.ipynb

1258 lines
156 KiB
Plaintext

{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "ad657d5a-bd36-4329-b134-6745daff7ae9",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from dataclasses import dataclass, replace\n",
"from pyvis.network import Network\n",
"from pyvis.options import Layout\n",
"import time"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "a9e0b910-c633-4dbe-827c-4ddb804f7a9a",
"metadata": {},
"outputs": [],
"source": [
"def phi(f, alpha):\n",
" return 1 - (1-f)**alpha"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "aa0aadce-a0be-4873-ba23-293be74db313",
"metadata": {},
"outputs": [],
"source": [
"@dataclass\n",
"class Block:\n",
" id: int\n",
" slot: int\n",
" height: int\n",
" parent: int\n",
" leader: int"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "a538cf45-d551-4603-b484-dbbc3f3d0a73",
"metadata": {},
"outputs": [],
"source": [
"@dataclass\n",
"class NetworkParams:\n",
" broadcast_delay_mean: int # second\n",
" pol_proof_time: int # seconds\n",
" # ---- blend network -- \n",
" blending_delay: int\n",
" desimenation_delay_mean: float\n",
" # desimenation_delay_var: float\n",
" blend_hops: int\n",
" no_network_delay: bool = False\n",
"\n",
" def sample_blending_delay(self):\n",
" return np.random.uniform(0, self.blending_delay)\n",
"\n",
" def sample_desimenation_delay(self):\n",
" return np.random.exponential(self.desimenation_delay_mean)\n",
" # scale = self.desimenation_delay_var / self.desimenation_delay_mean\n",
" # shape = self.desimenation_delay_mean / scale\n",
" # return np.random.gamma(shape=shape, scale=scale)\n",
"\n",
" def sample_blend_network_delay(self):\n",
" return sum(self.sample_blending_delay() + self.sample_desimenation_delay() for _ in range(self.blend_hops))\n",
" \n",
" def sample_broadcast_delay(self, blocks):\n",
" return np.random.exponential(self.broadcast_delay_mean, size=blocks.shape)\n",
"\n",
" def block_arrival_slot(self, block_slot):\n",
" if self.no_network_delay:\n",
" return block_slot\n",
" # return self.pol_proof_time + self.sample_mixnet_delay() + self.sample_broadcast_delay(block_slot) + block_slot\n",
" return self.pol_proof_time + self.sample_blend_network_delay() + self.sample_broadcast_delay(block_slot) + block_slot\n",
"\n",
" def empirical_network_delay(self, N=10000, M=1000):\n",
" return np.array([self.block_arrival_slot(np.zeros(M)) for _ in range(N)]).reshape(N*M)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "17ef82f8-968c-48b0-bee7-f2642c8b3f3e",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"blend_net = NetworkParams(\n",
" broadcast_delay_mean=0.5,\n",
" pol_proof_time=1,\n",
" blending_delay=2,\n",
" desimenation_delay_mean=0.5,\n",
" blend_hops=3,\n",
")\n",
"no_blend_net = replace(blend_net, blend_hops=0)\n",
"\n",
"N = 100\n",
"M = 10000\n",
"no_blend_samples = no_blend_net.empirical_network_delay()\n",
"no_blend_mean = no_blend_samples.mean()\n",
"blend_samples = blend_net.empirical_network_delay()\n",
"blend_mean = blend_samples.mean()\n",
"\n",
"_ = plt.hist(no_blend_samples, bins=100, density=True, label=\"no-blend\")\n",
"_ = plt.hist(blend_samples, bins=100, density=True, label=\"blend\")\n",
"\n",
"for p in [50, 99, 99.9]:\n",
" no_blend_pct = np.percentile(no_blend_samples, p)\n",
" _ = plt.vlines(no_blend_pct, ymin=0, ymax=0.25, color='darkblue', label=f\"no-blend {p}p={no_blend_pct:.1f}s\")\n",
"\n",
"for p in [50, 99, 99.9]:\n",
" blend_pct = np.percentile(blend_samples, p)\n",
" _ = plt.vlines(blend_pct, ymin=0, ymax=0.25, color='brown', label=f\"blend {p}p={blend_pct:.1f}s\")\n",
"# _ = plt.vlines(blend_mean, ymin=0, ymax=1, color='brown', label=f\"blend 50p={blend_mean:.1f}s\")\n",
"# _ = plt.hist(blend_net.block_arrival_slot(np.zeros(1000)), bins=100, density=True, label=\"blend\")\n",
"_ = plt.legend()\n",
"_ = plt.xlabel(\"block delay\")"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "24779de7-284f-4200-9e4a-d2aa6e1b823b",
"metadata": {},
"outputs": [],
"source": [
"@dataclass\n",
"class Params:\n",
" SLOTS: int\n",
" f: float\n",
" honest_stake: np.array\n",
" adversary_control: float\n",
" total_stake_estimate: float\n",
"\n",
" @property\n",
" def N(self):\n",
" return len(self.honest_stake) + 1\n",
"\n",
" @property\n",
" def stake(self):\n",
" return np.append(self.honest_stake, self.honest_stake.sum() / (1/self.adversary_control - 1))\n",
" \n",
" @property\n",
" def relative_stake(self):\n",
" return self.stake / self.total_stake_estimate\n",
"\n",
" def slot_prob(self):\n",
" return phi(self.f, self.relative_stake)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "a90495a8-fcda-4e47-92b4-cc5ceaa9ff9c",
"metadata": {},
"outputs": [],
"source": [
"class Sim:\n",
" def __init__(self, params: Params, network: NetworkParams):\n",
" self.params = params\n",
" self.network = network\n",
" self.leaders = np.zeros((params.N, params.SLOTS), dtype=np.int64)\n",
" self.blocks = []\n",
" max_number_of_blocks = int(3 * params.SLOTS * params.f)\n",
" self.block_slots = np.zeros(max_number_of_blocks, dtype=np.int64)\n",
" self.block_heights = np.zeros(max_number_of_blocks, dtype=np.int64)\n",
" self.block_arrivals = np.zeros(shape=(params.N, max_number_of_blocks), dtype=np.int64) # arrival time to each leader for each block\n",
" self.block_arrivals[:,:] = self.params.SLOTS\n",
" # self.block_arrivals = np.zeros(shape=(params.N, 0), dtype=np.int64) # arrival time to each leader for each block\n",
"\n",
" \n",
" def emit_block(self, leader, slot, height, parent):\n",
" assert type(leader) in [int, np.int64]\n",
" assert type(slot) in [int, np.int64]\n",
" assert type(height) in [int, np.int64]\n",
" assert type(parent) in [int, np.int64]\n",
"\n",
" block = Block(\n",
" id=len(self.blocks),\n",
" slot=slot,\n",
" height=height,\n",
" parent=parent,\n",
" leader=leader,\n",
" )\n",
" self.blocks.append(block)\n",
" self.block_slots[block.id] = block.slot\n",
" self.block_heights[block.id] = block.height\n",
" \n",
" # decide when this block will arrive at each node\n",
" new_block_arrival_by_node = self.network.block_arrival_slot(np.repeat(block.slot, self.params.N))\n",
"\n",
" if parent != -1:\n",
" # the new block cannot arrive before it's parent\n",
" parent_arrival_by_node = self.block_arrivals[:,parent]\n",
" new_block_arrival_by_node = np.maximum(new_block_arrival_by_node, parent_arrival_by_node)\n",
"\n",
" self.block_arrivals[:,block.id] = new_block_arrival_by_node\n",
" # self.block_arrivals = np.append(self.block_arrivals, new_block_arrival_by_node.reshape((self.params.N, 1)), axis=1)\n",
"\n",
" return block.id\n",
"\n",
" def emit_leader_block(self, leader, slot):\n",
" assert type(leader) in [int, np.int64], type(leader)\n",
" assert isinstance(slot, int)\n",
"\n",
" parent = self.fork_choice(leader, slot)\n",
" return self.emit_block(\n",
" leader,\n",
" slot,\n",
" height=self.blocks[parent].height + 1,\n",
" parent=parent,\n",
" )\n",
"\n",
" def fork_choice(self, leader, slot):\n",
" assert type(leader) in [int, np.int64], type(leader)\n",
" assert isinstance(slot, int)\n",
" arrived_blocks = (self.block_arrivals[leader, :len(self.blocks)] <= slot) * self.block_heights[:len(self.blocks)]\n",
" concurrent = (arrived_blocks == np.max(arrived_blocks)).nonzero()[0]\n",
" return np.random.choice(concurrent)\n",
"\n",
" def plot_spacetime_diagram(self, MAX_SLOT=1000):\n",
" alpha_index = sorted(range(self.params.N), key=lambda n: self.params.relative_stake[n])\n",
" nodes = [f\"$N_{n}$($\\\\alpha$={self.params.relative_stake[n]:.2f})\" for n in alpha_index]\n",
" messages = [(nodes[alpha_index.index(self.blocks[b].leader)], nodes[alpha_index.index(node)], self.blocks[b].slot, arrival_slot, f\"$B_{{{b}}}$\") for b, arrival_slots in enumerate(self.block_arrivals[:,:len(self.blocks)].T) for node, arrival_slot in enumerate(arrival_slots) if arrival_slot < MAX_SLOT]\n",
" \n",
" fig, ax = plt.subplots(figsize=(8,4))\n",
" \n",
" # Plot vertical lines for each node\n",
" max_slot = max(s for _,_,start_t, end_t,_ in messages for s in [start_t, end_t])\n",
" for i, node in enumerate(nodes):\n",
" ax.plot([i, i], [0, max_slot], 'k-', linewidth=0.1)\n",
" ax.text(i, max_slot + 30 * (0 if i % 2 == 0 else 1), node, ha='center', va='bottom')\n",
" \n",
" # Plot messages\n",
" colors = plt.cm.rainbow(np.linspace(0, 1, len(messages)))\n",
" for (start, end, start_time, end_time, label), color in zip(messages, colors):\n",
" start_idx = nodes.index(start)\n",
" end_idx = nodes.index(end)\n",
" ax.annotate('', xy=(end_idx, end_time), xytext=(start_idx, start_time),\n",
" arrowprops=dict(arrowstyle='->', color=\"black\", lw=0.5))\n",
" placement = 0\n",
" mid_x = start_idx * (1 - placement) + end_idx * placement\n",
" mid_y = start_time * (1 - placement) + end_time * placement\n",
" ax.text(mid_x, mid_y, label, ha='center', va='center', \n",
" bbox=dict(facecolor='white', edgecolor='none', alpha=0.7))\n",
" \n",
" ax.set_xlim(-1, len(nodes))\n",
" ax.set_ylim(0, max_slot + 70)\n",
" ax.set_xticks(range(len(nodes)))\n",
" ax.set_xticklabels([])\n",
" # ax.set_yticks([])\n",
" ax.set_title('Space-Time Diagram')\n",
" ax.set_ylabel('Slot')\n",
" \n",
" plt.tight_layout()\n",
" plt.show()\n",
"\n",
" def honest_chain(self):\n",
" chain_head = max(self.blocks, key=lambda b: b.height)\n",
" honest_chain = {chain_head.id}\n",
" \n",
" curr_block = chain_head\n",
" while curr_block.parent >= 0:\n",
" honest_chain.add(curr_block.parent)\n",
" curr_block = self.blocks[curr_block.parent]\n",
" return sorted(honest_chain, key=lambda b: self.blocks[b].height)\n",
"\n",
" def visualize_chain(self):\n",
" honest_chain = self.honest_chain()\n",
" print(\"Honest chain length\", len(honest_chain))\n",
" honest_chain_set = set(honest_chain)\n",
" \n",
" layout = Layout()\n",
" layout.hierachical = True\n",
" \n",
" G = Network(width=1600, height=800, notebook=True, directed=True, layout=layout, cdn_resources='in_line')\n",
"\n",
" for block in self.blocks:\n",
" # level = slot\n",
" level = block.height\n",
" color = \"lightgrey\"\n",
" if block.id in honest_chain_set:\n",
" color = \"orange\"\n",
"\n",
" G.add_node(int(block.id), level=level, color=color, label=f\"{block.id},{block.slot}\")\n",
" if block.parent >= 0:\n",
" G.add_edge(int(block.id), int(block.parent), width=2, color=color)\n",
" \n",
" return G.show(\"chain.html\")\n",
"\n",
" def run(self, seed=None):\n",
" from collections import defaultdict\n",
" timings = defaultdict(float)\n",
" start_t = time.time()\n",
" if seed is not None:\n",
" np.random.seed(seed)\n",
"\n",
" # emit the genesis block\n",
" self.emit_block(\n",
" leader=0,\n",
" slot=0,\n",
" height=1,\n",
" parent=-1,\n",
" )\n",
" self.block_arrivals[:,0] = 0 # all nodes see the genesis block\n",
"\n",
" prep_t = time.time()\n",
"\n",
"\n",
" for s in range(1, self.params.SLOTS):\n",
" slot_start_t = time.time()\n",
" # the adversary will not participate in the simulation\n",
" # (implemented by never delivering blocks to the adversary)\n",
" # self.block_arrivals[-1,:] = self.params.SLOTS\n",
"\n",
" self.leaders[:,s] = np.random.random(size=self.params.N) < self.params.slot_prob()\n",
" leader_lottery_t = time.time()\n",
"\n",
" for leader in np.nonzero(self.leaders[:,s])[0]:\n",
" lead_start_t = time.time()\n",
" if self.params.adversary_control is not None and leader == self.params.N - 1:\n",
" continue\n",
" \n",
" parent = self.fork_choice(leader, s)\n",
" fork_choice_t = time.time()\n",
" \n",
" self.emit_block(\n",
" leader,\n",
" s,\n",
" height=self.blocks[parent].height + 1,\n",
" parent=parent,\n",
" )\n",
" emit_leader_block_t = time.time()\n",
"\n",
" timings[\"forkchoice\"] += fork_choice_t - lead_start_t\n",
" timings[\"emit_leader_block\"] += emit_leader_block_t - fork_choice_t\n",
" \n",
" # self.emit_leader_block(leader, s)\n",
" slot_end_t = time.time()\n",
" timings[\"leader\"] += leader_lottery_t - slot_start_t\n",
" timings[\"emit\"] += slot_end_t - leader_lottery_t\n",
" timings[\"slot\"] += slot_end_t - slot_start_t\n",
"\n",
" end_t = time.time()\n",
" timings[\"prep\"] = prep_t - start_t\n",
" timings[\"total\"] = end_t - start_t\n",
" for phase, duration in timings.items():\n",
" print(f\"{phase}\\t{duration:.2f}s\")\n",
"\n",
" def adverserial_analysis(self, should_plot=False, seed=0, k=2160):\n",
" from collections import defaultdict\n",
"\n",
" timings = defaultdict(float)\n",
"\n",
" start_t = time.time()\n",
" np.random.seed(seed)\n",
" \n",
" adversary = self.params.N-1 # adversary is always the last node in our simulations\n",
"\n",
" self.block_arrivals[adversary,:len(self.blocks)] = self.block_slots[:len(self.blocks)] # we will say the adversary receives the blocks immidiately\n",
"\n",
" honest_chain = self.honest_chain()\n",
" \n",
" honest_chain_t = time.time()\n",
" \n",
" honest_height_by_slot = np.zeros(self.params.SLOTS, dtype=np.int64)\n",
"\n",
" for block_id in honest_chain:\n",
" honest_height_by_slot[self.blocks[block_id].slot] = 1\n",
" honest_height_by_slot = honest_height_by_slot.cumsum()\n",
" \n",
" honest_height_by_slot_t = time.time()\n",
" \n",
" reorg_depths = []\n",
" if should_plot:\n",
" plt.figure(figsize=(20, 6))\n",
" ax = plt.subplot(121)\n",
" advantage = np.zeros(self.params.SLOTS)\n",
" \n",
" adversary_active_slots = np.random.random(size=self.params.SLOTS) < phi(self.params.f, self.params.relative_stake[adversary])\n",
" adversary_cumsum = adversary_active_slots.cumsum()\n",
"\n",
" all_active_slots = (self.leaders.sum(axis=0) + adversary_active_slots) > 0\n",
" slot_lookahead = int(3 * k / self.params.f)\n",
" \n",
" prep_t = time.time()\n",
" timings[\"honest_chain\"] += honest_chain_t - start_t\n",
" timings[\"honest_height_by_slot\"] += honest_height_by_slot_t - honest_chain_t\n",
" timings[\"prep_analysis\"] += prep_t - start_t\n",
" for b in range(len(self.blocks)):\n",
" block_start_t = time.time()\n",
" block = self.blocks[b]\n",
" if block.id > 0 and block.id % 5000 == 0:\n",
" print(\"Processing block\", block)\n",
" \n",
" nearest_honest_block = block\n",
" while nearest_honest_block.height >= len(honest_chain) or honest_chain[nearest_honest_block.height-1] != nearest_honest_block.id:\n",
" nearest_honest_block = self.blocks[nearest_honest_block.parent]\n",
"\n",
" nearest_honest_t = time.time()\n",
" \n",
" cumulative_rel_height = adversary_cumsum[block.slot+1:block.slot+1 + slot_lookahead] - adversary_cumsum[block.slot]\n",
"\n",
" adverserial_height_by_slot = block.height + cumulative_rel_height\n",
"\n",
" honest_height_by_slot_lookahead = honest_height_by_slot[block.slot + 1:block.slot+1 + slot_lookahead]\n",
" \n",
" adverserial_wins = adverserial_height_by_slot > honest_height_by_slot_lookahead\n",
" \n",
" reorg_events = adverserial_wins & all_active_slots[block.slot+1:block.slot+1 + slot_lookahead]\n",
"\n",
" \n",
" reorg_events_t = time.time()\n",
" reorg_depth = honest_height_by_slot_lookahead[reorg_events] - nearest_honest_block.height\n",
" reorg_depth_t = time.time()\n",
" reorg_depths += list(reorg_depth)\n",
" block_end_t = time.time()\n",
" timings[\"nearest_honest\"] += nearest_honest_t - block_start_t\n",
" timings[\"reorg_events\"] += reorg_events_t - nearest_honest_t\n",
" timings[\"reorg_depth\"] += reorg_depth_t - reorg_events_t\n",
" timings[\"depth_append\"] += block_end_t - reorg_depth_t\n",
" \n",
" if should_plot:\n",
" if reorg_events.sum() > 0:\n",
" first_slot = block.slot+1\n",
" last_slot = first_slot + np.nonzero(reorg_events)[0].max() + 1\n",
" advantage[first_slot:last_slot] = np.maximum(advantage[first_slot:last_slot], adverserial_height_by_slot[:last_slot-first_slot]-honest_height_by_slot[first_slot:last_slot])\n",
"\n",
" for phase, duration in timings.items():\n",
" print(f\"{phase}\\t{duration:.2f}s\")\n",
" \n",
" if should_plot:\n",
" ax.plot(advantage, color='k', lw=\"0.5\")\n",
" _ = ax.set_title(f\"max chain weight with adversery controlling {self.params.relative_stake[adversary] * 100:.0f}% of stake\")\n",
" _ = ax.set_ylabel(\"adversary height advantage\")\n",
" _ = ax.set_xlabel(\"slot\")\n",
" _ = ax.legend()\n",
"\n",
" ax = plt.subplot(122)\n",
" _ = ax.grid(True)\n",
" _ = ax.hist(reorg_depths, density=False, bins=100)\n",
" _ = ax.set_title(f\"re-org depth with {self.params.relative_stake[adversary] * 100:.0f}% adversary\")\n",
" _ = ax.set_xlabel(\"re-org depth\")\n",
" _ = ax.set_ylabel(\"frequency\")\n",
" return reorg_depths"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "5f2da916-30a0-4b1d-886f-1bc81c17056e",
"metadata": {},
"outputs": [
{
"ename": "TypeError",
"evalue": "Params.__init__() missing 1 required positional argument: 'total_stake_estimate'",
"output_type": "error",
"traceback": [
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
"\u001b[31mTypeError\u001b[39m Traceback (most recent call last)",
"\u001b[36mFile \u001b[39m\u001b[32m<timed exec>:3\u001b[39m\n",
"\u001b[31mTypeError\u001b[39m: Params.__init__() missing 1 required positional argument: 'total_stake_estimate'"
]
}
],
"source": [
"%%time\n",
"np.random.seed(0)\n",
"sim = Sim(\n",
" params=Params(\n",
" SLOTS=1000,\n",
" f=1/5,\n",
" adversary_control = 0.3,\n",
" honest_stake = np.random.pareto(10, 1000)\n",
" ),\n",
" network=blend_net\n",
")\n",
"sim.run(seed=0)\n",
"\n",
"n_blocks_per_slot = len(sim.blocks) / sim.params.SLOTS\n",
"print(\"avg blocks per slot\", n_blocks_per_slot)\n",
"print(\"Number of blocks\", len(sim.blocks))\n",
"print(\"longest chain\", max(b.height for b in sim.blocks))"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "e2a825cf-f915-43ee-8d31-0cb09947c7dc",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"leader\t11.35s\n",
"emit\t2.57s\n",
"slot\t13.93s\n",
"forkchoice\t0.63s\n",
"emit_leader_block\t0.71s\n",
"prep\t0.00s\n",
"total\t14.07s\n",
"leader\t11.50s\n",
"emit\t2.76s\n",
"slot\t14.26s\n",
"forkchoice\t0.76s\n",
"emit_leader_block\t0.75s\n",
"prep\t0.00s\n",
"total\t14.41s\n",
"leader\t11.27s\n",
"emit\t2.70s\n",
"slot\t13.97s\n",
"forkchoice\t0.74s\n",
"emit_leader_block\t0.73s\n",
"prep\t0.00s\n",
"total\t14.12s\n",
"leader\t11.09s\n",
"emit\t2.69s\n",
"slot\t13.79s\n",
"forkchoice\t0.76s\n",
"emit_leader_block\t0.73s\n",
"prep\t0.00s\n",
"total\t13.93s\n",
"leader\t11.19s\n",
"emit\t2.68s\n",
"slot\t13.87s\n",
"forkchoice\t0.75s\n",
"emit_leader_block\t0.72s\n",
"prep\t0.00s\n",
"total\t14.01s\n",
"leader\t11.26s\n",
"emit\t2.71s\n",
"slot\t13.96s\n",
"forkchoice\t0.75s\n",
"emit_leader_block\t0.74s\n",
"prep\t0.00s\n",
"total\t14.10s\n",
"leader\t11.15s\n",
"emit\t2.66s\n",
"slot\t13.81s\n",
"forkchoice\t0.74s\n",
"emit_leader_block\t0.71s\n",
"prep\t0.00s\n",
"total\t13.95s\n",
"leader\t11.09s\n",
"emit\t2.70s\n",
"slot\t13.78s\n",
"forkchoice\t0.77s\n",
"emit_leader_block\t0.73s\n",
"prep\t0.00s\n",
"total\t13.92s\n",
"leader\t11.05s\n",
"emit\t2.62s\n",
"slot\t13.67s\n",
"forkchoice\t0.73s\n",
"emit_leader_block\t0.71s\n",
"prep\t0.00s\n",
"total\t13.81s\n",
"leader\t10.86s\n",
"emit\t2.57s\n",
"slot\t13.43s\n",
"forkchoice\t0.71s\n",
"emit_leader_block\t0.70s\n",
"prep\t0.00s\n",
"total\t13.57s\n"
]
}
],
"source": [
"stake_real = np.random.pareto(10, 1000)\n",
"stake_estimate = stake_real.sum()\n",
"\n",
"f_value = 1/30\n",
"beta = 0.8\n",
"slot_size = int(21600 / f_value)\n",
"\n",
"ratios_sims = []\n",
"prev_epoch_honest_blocks = 0\n",
"\n",
"for j in range(10):\n",
" sim_chain_growth_fixed = Sim(\n",
" params=Params(\n",
" SLOTS= slot_size,\n",
" f=f_value,\n",
" adversary_control = 10 ** -9,\n",
" honest_stake = stake_real,\n",
" total_stake_estimate = stake_estimate\n",
" ),\n",
" network=blend_net\n",
" )\n",
" sim_chain_growth_fixed.run()\n",
"\n",
" honest = sim_chain_growth_fixed.honest_chain()\n",
" honest_slots = np.array([sim_chain_growth_fixed.blocks[x].slot for x in honest])\n",
" \n",
" positions = np.searchsorted(honest_slots, np.arange(slot_size), side='right') + prev_epoch_honest_blocks\n",
" ratios_slots = positions / (np.arange(slot_size) + 1 + (slot_size * j))\n",
" \n",
" ratios_sims.append(ratios_slots)\n",
" \n",
" error = f_value - (len(honest) / slot_size)\n",
" h = beta * (stake_estimate / f_value)\n",
" stake_estimate = stake_estimate - h * error\n",
"\n",
" prev_epoch_honest_blocks += len(honest)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "67861700-6d03-4bd1-8120-a3452f29b098",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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DQAotT/vuEREREQ6DUkDBw5EVlYWIiIikJ6ejqZNmyI+Ph4uLi4AgPT0dKW1i1xdXREfH48JEyZg6dKlcHR0xOLFi9GvXz+tHpeIiIiIjyV5Qbm5ubC2tkZOTo5W5w5tOXETY9Yeh5+bHdZ+/KbW9ktERETS/n7r/LEkRERERJURQxIRERGRGgxJRERERGowJBERERGpwZBEREREpAZDEhEREZEaDElEREREajAkEREREanBkERERESkBkMSERERkRoMSURERERqMCQRERERqcGQRERERKQGQxIRERGRGgxJRERERGowJBERERGpwZBEREREpAZDEhEREZEaDElEREREajAkEREREanBkERERESkBkMSERERkRoMSURERERqMCQRERERqcGQRERERKQGQxIRERGRGgxJRERERGowJBERERGpwZBEREREpAZDEhEREZEaDElEREREajAkEREREanBkERERESkBkMSERERkRoMSURERERqMCQRERERqcGQRERERKQGQxIRERGRGgxJRERERGowJBERERGpwZBEREREpAZDEhEREZEaDElEREREakgOSWlpaRBCqLQLIZCWlqaVooiIiIh0TXJIcnV1xe3bt1Xas7Oz4erqqpWiiIiIiHRNckgSQkAmk6m05+XlwcTERCtFEREREemagaYdQ0NDAQAymQxTp06FmZmZYptcLsfff/8NLy8vrRdIREREpAsah6Tjx48DeDKSdOrUKRgZGSm2GRkZwdPTE5999pn2KyQiIiLSAY1D0u7duwEAwcHB+Pbbb2FlZVVuRRERERHpmsYhqcSqVasU31+/fh0ymQxOTk5aLYqIiIhI1yRP3C4uLkZERASsra3h4uICZ2dn2NjYYObMmSguLi6PGomIiIgqnOSRpLCwMMTGxmLu3Lnw9/eHEAIHDhzAjBkz8PjxY8yePbs86iQiIiKqUJJD0urVq7FixQq8/fbbijZPT084OTlh5MiRDElERERUJUi+3JadnQ0PDw+Vdg8PD2RnZ2ulKCIiIiJdkxySPD09sWTJEpX2JUuWwNPTU3IBUVFRcHV1hYmJCXx8fLBv374y+yclJcHHxwcmJiZwc3NDdHS00vaNGzfC19cXNjY2MDc3h5eXF3744QelPkVFRZgyZQpcXV1hamoKNzc3REREcE4VERERKUi+3DZ//nx0794dO3fu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btKECRPw+eefa71AIiIiIl2QPCdpwoQJiu87dOiA8+fP48iRI6hXrx48PT21WhwRERGRrkgaSSosLESHDh1w8eJFRZuzszP69u3LgERERERViqSQZGhoiNOnT6t9wC0RERFRVSJ5TtKQIUMQGxtbHrUQERERVRqS5yQVFBRgxYoVSEhIgK+vL8zNzZW2L1iwQGvFEREREemK5JB0+vRpeHt7A4DS3CQAvAxHREREVYbkkLR79+7yqIOIiIioUpE8J4mIiIjodcCQRERERKQGQxIRERGRGgxJRERERGowJBERERGpIfnuNuDJrf979uxBZmYmiouLlbZNmzZNK4URERER6ZLkkPTdd9/h008/RfXq1eHg4KC0NpJMJmNIIiIioipBckiaNWsWZs+ejYkTJ5ZHPURERESVguQ5SXfv3kX//v3LoxYiIiKiSkNySOrfvz927NhRHrUQERERVRoaXW5bvHix4nt3d3dMnToVf/31F5o1awZDQ0OlvmPHjtVuhUREREQ6oFFIWrhwodJrCwsLJCUlISkpSaldJpMxJBEREVGVoFFISk1NLe86iIiIiCoVyXOSIiIi8PDhQ5X2R48eISIiQitFEREREema5JAUHh6OvLw8lfaHDx8iPDxcK0URERER6ZrkkCSEUFpAssSJEydga2urlaKIiIiIdE3jxSSrVasGmUwGmUyGBg0aKAUluVyOvLw8hISElEuRRERERBVN45GkRYsWYcGCBRBCIDw8HAsXLlR8RUdHY//+/Vi6dKnkAqKiouDq6goTExP4+Phg3759ZfZPSkqCj48PTExM4ObmhujoaKXtGzduhK+vL2xsbGBubg4vLy/88MMPpe4vMjISMpkM48ePl1w7ERERVV0ajyQNHToUAODq6gp/f38YGLzQs3GVrF+/HuPHj0dUVBT8/f2xfPlydO3aFWfPnoWzs7NK/9TUVHTr1g0jRozAmjVrcODAAYwcORI1atRAv379AAC2trYICwuDh4cHjIyMsHXrVgQHB6NmzZoICgpS2t/hw4cRExOD5s2bv/S5EBERUdUieU5STEwMVq1ahYsXL770wRcsWIAPP/wQH330ERo1aoRFixahTp06WLZsmdr+0dHRcHZ2xqJFi9CoUSN89NFHGD58OL7++mtFn/bt26NPnz5o1KgR6tWrh3HjxqF58+bYv3+/0r7y8vIwaNAgfPfdd6hWrdpLnwsRERFVLZJDkqWlJb755ht4eHjA0dER7733HqKjo3H+/HlJ+ykoKMDRo0cRGBio1B4YGIiDBw+qfU9ycrJK/6CgIBw5cgSFhYUq/YUQSExMxIULF9CuXTulbaNGjUL37t3RqVMnjerNz89Hbm6u0hcRERFVXZKvmZXMAcrIyMCePXuwZ88efPvttxg1ahRq1qyJ9PR0jfZz584dyOVy2NvbK7Xb29sjIyND7XsyMjLU9i8qKsKdO3dQq1YtAEBOTg6cnJyQn58PfX19REVFoXPnzor3rFu3DseOHcPhw4c1Pu/IyEgucUBERPQaeeGJRZaWlqhWrRqqVasGGxsbGBgYwMHBQfJ+nl1OoLQlBsrq/2y7paUlUlJSkJeXh8TERISGhsLNzQ3t27fHtWvXMG7cOOzYsQMmJiYa1zl58mSEhoYqXufm5qJOnToav5+IiIheLZJD0sSJE5GUlIQTJ06gadOmaNeuHSZPnox27drBxsZG4/1Ur14d+vr6KqNGmZmZKqNFJRwcHNT2NzAwgJ2dnaJNT08P7u7uAAAvLy+cO3cOkZGRaN++PY4ePYrMzEz4+Pgo+svlcuzduxdLlixRjD49y9jYGMbGxhqfHxEREb3aJIekr776CjVq1MD06dPRq1cvNGrU6IUObGRkBB8fHyQkJKBPnz6K9oSEBPTq1Uvte/z8/LBlyxalth07dsDX1xeGhoalHksIgfz8fABAx44dcerUKaXtwcHB8PDwwMSJE9UGJCIiInr9SA5Jx48fR1JSEvbs2YNvvvkG+vr6CAgIQPv27dG+fXtJoSk0NBSDBw+Gr68v/Pz8EBMTg7S0NMWilJMnT8aNGzcQFxcHAAgJCcGSJUsQGhqKESNGIDk5GbGxsVi7dq1in5GRkfD19UW9evVQUFCA+Ph4xMXFKe6Ys7S0RNOmTZXqMDc3h52dnUo7ERERvb4khyRPT094enpi7NixAJ48jmTRokUYO3YsiouLIZfLNd7XwIEDkZWVhYiICKSnp6Np06aIj4+Hi4sLACA9PR1paWmK/q6uroiPj8eECROwdOlSODo6YvHixYo1kgDgwYMHGDlyJK5fvw5TU1N4eHhgzZo1GDhwoNRTJSIioteYTJTMfJbg+PHjijvb9u3bh9zcXHh5eaFDhw746quvyqPOSic3NxfW1tbIycmBlZWV1va75cRNjFl7HH5udlj78Zta2y8RERFJ+/steSSpWrVqyMvLg6enJ9q3b48RI0agXbt2Wg0KRERERLomOST98MMPDEVERERU5UkOST169FB8f/36dchkMjg5OWm1KCIiIiJdk/xYkuLiYkRERMDa2houLi5wdnaGjY0NZs6cieLi4vKokYiIiKjCSR5JCgsLQ2xsLObOnQt/f38IIXDgwAHMmDEDjx8/xuzZs8ujTiIiIqIKJTkkrV69GitWrMDbb7+taPP09ISTkxNGjhzJkERERERVguTLbdnZ2fDw8FBp9/DwQHZ2tlaKIiIiItI1ySHJ09MTS5YsUWlfsmQJPD09tVIUERERka5Jvtw2f/58dO/eHTt37oSfnx9kMhkOHjyIa9euIT4+vjxqJCIiIqpwkkeSAgICcPHiRfTp0wf37t1DdnY2+vbtiwsXLqBt27blUSMRERFRhZM0klRYWIjAwEAsX76cE7SJiIioSpM0kmRoaIjTp09DJpOVVz1ERERElYLky21DhgxBbGxsedRCREREVGlInrhdUFCAFStWICEhAb6+vjA3N1favmDBAq0V9zoyeZiOIL1DaPjIEjibqetyiIiIdMfKCajto7PDSw5Jp0+fhre3NwDg4sWLStt4Ge7l2WYfw3KjRcBdAD/ruhoiIiIdavoO8I7url5JDkm7d+8ujzro/xUYVcPh4gawNDGEh72lrsshIiLSHTt3nR5eckii8nWnZmuMKZgBv9p2WPvhm7ouh4iI6LUlOST16dNH7WU1mUwGExMTuLu74/3330fDhg21UiARERGRLki+u83a2hq7du3CsWPHFGHp+PHj2LVrF4qKirB+/Xp4enriwIEDWi+WiIiIqKJIHklycHDA+++/jyVLlkBP70nGKi4uxrhx42BpaYl169YhJCQEEydOxP79+7VeMBEREVFFkDySFBsbi/HjxysCEgDo6elhzJgxiImJgUwmw+jRo3H69GmtFkpERERUkSSHpKKiIpw/f16l/fz585DL5QAAExMTLgdARERErzTJl9sGDx6MDz/8EF9++SXeeOMNyGQyHDp0CHPmzMGQIUMAAElJSWjSpInWiyUiIiKqKJJD0sKFC2Fvb4/58+fj1q1bAAB7e3tMmDABEydOBAAEBgaiS5cu2q2UiIiIqAJJDkn6+voICwtDWFgYcnNzAQBW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",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"slots_list = np.array(range(slot_size * 10))\n",
"\n",
"plt.plot(slots_list, np.array(ratios_sims).ravel())\n",
"plt.plot(slots_list, np.full(slot_size * 10, f_value))\n",
"plt.xlabel(\"Slot\")\n",
"#plt.yscale('log')\n",
"plt.ylim(1/35, 1/25)\n",
"plt.ylabel(\"Honest chain growth rate until this slot\")\n",
"plt.title(\"Honest chain growth rate convergence\")\n",
" \n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "574faa19-6dde-4da6-a71e-4a59291b6e0d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.14560000000000003\n",
"0.11371657754010694\n",
"0.08065902578796559\n",
"0.06032778864970646\n",
"0.0460066864784546\n"
]
}
],
"source": [
"for i in range(5):\n",
" print(abs(ratios_sims[i][99999] - (1/30)) / (1/30))"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "def10743-5c85-4c77-9d36-64cdac0d6839",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"leader\t1.43s\n",
"emit\t5.49s\n",
"slot\t6.93s\n",
"forkchoice\t3.48s\n",
"emit_leader_block\t1.73s\n",
"prep\t0.00s\n",
"total\t6.95s\n",
"HONEST RATIO: 0.16928\n",
"ACTIVE RATIO: 0.16928\n",
"F RATIO: 0.5\n",
"leader\t1.81s\n",
"emit\t1.87s\n",
"slot\t3.69s\n",
"forkchoice\t0.82s\n",
"emit_leader_block\t0.82s\n",
"prep\t0.00s\n",
"total\t3.71s\n",
"HONEST RATIO: 0.12164\n",
"ACTIVE RATIO: 0.12164\n",
"F RATIO: 0.25\n",
"leader\t1.81s\n",
"emit\t0.87s\n",
"slot\t2.68s\n",
"forkchoice\t0.29s\n",
"emit_leader_block\t0.36s\n",
"prep\t0.00s\n",
"total\t2.70s\n",
"HONEST RATIO: 0.08022\n",
"ACTIVE RATIO: 0.08022\n",
"F RATIO: 0.125\n",
"leader\t1.78s\n",
"emit\t0.49s\n",
"slot\t2.26s\n",
"forkchoice\t0.11s\n",
"emit_leader_block\t0.18s\n",
"prep\t0.00s\n",
"total\t2.29s\n",
"HONEST RATIO: 0.04872\n",
"ACTIVE RATIO: 0.04872\n",
"F RATIO: 0.0625\n",
"leader\t1.85s\n",
"emit\t0.36s\n",
"slot\t2.20s\n",
"forkchoice\t0.05s\n",
"emit_leader_block\t0.09s\n",
"prep\t0.00s\n",
"total\t2.23s\n",
"HONEST RATIO: 0.02687\n",
"ACTIVE RATIO: 0.02687\n",
"F RATIO: 0.03125\n",
"leader\t1.89s\n",
"emit\t0.31s\n",
"slot\t2.21s\n",
"forkchoice\t0.04s\n",
"emit_leader_block\t0.06s\n",
"prep\t0.00s\n",
"total\t2.23s\n",
"HONEST RATIO: 0.01458\n",
"ACTIVE RATIO: 0.01458\n",
"F RATIO: 0.015625\n",
"leader\t1.80s\n",
"emit\t0.25s\n",
"slot\t2.05s\n",
"forkchoice\t0.02s\n",
"emit_leader_block\t0.03s\n",
"prep\t0.00s\n",
"total\t2.08s\n",
"HONEST RATIO: 0.00778\n",
"ACTIVE RATIO: 0.00778\n",
"F RATIO: 0.0078125\n",
"leader\t1.70s\n",
"emit\t0.21s\n",
"slot\t1.91s\n",
"forkchoice\t0.01s\n",
"emit_leader_block\t0.01s\n",
"prep\t0.00s\n",
"total\t1.93s\n",
"HONEST RATIO: 0.00394\n",
"ACTIVE RATIO: 0.00394\n",
"F RATIO: 0.00390625\n",
"leader\t1.65s\n",
"emit\t0.19s\n",
"slot\t1.84s\n",
"forkchoice\t0.00s\n",
"emit_leader_block\t0.01s\n",
"prep\t0.00s\n",
"total\t1.86s\n",
"HONEST RATIO: 0.00209\n",
"ACTIVE RATIO: 0.00209\n",
"F RATIO: 0.001953125\n",
"leader\t1.69s\n",
"emit\t0.19s\n",
"slot\t1.88s\n",
"forkchoice\t0.00s\n",
"emit_leader_block\t0.00s\n",
"prep\t0.00s\n",
"total\t1.90s\n",
"HONEST RATIO: 0.00086\n",
"ACTIVE RATIO: 0.00086\n",
"F RATIO: 0.0009765625\n",
"leader\t1.71s\n",
"emit\t0.20s\n",
"slot\t1.90s\n",
"forkchoice\t0.00s\n",
"emit_leader_block\t0.00s\n",
"prep\t0.00s\n",
"total\t1.92s\n",
"HONEST RATIO: 0.00052\n",
"ACTIVE RATIO: 0.00052\n",
"F RATIO: 0.00048828125\n",
"leader\t1.68s\n",
"emit\t0.19s\n",
"slot\t1.86s\n",
"forkchoice\t0.00s\n",
"emit_leader_block\t0.00s\n",
"prep\t0.00s\n",
"total\t1.89s\n",
"HONEST RATIO: 0.0002\n",
"ACTIVE RATIO: 0.0002\n",
"F RATIO: 0.000244140625\n",
"leader\t1.63s\n",
"emit\t0.18s\n",
"slot\t1.81s\n",
"forkchoice\t0.00s\n",
"emit_leader_block\t0.00s\n",
"prep\t0.00s\n",
"total\t1.83s\n",
"HONEST RATIO: 0.0001\n",
"ACTIVE RATIO: 0.0001\n",
"F RATIO: 0.0001220703125\n",
"leader\t1.64s\n",
"emit\t0.18s\n",
"slot\t1.82s\n",
"forkchoice\t0.00s\n",
"emit_leader_block\t0.00s\n",
"prep\t0.00s\n",
"total\t1.84s\n",
"HONEST RATIO: 9e-05\n",
"ACTIVE RATIO: 9e-05\n",
"F RATIO: 6.103515625e-05\n"
]
}
],
"source": [
"f_values = [1 / (2**i) for i in range(1, 15)]\n",
"ratios = []\n",
"for i in f_values:\n",
" stake = np.random.pareto(10, 1000)\n",
" sim_chain_growth = Sim(\n",
" params=Params(\n",
" SLOTS=100000,\n",
" f=i,\n",
" adversary_control = 10 ** -9,\n",
" honest_stake = stake,\n",
" total_stake_estimate=stake.sum()\n",
" ),\n",
" network=blend_net\n",
" )\n",
" sim_chain_growth.run()\n",
"\n",
" active_slots = np.full(sim_chain_growth.params.SLOTS, 0)\n",
" for b in sim_chain_growth.blocks:\n",
" if active_slots[b.height - 1] == 0:\n",
" active_slots[b.height - 1] = 1\n",
" \n",
" print(f\"HONEST RATIO: {len(sim_chain_growth.honest_chain()) / sim_chain_growth.params.SLOTS}\")\n",
" print(f\"ACTIVE RATIO: {active_slots.sum() / sim_chain_growth.params.SLOTS}\")\n",
" print(f\"F RATIO: {sim_chain_growth.params.f}\")\n",
" ratios.append(len(sim_chain_growth.honest_chain()) / sim_chain_growth.params.SLOTS / sim_chain_growth.params.f)"
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "e0dfb7d6-3975-4e8c-b4f2-42b7f5f600bf",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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",
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.plot(f_values, ratios)\n",
"plt.xscale('log')\n",
"plt.xlabel(\"Target slot occupancy rate (f)\")\n",
"plt.ylabel(\"Honest chain blocks per slot vs f\")\n",
"plt.title(\"Honest chain block production vs target block production rates\")\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1558fd2d-ec9a-462a-ae69-05cb1636946e",
"metadata": {},
"outputs": [],
"source": [
"f_values = [1 / (50 * i) for i in range(1, 30)]\n",
"ratios = []\n",
"for i in f_values:\n",
" np.random.seed(0)\n",
" sim_chain_growth = Sim(\n",
" params=Params(\n",
" SLOTS=100000,\n",
" f=i,\n",
" adversary_control = 10 ** -9,\n",
" honest_stake = np.random.pareto(10, 1000)\n",
" ),\n",
" network=blend_net\n",
" )\n",
" sim_chain_growth.run(seed=0)\n",
"\n",
" active_slots = np.full(sim_chain_growth.params.SLOTS, 0)\n",
" for b in sim_chain_growth.blocks:\n",
" if active_slots[b.height - 1] == 0:\n",
" active_slots[b.height - 1] = 1\n",
" \n",
" print(f\"HONEST RATIO: {len(sim_chain_growth.honest_chain()) / sim_chain_growth.params.SLOTS}\")\n",
" print(f\"ACTIVE RATIO: {active_slots.sum() / sim_chain_growth.params.SLOTS}\")\n",
" print(f\"F RATIO: {sim_chain_growth.params.f}\")\n",
" ratios.append(len(sim_chain_growth.honest_chain()) / sim_chain_growth.params.SLOTS / sim_chain_growth.params.f)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4d2de249-9d61-498a-b0a3-56f0c5164459",
"metadata": {},
"outputs": [],
"source": [
"plt.plot(f_values, ratios)\n",
"plt.xscale('log')\n",
"plt.xlabel(\"Target slot occupancy rate (f)\")\n",
"plt.ylabel(\"Honest chain blocks per slot vs f\")\n",
"plt.title(\"Honest chain block production vs target block production rates\")\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3130e27c-1ce6-439a-a6e7-436990b9315d",
"metadata": {},
"outputs": [],
"source": [
"_ = sim.adverserial_analysis(should_plot=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "13b4392d-d4ab-4b97-a7db-2bede9b3de9a",
"metadata": {},
"outputs": [],
"source": [
"# sim.visualize_chain()"
]
},
{
"cell_type": "markdown",
"id": "1f552a04-a9bf-4723-b6a8-8faaa147762e",
"metadata": {},
"source": [
"# Reorg sensitivity to blending delay"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f2bbb618-96c0-4a6d-b1dc-3b73ebafa5e2",
"metadata": {},
"outputs": [],
"source": [
"def reorg_depth_analysis(sim, adv, MAX, **kwargs):\n",
" max_depth = min(MAX, max(adv) if sum(adv) > 0 else 0)\n",
" \n",
" count_by_depth = np.bincount(adv, minlength=max_depth)[:max_depth]\n",
" \n",
" # count_by_depth = np.zeros(max_depth)\n",
" \n",
" # for d in range(max_depth):\n",
" # count_by_depth[d] = (adv == d).sum()\n",
"\n",
" block_time = 1 / sim.params.f\n",
" honest_chain_length = len(sim.honest_chain())\n",
" blocks = len(sim.blocks)\n",
" expected_blocks = sim.params.SLOTS * sim.params.f\n",
" plt.plot(np.arange(max_depth), count_by_depth / expected_blocks, **kwargs)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9cca2f57-1083-446c-b083-1dd158e0e7ca",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"\n",
"np.random.seed(0)\n",
"stake = np.random.pareto(10, 100)\n",
"SLOTS = 2000\n",
"\n",
"\n",
"sims = [Sim(\n",
" params=Params(\n",
" SLOTS=SLOTS,\n",
" f=1/30,\n",
" adversary_control = 0.3,\n",
" honest_stake = stake\n",
" ),\n",
" network=replace(blend_net, blending_delay=i),\n",
") for i in [1,2,3,5]]\n",
"sims += [Sim(\n",
" params=Params(\n",
" SLOTS=int(SLOTS * 3/2),\n",
" f=1/20,\n",
" adversary_control = 0.3,\n",
" honest_stake = stake\n",
" ),\n",
" network=replace(blend_net, blend_hops=0),\n",
")]\n",
"\n",
"for i, sim in enumerate(sims):\n",
" print(f\"simulating {i+1}/{len(sims)}\")\n",
" sim.run(seed=0)\n",
"\n",
"print(\"finished simulation, starting analysis\")\n",
"advs = [sim.adverserial_analysis(should_plot=False) for sim in sims]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1ff938f3-6bc4-4b8e-bd9a-492db140d7b9",
"metadata": {},
"outputs": [],
"source": [
"MAX=min(40, max(max(adv) for adv in advs))\n",
"\n",
"for s in range(len(sims) - 1):\n",
" reorg_depth_analysis(sims[s], advs[s], MAX, label=f\"blending_delay={sims[s].network.blending_delay}s\")\n",
"\n",
"reorg_depth_analysis(sims[-1], advs[-1], MAX, label=f\"cardano\")\n",
"\n",
"_ = plt.title(f\"reorg depth sensitivity to blend network delay @ {1/sims[s].params.f:.0f}s block time\")\n",
"_ = plt.xlabel(\"reorg depth\")\n",
"_ = plt.ylabel(\"frequency\")\n",
"_ = plt.legend()\n",
"_ = plt.yscale(\"log\")\n",
"_ = plt.xlim(0, MAX)\n",
"_ = plt.ylim(10**-4,None)\n",
"# _ = plt."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8c9a369c-2d55-4c07-8bfe-9e270cfed90a",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"PATHS = 1\n",
"target_block_num = 2000\n",
"np.random.seed(0)\n",
"stake = np.random.pareto(10, 100)\n",
"network = blend_net\n",
"sim_params = Params(\n",
" SLOTS=0,\n",
" f=0.05,\n",
" adversary_control = 0.3,\n",
" honest_stake = stake\n",
")\n",
"np.random.seed(1)\n",
"sims = [Sim(\n",
" params=replace(\n",
" sim_params,\n",
" SLOTS=int(target_block_num * block_time),\n",
" f=1/block_time\n",
" ),\n",
" network=network\n",
") for block_time in np.array([35]).repeat(PATHS)]\n",
"\n",
"\n",
"for i, sim in enumerate(sims):\n",
" print(f\"simulating {i+1}/{len(sims)}\")\n",
" sim.run(seed=i)\n",
"\n",
"print(\"finished simulation, starting analysis\")\n",
"advs = [sim.adverserial_analysis() for sim in sims]\n",
"\n",
"print(\"cardano parameters\")\n",
"cardano_block_time = 20\n",
"cardano_sims = [Sim(\n",
" params=replace(\n",
" sim_params,\n",
" SLOTS=int(target_block_num * cardano_block_time),\n",
" f=1/cardano_block_time,\n",
" ),\n",
" network=replace(network, blend_hops=0)\n",
") for _ in range(PATHS)]\n",
"\n",
"for i, sim in enumerate(cardano_sims):\n",
" print(f\"simulating {i+1}/{len(cardano_sims)}\")\n",
" sim.run(seed=i)\n",
"\n",
"cardano_advs = [sim.adverserial_analysis() for sim in cardano_sims]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5f47f6b7-f2bd-4db4-a6a8-358d2081820c",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"i = 0\n",
"j = 0\n",
"expected_blocks_nomos = sims[i].params.SLOTS * sims[i].params.f\n",
"expected_blocks_cardano = cardano_sims[j].params.SLOTS * cardano_sims[j].params.f\n",
"\n",
"MAX = max(max(adv) for adv in (advs + cardano_advs))\n",
"print(\"MAX\", MAX)\n",
"hist_nomos = np.bincount(advs[0], minlength=MAX)\n",
"hist_cardano = np.bincount(cardano_advs[0], minlength=MAX)\n",
"\n",
"cutoff = min(min(np.where(hist_nomos == 0)[0], default=MAX), min(np.where(hist_cardano == 0)[0], default=MAX))\n",
"print(\"cutoff\", cutoff)\n",
"\n",
"plt.plot(np.arange(cutoff), (hist_nomos[:cutoff] / expected_blocks_nomos) / (hist_cardano[:cutoff] / expected_blocks_cardano))\n",
"plt.ylabel(\"Multiple Increase in Reorgs\")\n",
"plt.xlabel(\"Reorg Depth\")\n",
"plt.title(\"Plotting the ratio $\\\\frac{\\\\text{Nomos reorgs}}{\\\\text{Caradano reorgs}}$\")\n",
"None"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "87c8d0b8-c8d2-4c49-a9c4-2eaefd41c254",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"k = 2160\n",
"MAX=min(k, max(max(adv) for adv in advs))\n",
"network_samples = network.empirical_network_delay()\n",
"network_delay_mean = network_samples.mean()\n",
"f_index = list({s.params.f for s in sims})\n",
"hops_index = list({s.network.blend_hops for s in sims})\n",
"delay_index = list({s.network.blending_delay for s in sims})\n",
"colors = ['#855C75FF', '#D9AF6BFF', '#AF6458FF', '#736F4CFF', '#526A83FF', '#625377FF', '#68855CFF', '#9C9C5EFF', '#A06177FF', '#8C785DFF', '#467378FF', '#7C7C7CFF']\n",
"\n",
"for s in range(len(sims)):\n",
" print(f'{s+1} / {len(sims)}')\n",
" block_time = 1 / sims[s].params.f\n",
" c = colors[f_index.index(sims[s].params.f)]\n",
" # c = colors[hops_index.index(sims[s].network.blend_hops)]\n",
" # c = colors[delay_index.index(sims[s].network.blending_delay)]\n",
" reorg_depth_analysis(sims[s], advs[s], MAX, color=c, lw=\"1\", label=f\"{block_time:.1f}s ~ {block_time / network_delay_mean:.0f}x net delay\")\n",
" # reorg_depth_analysis(sims[s], advs[s], MAX, color=c, lw=\"1\", label=f\"hops={sims[s].network.blend_hops}\")\n",
" # reorg_depth_analysis(sims[s], advs[s], MAX, color=c, lw=\"1\", label=f\"blending delay={sims[s].network.blending_delay}\")\n",
"\n",
"\n",
"for s in range(len(cardano_sims)):\n",
" reorg_depth_analysis(cardano_sims[s], cardano_advs[s], MAX, lw=\"1\", color=\"k\", label=f\"cardano\")\n",
"\n",
"_ = plt.title(f\"reorg depth sensitivity\")\n",
"_ = plt.xlabel(\"reorg depth\")\n",
"_ = plt.ylabel(\"frequency (per block)\")\n",
"_ = plt.yscale(\"log\")\n",
"_ = plt.xlim(0, MAX)\n",
"_ = plt.ylim(10**-4,None)\n",
"\n",
"# avoid duplicate legend entries with the same label\n",
"handles, labels = plt.gca().get_legend_handles_labels()\n",
"by_label = dict(zip(labels, handles))\n",
"_ = plt.legend(by_label.values(), by_label.keys())"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fdc9ae28-5c4f-4ffd-a090-ae7a7225ec4c",
"metadata": {},
"outputs": [],
"source": [
"N = 100\n",
"M = 10000\n",
"no_blend_samples = no_blend_net.empirical_network_delay()\n",
"no_blend_mean = no_blend_samples.mean()\n",
"blend_samples = blend_net.empirical_network_delay()\n",
"blend_mean = blend_samples.mean()\n",
"\n",
"_ = plt.hist(no_blend_samples, bins=100, density=True, label=\"no-blend\")\n",
"_ = plt.hist(blend_samples, bins=100, density=True, label=\"blend\")\n",
"\n",
"for p in [50, 99, 99.9]:\n",
" no_blend_pct = np.percentile(no_blend_samples, p)\n",
" _ = plt.vlines(no_blend_pct, ymin=0, ymax=0.25, color='darkblue', label=f\"no-blend {p}p={no_blend_pct:.1f}s\")\n",
"\n",
"for p in [50, 99, 99.9]:\n",
" blend_pct = np.percentile(blend_samples, p)\n",
" _ = plt.vlines(blend_pct, ymin=0, ymax=0.25, color='brown', label=f\"blend {p}p={blend_pct:.1f}s\")\n",
"# _ = plt.vlines(blend_mean, ymin=0, ymax=1, color='brown', label=f\"blend 50p={blend_mean:.1f}s\")\n",
"# _ = plt.hist(blend_net.block_arrival_slot(np.zeros(1000)), bins=100, density=True, label=\"blend\")\n",
"_ = plt.legend()\n",
"_ = plt.xlabel(\"block delay\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fa860b3d-6cb1-4ee5-a534-ab6fe4440a46",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "48350036-439a-4954-9021-e55b7b5f004a",
"metadata": {},
"outputs": [],
"source": [
"1 + 1"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "efdd3166-892f-4f0e-b80f-ba279e3a02f2",
"metadata": {},
"outputs": [],
"source": []
}
],
"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.13.4"
}
},
"nbformat": 4,
"nbformat_minor": 5
}