"""Four acts against chain.py. No clock, no randomness: byte-identical runs.

  1  tamper a block and remine nothing  -> the damage is ONE link, not n
  2  tamper it and remine the suffix    -> validate() says True
  3  a second node, and the fork rule   -> 9 blocks for 21 hashes
  4  what burying a transaction buys    -> linear in depth, not exponential
"""

from chain import (Block, GENESIS_PREV, build_chain, choose_heaviest,
                   choose_longest, counted, counter_reset, meets, mine,
                   tamper_and_remine, total_work, validate)

DATAS = [f"tx{i}: alice pays bob {i}".encode() for i in range(8)]
MALLORY = b"tx3: alice pays MALLORY 1000000"

# ------------------------------------------------------------------- act 1

counter_reset()
honest16, cost16 = build_chain(DATAS, 16)
print(f"=== [1] tamper block 3, remine NOTHING  ({len(honest16)} blocks @ 16 bits) ===")
print(f"  honest chain mined in {cost16:,} hashes "
      f"(expected 8 * 2**16 = {8 * (1 << 16):,})")
print(f"  validate(honest)  = {validate(honest16)}")

naive = [Block(b.index, b.prev, b.data, b.bits, b.nonce) for b in honest16]
naive[3].data = MALLORY
print(f"  validate(tampered)= {validate(naive)}")
print("  per-block damage report:")
broken_link = broken_pow = 0
for i, b in enumerate(naive):
    link = b.prev == (naive[i - 1].hash() if i else GENESIS_PREV)
    pow_ok = meets(b.hash(), b.bits)
    broken_link += not link
    broken_pow += not pow_ok
    print(f"    block {i}: link={str(link):5} pow={str(pow_ok):5}")
print(f"  broken links = {broken_link}, broken proofs-of-work = {broken_pow}, "
      f"blocks after 3 = {len(naive) - 4}")

# ------------------------------------------------------------------- act 2

forged, forge_cost = tamper_and_remine(honest16, 3, MALLORY)
print(f"\n=== [2] tamper block 3 and REMINE blocks 3..7 ===")
print(f"  remine cost   = {forge_cost:,} hashes for {len(honest16) - 3} blocks")
print(f"  validate      = {validate(forged)}")
print(f"  block 3 says  = {forged[3].data!r}")
print(f"  honest said   = {honest16[3].data!r}")
print(f"  same length   = {len(forged) == len(honest16)}")
print(f"  same work     = {total_work(forged) == total_work(honest16)} "
      f"({total_work(forged):,} vs {total_work(honest16):,})")
print(f"  tips differ   = {forged[-1].hash().hex()[:16]} vs "
      f"{honest16[-1].hash().hex()[:16]}")

# ------------------------------------------------------------------- act 3

HONEST_BITS, ATTACK_BITS, FORK_AT = 18, 1, 3
counter_reset()
honest, hon_cost = build_chain(DATAS, HONEST_BITS)

before = counted()
attack = list(honest[:FORK_AT])
prev = honest[FORK_AT - 1].hash()
for i in range(FORK_AT, len(honest) + 1):          # ONE block longer
    d = MALLORY if i == FORK_AT else f"tx{i}: filler".encode()
    b, _ = mine(i, prev, d, ATTACK_BITS)           # ...at a difficulty it picks
    attack.append(b)
    prev = b.hash()
att_cost = counted() - before

print(f"\n=== [3] a second node arrives holding a different chain ===")
print(f"  honest  {len(honest)} blocks @ {HONEST_BITS} bits  mined {hon_cost:>10,} hashes"
      f"  work {total_work(honest):>10,}")
print(f"  attack  {len(attack)} blocks @ {ATTACK_BITS} bit   mined {att_cost:>10,} hashes"
      f"  work {total_work(attack):>10,}")
print(f"  attacker paid {att_cost / hon_cost * 100:.5f}% of the honest work "
      f"({hon_cost / att_cost:,.0f}x cheaper)")
print()
print(f"  {'validator':>10} {'fork rule':>10} | {'accepts?':>9} {'picks':>8} "
      f"{'history rewritten?':>19}")
print("  " + "-" * 62)
for pin in (False, True):
    for bywork in (False, True):
        ok, _ = validate(attack, expected_bits=HONEST_BITS if pin else None)
        chooser = choose_heaviest if bywork else choose_longest
        picks = "n/a" if not ok else (
            "ATTACK" if chooser(honest, attack) is attack else "honest")
        print(f"  {'pinned' if pin else 'unpinned':>10} "
              f"{'work' if bywork else 'length':>10} | {str(ok):>9} {picks:>8} "
              f"{str(picks == 'ATTACK').upper():>19}")

# ------------------------------------------------------------------- act 4

DEPTH_BITS, N, SAMPLES = 10, 14, 100
counter_reset()
deep, deep_cost = build_chain([f"tx{i}".encode() for i in range(N)], DEPTH_BITS)
print(f"\n=== [4] what burial depth buys ({N} blocks @ {DEPTH_BITS} bits, "
      f"{SAMPLES} rewrites averaged) ===")
print(f"  {'k buried':>9} {'mean rewrite':>13} {'mean/block':>11} "
      f"{'vs 2**bits':>11} {'vs k=1':>8} {'linear?':>8} {'all valid':>10}")
print("  " + "-" * 74)
base = None
for k in (1, 2, 3, 6, 9, 12):
    total, allok = 0, True
    for s in range(SAMPLES):
        f, c = tamper_and_remine(deep, N - k, f"MALLORY-{s}".encode())
        total += c
        allok &= validate(f)[0]
    mean = total / SAMPLES
    base = base if base is not None else mean
    print(f"  {k:>9} {mean:>13,.0f} {mean / k:>11,.0f} "
          f"{mean / k / (1 << DEPTH_BITS):>10.2f}x {mean / base:>7.2f}x "
          f"{k:>7}x {str(allok):>10}")

print(f"\ntotal SHA-256 evaluations in this demo: {counted() + hon_cost + att_cost + cost16 + forge_cost:,}")
