.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/contracts/payments/plot_02_governmental_gas.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_payments_plot_02_governmental_gas.py: GovernMental: an unbounded loop meets the block gas limit (2016) ================================================================ GovernMental was a Ponzi game: the last person to invest before 12 hours of silence won the jackpot. Paying out also reset the game, by clearing the array of creditors, one storage write per entry. In April 2016 the array had grown so long that clearing it needed more gas than a block allowed (about 4.7 million then), so the payout could not execute and roughly 1,100 ether sat stuck in the contract. The cost of the reset grows linearly with the number of creditors :math:`n`, and the payout fails once .. math:: G_{\text{base}} + n \cdot G_{\text{write}} > G_{\text{block}}. Any loop over a collection that users can grow is a denial of service waiting to happen. A constant-cost reset, here a new round number instead of deleting old entries, pays out whatever the size. .. GENERATED FROM PYTHON SOURCE LINES 25-31 .. code-block:: Python import matplotlib.pyplot as plt import blockchainkit as bk from blockchainkit.contracts.systems.payments import GOVERNMENTAL_TIMEOUT from blockchainkit.contracts.systems.world import GAS_COSTS .. GENERATED FROM PYTHON SOURCE LINES 32-34 Payout gas against the number of creditors ------------------------------------------ .. GENERATED FROM PYTHON SOURCE LINES 34-72 .. code-block:: Python BLOCK_GAS_LIMIT = 4_712_388 # Ethereum's block gas limit in early 2016. sizes = [100, 300, 600, 900, 920, 940, 1_200] def payout(creditors, lazy): world = bk.contracts.World(block_gas_limit=BLOCK_GAS_LIMIT) game = world.deploy("operator", bk.contracts.GovernMental, lazy) for index in range(creditors): world.fund(f"p{index}", 1) world.transact(f"p{index}", game, "invest", value=1) world.advance(seconds=GOVERNMENTAL_TIMEOUT) return world.transact("anyone", game, "payout") eager = {n: payout(n, lazy=False) for n in sizes} lazy = {n: payout(n, lazy=True) for n in sizes} for n in sizes: print( f"{n:5d} creditors: clear-all {eager[n].gas_used:>9,} gas " f"{'ok' if eager[n].success else eager[n].error:10s} new round {lazy[n].gas_used:,} gas" ) first_failure = min(n for n in sizes if not eager[n].success) assert all(r.success for r in lazy.values()) and first_failure == 940 assert (BLOCK_GAS_LIMIT - GAS_COSTS["transaction"]) // GAS_COSTS["write"] < first_failure fig, ax = plt.subplots(figsize=(7, 4)) ax.plot( sizes, [eager[n].gas_used for n in sizes], "o-", color="#dc2626", label="clear every creditor" ) ax.plot(sizes, [lazy[n].gas_used for n in sizes], "o-", color="#16a34a", label="start a new round") ax.axhline(BLOCK_GAS_LIMIT, color="#64748b", linestyle="--", label="block gas limit") ax.set(xlabel="creditors", ylabel="gas used by the payout", title="The payout outgrows the block") ax.legend() fig.tight_layout() plt.show() .. image-sg:: /api/gallery/contracts/payments/images/sphx_glr_plot_02_governmental_gas_001.png :alt: The payout outgrows the block :srcset: /api/gallery/contracts/payments/images/sphx_glr_plot_02_governmental_gas_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none 100 creditors: clear-all 531,500 gas ok new round 36,500 gas 300 creditors: clear-all 1,531,500 gas ok new round 36,500 gas 600 creditors: clear-all 3,031,500 gas ok new round 36,500 gas 900 creditors: clear-all 4,531,500 gas ok new round 36,500 gas 920 creditors: clear-all 4,631,500 gas ok new round 36,500 gas 940 creditors: clear-all 4,712,388 gas out of gas new round 36,500 gas 1200 creditors: clear-all 4,712,388 gas out of gas new round 36,500 gas .. GENERATED FROM PYTHON SOURCE LINES 73-79 Exercise -------- Ethereum's block gas limit has since grown to 30 million. With this model's 5,000 gas per write, how many creditors would it take to freeze the same contract today? Check your answer with ``World()``, whose default limit is 30 million. A worked solution is in :doc:`/exercises/contracts`. .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.132 seconds) .. _sphx_glr_download_api_gallery_contracts_payments_plot_02_governmental_gas.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/payments/plot_02_governmental_gas.ipynb :alt: Launch JupyterLite :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_02_governmental_gas.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_02_governmental_gas.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_02_governmental_gas.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_