113 lines
4.2 KiB
Python
113 lines
4.2 KiB
Python
"""CRDT Flow tab — multi-peer network simulation."""
|
|
|
|
import streamlit as st
|
|
|
|
from src.crdt.bridge import Network, NetworkConfig, EventType
|
|
from src.crdt.labor_crdt import AttestationEntry, submit_attestation
|
|
from src.crdt.intent_matching import Intent, add_intent
|
|
from src.crdt.dca_schedule import create_dca_schedule, DCAScheduleRegistry
|
|
from dashboard.charts import fig_crdt_divergence
|
|
|
|
|
|
def render():
|
|
st.header("CRDT Network Simulation")
|
|
|
|
col1, col2, col3, col4 = st.columns(4)
|
|
with col1:
|
|
n_peers = st.slider("Peers", 2, 10, 5)
|
|
with col2:
|
|
partition_prob = st.slider("Partition probability", 0.0, 0.5, 0.15, 0.05)
|
|
with col3:
|
|
reconnect_delay = st.slider("Reconnect delay", 1.0, 20.0, 5.0, 1.0)
|
|
with col4:
|
|
sim_steps = st.slider("Steps", 10, 100, 40)
|
|
|
|
if st.button("Simulate Network", key="crdt_run"):
|
|
peer_ids = [f"p{i+1}" for i in range(n_peers)]
|
|
config = NetworkConfig(
|
|
partition_probability=partition_prob,
|
|
reconnect_delay=reconnect_delay,
|
|
seed=42,
|
|
)
|
|
net = Network.create(peer_ids, config)
|
|
|
|
# Inject mutations across peers
|
|
for i, pid in enumerate(peer_ids):
|
|
if i % 3 == 0:
|
|
entry = AttestationEntry(
|
|
entry_id=f"e{i}", contribution_type="code",
|
|
units=5.0, timestamp=1.0, attester="admin",
|
|
)
|
|
net.mutate_peer(pid, lambda s, e=entry, c=f"dev_{i}": _with_labor(s, c, e))
|
|
elif i % 3 == 1:
|
|
intent = Intent(
|
|
intent_id=f"i{i}", maker=f"user_{i}",
|
|
sell_token="USDC", sell_amount=100.0,
|
|
buy_token="MYCO", min_buy_amount=80.0,
|
|
valid_until=999.0,
|
|
)
|
|
net.mutate_peer(pid, lambda s, it=intent: _with_intent(s, it))
|
|
else:
|
|
schedule = create_dca_schedule(
|
|
schedule_id=f"s{i}", maker=f"maker_{i}",
|
|
total_amount=1000.0, n_chunks=5,
|
|
start_time=0.0, interval=1.0,
|
|
)
|
|
net.mutate_peer(pid, lambda s, sc=schedule: _with_dca(s, sc))
|
|
|
|
with st.spinner("Simulating network..."):
|
|
sim_result = net.simulate(steps=sim_steps)
|
|
sim_result["convergence_time"] = net.convergence_time()
|
|
st.session_state["crdt_result"] = sim_result
|
|
|
|
if "crdt_result" in st.session_state:
|
|
result = st.session_state["crdt_result"]
|
|
|
|
# Metrics
|
|
m1, m2, m3, m4 = st.columns(4)
|
|
m1.metric("Final Divergence", result["divergence"][-1])
|
|
m2.metric("Total Merges", result["merge_count"][-1])
|
|
|
|
n_partitions = sum(1 for e in result["events"] if e.event_type == EventType.PARTITION)
|
|
m3.metric("Partition Events", n_partitions)
|
|
|
|
ct = result.get("convergence_time")
|
|
m4.metric("Convergence Time", f"{ct:.1f}" if ct is not None else "N/A")
|
|
|
|
st.plotly_chart(fig_crdt_divergence(result), use_container_width=True)
|
|
|
|
# Per-peer state table
|
|
st.subheader("Per-Peer State Signatures (final)")
|
|
peer_sigs = result["peer_signatures"]
|
|
table_data = []
|
|
for pid, sigs in peer_sigs.items():
|
|
final = sigs[-1] if sigs else ()
|
|
table_data.append({
|
|
"Peer": pid,
|
|
"Labor Logs": final[0] if len(final) > 0 else 0,
|
|
"Labor Entries": final[1] if len(final) > 1 else 0,
|
|
"Intents": final[2] if len(final) > 2 else 0,
|
|
"Flow Peers": final[3] if len(final) > 3 else 0,
|
|
"Trust Peers": final[4] if len(final) > 4 else 0,
|
|
"Credit Lines": final[5] if len(final) > 5 else 0,
|
|
"DCA Schedules": final[6] if len(final) > 6 else 0,
|
|
})
|
|
st.dataframe(table_data, use_container_width=True)
|
|
|
|
|
|
def _with_labor(state, contributor, entry):
|
|
state.labor = submit_attestation(state.labor, contributor, entry)
|
|
return state
|
|
|
|
|
|
def _with_intent(state, intent):
|
|
state.intents = add_intent(state.intents, intent)
|
|
return state
|
|
|
|
|
|
def _with_dca(state, schedule):
|
|
state.dca = DCAScheduleRegistry(
|
|
schedules={**state.dca.schedules, schedule.schedule_id: schedule}
|
|
)
|
|
return state
|