Great Consensus Cleanup Revival

To back up this claim i’ve run a bit more rigorous simulation than my previous estimate. If the attack were to start at block 842688 (timestamp 1715252414, difficulty 83148355189239) it’d take about 39 days to bring down the difficulty down to 1 by exploiting the timewarp vulnerability.

Here is the Python script i’ve used. The resulting table of every block and its timestamp in this window is available in this gist.

from datetime import datetime

# A real block on mainnet.
START_HEIGHT = 842688
START_TIMESTAMP = 1715252414
START_DIFF = 83148355189239

# The list of (height, timestamp) of each block. Will contain all the blocks during
# the attack, plus the blocks for the period preceding the attack.
blocks = []

# Push the honest period of blocks before the starting height.
for i in range(START_HEIGHT - 2016, START_HEIGHT):
    blocks.append((i, START_TIMESTAMP - (START_HEIGHT - i) * 10 * 60))

# Now the attack starts.
difficulty = START_DIFF
height = START_HEIGHT
periods = 0
while difficulty > 1:
    # Always set the timestamp of each block to the minimum allowed by the MTP rule.
    # We'll override it below for the last block in a period.
    median = sorted(ts for (h, ts) in blocks[-11:])[5]
    blocks.append((height, median + 1))

    # New period. First override the last block of the previous period (ie not the block
    # at the tail of the list which is the first of the new period, but the one before).
    # Then update the difficulty.
    if height > START_HEIGHT and height % 2016 == 0:
        # Estimate how long it took to mine the past 2016 blocks given the current diff.
        diff_reduction = START_DIFF / difficulty
        time_spent = 2016 * 10 * 60 / diff_reduction
        # For the first period we set the 2h in the future. For the next ones, we
        # just offset from the previous period's last block's timestamp.
        prev_last_ts = blocks[-2 - 2016][1]
        max_timestamp = prev_last_ts + time_spent
        if periods == 0:
            max_timestamp += 3_600 * 2
        blocks[-2] = (height, max_timestamp)

        # Adjust the difficulty
        red = (blocks[-2][1] - blocks[-2 - 2015][1]) / (2016 * 10 * 60)
        assert red <= 4
        difficulty /= red
        periods += 1
        print(f"End of period {periods}, reducing the diff by {red}.")

    height += 1

attack_duration = datetime.fromtimestamp(blocks[-2][1] - 3_600 * 2) - datetime.fromtimestamp(START_TIMESTAMP)
print(f"Took the difficulty down to 1 in {attack_duration} after {periods} periods.")

print(f"| height | timestamp |")
print(f"| ------ | --------- |")
for (h, ts) in blocks:
    print(f"| {h} | {datetime.fromtimestamp(ts)} |")
1 Like