.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/contracts/wallets/plot_02_parity_library_freeze.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code or to run this example in your browser via JupyterLite. .. rst-class:: sphx-glr-example-title .. _sphx_glr_api_gallery_contracts_wallets_plot_02_parity_library_freeze.py: The Parity library freeze: selfdestruct behind DELEGATECALL (November 2017) =========================================================================== After the July hack, Parity deployed a fixed library whose ``initWallet`` runs only on uninitialized storage. Every wallet initialized itself at deployment, so no wallet could be taken over again. But the library was a contract too, with storage of its own, and nobody had initialized it. On 6 November 2017 a user called ``initWallet`` on the library itself, became its sole owner, and called ``kill``, which ran ``selfdestruct``. The library's code was gone. A ``DELEGATECALL`` to an address without code does not fail: it runs nothing and returns success. Every wallet built on the library kept its ether and lost every function that could move it; about 513,774 ether were frozen for good. The rule it teaches: code reached by ``DELEGATECALL`` is part of every caller, so a library must be initialized, or better have no state and no ``selfdestruct`` at all. .. GENERATED FROM PYTHON SOURCE LINES 23-28 .. code-block:: Python import matplotlib.pyplot as plt import blockchainkit as bk from blockchainkit.contracts.visualizers import plot_call_tree .. GENERATED FROM PYTHON SOURCE LINES 29-31 Three wallets on the patched library ------------------------------------ .. GENERATED FROM PYTHON SOURCE LINES 31-46 .. code-block:: Python ALICE, BOB, USER = 11, 13, 199 # Fixed teaching keys. owners = [bk.crypto.public_key(ALICE), bk.crypto.public_key(BOB)] world = bk.contracts.World() library = world.deploy("parity", bk.contracts.PatchedWalletLibrary, name="library") wallets = [] for index, amount in enumerate((300, 500, 700)): wallet = world.deploy("alice", bk.contracts.Wallet, library, owners, 2, name=f"wallet {index}") world.fund(wallet, amount) wallets.append(wallet) before = [world.balance(w) for w in wallets] reinit = world.transact("attacker", wallets[0], "init_wallet", [bk.crypto.public_key(USER)], 1) assert reinit.error == "already initialized" # The July bug is fixed. .. GENERATED FROM PYTHON SOURCE LINES 47-49 Initialize the library itself, then kill it ------------------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 49-79 .. code-block:: Python assert world.transact("devops199", library, "init_wallet", [bk.crypto.public_key(USER)], 1).success kill = [bk.contracts.approve_action(USER, library, 0, "kill", "devops199")] assert world.transact("devops199", library, "kill", "devops199", kill).success assert world.code(library) is None approvals = [ bk.contracts.approve_action(k, wallets[0], 0, "execute", "alice", 300) for k in (ALICE, BOB) ] attempt = world.transact("alice", wallets[0], "execute", "alice", 300, approvals) print( "withdrawal 'succeeded':", attempt.success, "result:", attempt.result, "alice received:", world.balance("alice"), ) assert attempt.success and world.balance("alice") == 0 assert [world.balance(w) for w in wallets] == before fig, (left, right) = plt.subplots(1, 2, figsize=(11, 3.5), gridspec_kw={"width_ratios": [1, 1.6]}) left.bar([world.name(w) for w in wallets], before, color="#64748b") left.set(ylabel="ether", title="Balances, forever out of reach") plot_call_tree(attempt, names=world.name, ax=right) right.set_title("A signed withdrawal delegates to nothing") fig.tight_layout() plt.show() .. image-sg:: /api/gallery/contracts/wallets/images/sphx_glr_plot_02_parity_library_freeze_001.png :alt: Balances, forever out of reach, A signed withdrawal delegates to nothing :srcset: /api/gallery/contracts/wallets/images/sphx_glr_plot_02_parity_library_freeze_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none withdrawal 'succeeded': True result: None alice received: 0 .. GENERATED FROM PYTHON SOURCE LINES 80-85 Exercise -------- Give ``PatchedWalletLibrary`` a constructor that initializes the library's own storage with no owners it could ever satisfy (for example a threshold no signer can meet). Repeat the attack. Which call now fails? .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.083 seconds) .. _sphx_glr_download_api_gallery_contracts_wallets_plot_02_parity_library_freeze.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: lite-badge .. image:: images/jupyterlite_badge_logo.svg :target: ../../../../lite/lab/index.html?path=api/gallery/contracts/wallets/plot_02_parity_library_freeze.ipynb :alt: Launch JupyterLite :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_02_parity_library_freeze.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_02_parity_library_freeze.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_02_parity_library_freeze.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_