"""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