96 lines
4.0 KiB
Python
96 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Illustrative, non-consensus PoP fee/fairness research. All numeric values hypothetical."""
|
|
from __future__ import annotations
|
|
import argparse
|
|
import random
|
|
|
|
|
|
def progressive_example(rewards_90d: float) -> float:
|
|
"""Example marginal brackets. NEVER use floating-point for on-chain fees."""
|
|
if rewards_90d < 0:
|
|
raise ValueError('Rewards must be nonnegative')
|
|
return (min(rewards_90d, 10) * 0.00005
|
|
+ min(max(rewards_90d - 10, 0), 90) * 0.0002
|
|
+ max(rewards_90d - 100, 0) * 0.0005)
|
|
|
|
|
|
def fee_example(window: int, rewards_90d: float) -> float:
|
|
if window == 1:
|
|
return 0.0
|
|
p = progressive_example(rewards_90d)
|
|
if window == 2:
|
|
return 0.001 + p
|
|
if window == 3:
|
|
return 0.003 + 2 * p
|
|
raise ValueError('Window must be 1, 2 or 3')
|
|
|
|
|
|
def qualify(rng: random.Random, windows: int, probability: float,
|
|
rewards_90d: float) -> tuple[bool, float]:
|
|
"""One ticket at most, fees charged only after the previous window fails."""
|
|
if not 1 <= windows <= 3 or not 0 <= probability <= 1:
|
|
raise ValueError('Invalid windows/probability')
|
|
fee = 0.0
|
|
for window in range(1, windows + 1):
|
|
if window > 1:
|
|
fee += fee_example(window, rewards_90d)
|
|
if rng.random() < probability:
|
|
return True, fee
|
|
return False, fee
|
|
|
|
|
|
def simulate(miners: int, runs: int, seed: int, probability: float,
|
|
windows: int, rewards_90d: float, reward: float = 1.0) -> dict[str, float]:
|
|
"""One chosen miner vs N-1 miners always attempting window one."""
|
|
if miners < 1 or runs < 1 or reward < 0:
|
|
raise ValueError('Invalid simulation scenario')
|
|
rng = random.Random(seed)
|
|
got_ticket = wins = 0
|
|
spent = 0.0
|
|
for _ in range(runs):
|
|
qualified, fee = qualify(rng, windows, probability, rewards_90d)
|
|
spent += fee
|
|
if qualified:
|
|
got_ticket += 1
|
|
others = sum(rng.random() < probability for _ in range(miners - 1))
|
|
if qualified and rng.randrange(others + 1) == 0:
|
|
wins += 1
|
|
return {
|
|
'ticket_probability': got_ticket / runs,
|
|
'win_probability': wins / runs,
|
|
'avg_fee': spent / runs,
|
|
'max_fee': sum(fee_example(w, rewards_90d) for w in range(2, windows + 1)),
|
|
'net_expected_ptm': (wins / runs) * reward - spent / runs,
|
|
}
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--runs', type=int, default=25000)
|
|
parser.add_argument('--seed', type=int, default=20261025)
|
|
parser.add_argument('--p', type=float, default=0.70, help='Example success per full window')
|
|
parser.add_argument('--reward', type=float, default=1.0, help='Illustrative PTM per won lottery')
|
|
args = parser.parse_args()
|
|
print('FORSCHUNGSMODELL, KEINE PROTOKOLLREGEL. Float nur für Simulation, niemals On-chain.')
|
|
print(f'Parameter: runs={args.runs}, seed={args.seed}, p={args.p}, reward={args.reward} PTM')
|
|
print('Alle anderen Miner nutzen nur das kostenlose Fenster 1.')
|
|
print('N 90d-Ertrag Fenster Ticket% Gewinn% Ø Gebühr max Gebühr Netto-PTM/Runde')
|
|
for n in (5, 10, 20, 50):
|
|
for mined_90d in (0, 10, 100, 1000):
|
|
for windows in (1, 2, 3):
|
|
res = simulate(n, args.runs, args.seed + n + int(mined_90d),
|
|
args.p, windows, float(mined_90d), args.reward)
|
|
print(f'{n:<3} {mined_90d:>10} {windows:>7} '
|
|
f'{100*res["ticket_probability"]:>7.2f} '
|
|
f'{100*res["win_probability"]:>8.2f} '
|
|
f'{res["avg_fee"]:>9.6f} '
|
|
f'{res["max_fee"]:>11.6f} '
|
|
f'{res["net_expected_ptm"]:>15.6f}')
|
|
print('\nACHTUNG: Modell ohne Sybil-Effekt, Gebührenbudget, Validator-Zensur, reale Hashzeiten,')
|
|
print('endogene Teilnahmeentscheidung oder Unsicherheit der PoP-Belohnung; keine')
|
|
print('Garantie für faire Chancen oder wirtschaftlich sinnvolle Tarifwahl.')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|