118 lines
3.8 KiB
Python

from typing import List, Dict, Any, Tuple
import numpy as np
np.random.seed(42)
import pandas as pd
import tqdm
from simulation_parameters import SimulationParameters
from tx_fees_models import Block, Blockchain, MemPool, TransactionFeeMechanism, \
EIP1559, StableFee, Transaction, create_demand
def _generate_random_bool(p_true=0.5):
return np.random.choice([True, False], p=(p_true, 1.-p_true), size=1)[0]
def run_simulation(params: SimulationParameters) -> Tuple[pd.DataFrame, pd.DataFrame]:
blockchain:Dict[str,Blockchain] = {
"EIP": Blockchain(),
"StableFee": Blockchain()
}
mempool:Dict[str, MemPool] = {
"EIP": MemPool(),
"StableFee": MemPool()
}
tf_models:dict[str, TransactionFeeMechanism] = {
"EIP": EIP1559(),
"StableFee": StableFee()
}
df_stats:Dict[str, pd.DataFrame] = {
"EIP": pd.DataFrame(
0,
columns=["demand_size", "num_tx", "price", "tot_paid"],
index=range(params.num_blocks)
),
"StableFee": pd.DataFrame(
0,
columns=["demand_size", "num_tx", "price", "tot_paid"],
index=range(params.num_blocks)
)
}
pbar = tqdm.tqdm(total=params.num_blocks)
for b in range(params.num_blocks):
# transactions are created with random values
demand_size:int = np.random.choice(params.demand_sizes, p=params.demand_probabilities, size=1)[0]
demand:List[Transaction] = create_demand(
demand_size,
fee_cap_range=params.fee_cap_range,
max_tip_pct=params.max_tip_pct,
variable_gas=True
)
stop_below_gas_limit:bool = _generate_random_bool(params.probability_stop_below_gas_limit)
scale_block_size = np.random.uniform(
params.scale_block_size_limits[0], params.scale_block_size_limits[1]
)
for chain, pool, tfm, stats in zip(
blockchain.values(), mempool.values(), tf_models.values(), df_stats.values()
):
scaled_demand:List[Transaction] = tfm.scale_demand(demand)
# transactions are added to the mempool
pool.add_txs(scaled_demand)
# transactions are selected from the mempool based on the gas premium
selected_transactions, to_be_purged_transactions = tfm.select_transactions(
pool,
stop_below_gas_limit=stop_below_gas_limit,
scale_block_size=scale_block_size,
purge_after=params.purge_after
)
# selected transactions are added to the blockchain
chain.add_block(Block(selected_transactions))
# the price is updated based on the selected transactions
tfm.update_price(chain)
# base fee is updated for the next round
stats.loc[b, "demand_size"] = demand_size
stats.loc[b, "num_tx"] = len(selected_transactions)
stats.loc[b, "price"] = tfm.get_current_price()
stats.loc[b, "tot_paid"] = tfm.total_paid_fees(selected_transactions)
# clear the mempool
pool.remove_txs(selected_transactions)
pool.remove_txs(to_be_purged_transactions)
pbar.update(1)
pbar.close()
df_stats_merged = pd.concat(
[df_stats[mechanism].add_suffix("_" + mechanism) for mechanism in df_stats.keys()],
axis=1
)
df_chain_stats:Dict[str,pd.DataFrame] = {
"EIP": blockchain["EIP"].compute_stats(),
"StableFee": blockchain["StableFee"].compute_stats()
}
df_chain_stats_merged = pd.concat(
[df_chain_stats[mechanism].add_suffix("_" + mechanism) for mechanism in df_stats.keys()],
axis=1
)
return df_stats_merged, df_chain_stats_merged