.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/vm/ethereum/plot_03_reentrancy.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_vm_ethereum_plot_03_reentrancy.py: The DAO and reentrancy: checks, effects, interactions (2016) ============================================================ The DAO was an Ethereum fund holding about 14% of all ether. Its withdrawal code sent ether *before* updating the caller's balance. Sending ether to a contract runs the contract's code, so in June 2016 an attacker's contract called back into the withdrawal from inside the payment, again and again, while its balance still showed the original deposit. About 3.6 million ether were drained, and Ethereum hard-forked to return them. What to look for ---------------- With the vulnerable order, the attacker withdraws its deposit once per nested call, until the bank is empty or the call-depth limit stops it. With the *checks-effects-interactions* order, the re-entrant call finds a zero balance and the attacker gets back exactly its deposit. The history behind this experiment: :doc:`/history/vm_breakthroughs`. .. GENERATED FROM PYTHON SOURCE LINES 24-26 Drain a bank holding 10,000 of other people's funds --------------------------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 26-40 .. code-block:: Python import matplotlib.pyplot as plt import blockchainkit as bk deposits = [10, 50, 100, 500, 1000] vulnerable = [bk.vm.drain_bank(10_000, d) for d in deposits] safe = [bk.vm.drain_bank(10_000, d, checks_effects_interactions=True) for d in deposits] for d, v, s in zip(deposits, vulnerable, safe, strict=True): print(f"deposit {d:5d}: vulnerable steals {v.stolen} in {v.calls} calls, safe {s.stolen}") assert all(s.stolen == 0 for s in safe) assert all( v.withdrawn == ((10_000 + d) // d) * d for d, v in zip(deposits, vulnerable, strict=True) ) .. rst-class:: sphx-glr-script-out .. code-block:: none deposit 10: vulnerable steals 10000 in 1002 calls, safe 0 deposit 50: vulnerable steals 10000 in 202 calls, safe 0 deposit 100: vulnerable steals 10000 in 102 calls, safe 0 deposit 500: vulnerable steals 10000 in 22 calls, safe 0 deposit 1000: vulnerable steals 10000 in 12 calls, safe 0 .. GENERATED FROM PYTHON SOURCE LINES 41-44 The depth limit --------------- Ethereum then limited calls to a depth of 1024; tiny deposits run into it. .. GENERATED FROM PYTHON SOURCE LINES 44-63 .. code-block:: Python deep = bk.vm.drain_bank(10**9, 10) print(deep) assert deep.calls == 1024 and deep.withdrawn == 10_240 fig, (left, right) = plt.subplots(1, 2, figsize=(10, 4)) left.bar( ["pay, then update", "update, then pay"], [vulnerable[2].stolen, safe[2].stolen], color=["#dc2626", "#16a34a"], ) left.set(ylabel="stolen", title="Deposit 100, bank 10,000") right.loglog(deposits, [v.calls for v in vulnerable], "o-", color="#dc2626") right.set( xlabel="attacker deposit", ylabel="nested withdraw calls", title="Smaller deposit, deeper recursion", ) fig.tight_layout() .. image-sg:: /api/gallery/vm/ethereum/images/sphx_glr_plot_03_reentrancy_001.png :alt: Deposit 100, bank 10,000, Smaller deposit, deeper recursion :srcset: /api/gallery/vm/ethereum/images/sphx_glr_plot_03_reentrancy_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none ReentrancyResult(withdrawn=10240, deposited=10, calls=1024, bank_balance=999989770) .. GENERATED FROM PYTHON SOURCE LINES 64-69 Exercise -------- A reentrancy *guard* sets a lock flag on entry and refuses to run while it is set. Add a ``guarded`` option to a copy of ``drain_bank``'s loop. Why do auditors still recommend checks-effects-interactions as well? .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.267 seconds) .. _sphx_glr_download_api_gallery_vm_ethereum_plot_03_reentrancy.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/vm/ethereum/plot_03_reentrancy.ipynb :alt: Launch JupyterLite :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_03_reentrancy.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_03_reentrancy.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_03_reentrancy.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_