micronet_antenna_core/
state.rs1use alloc::collections::{BTreeMap, BTreeSet};
2use alloc::vec::Vec;
3
4use crate::{Decision, Message, NodeId, Proposal, ProposalId, Vote, VoteRule};
5
6#[derive(Clone, Debug, Default)]
7pub struct GlobalState {
12 peers: BTreeSet<NodeId>,
13 proposals: BTreeMap<ProposalId, Proposal>,
14 votes: BTreeMap<ProposalId, Vec<Vote>>,
15 decisions: BTreeMap<ProposalId, Decision>,
16}
17
18impl GlobalState {
19 pub fn peers(&self) -> &BTreeSet<NodeId> {
21 &self.peers
22 }
23
24 pub fn proposals(&self) -> &BTreeMap<ProposalId, Proposal> {
26 &self.proposals
27 }
28
29 pub fn decision(&self, id: ProposalId) -> Option<Decision> {
31 self.decisions.get(&id).copied()
32 }
33}
34
35#[derive(Clone, Debug)]
36pub enum RuntimeEvent {
40 PeerDiscovered(NodeId),
42 ProposalReceived(ProposalId),
44 VoteReceived(ProposalId),
46 DecisionUpdated {
48 proposal_id: ProposalId,
49 decision: Decision,
50 },
51}
52
53#[derive(Clone, Debug)]
54pub struct Runtime {
62 node_id: NodeId,
63 state: GlobalState,
64 vote_rule: VoteRule,
65}
66
67impl Default for Runtime {
68 fn default() -> Self {
69 Self::new(NodeId::new([0u8; 32]))
70 }
71}
72
73impl Runtime {
74 pub fn new(node_id: NodeId) -> Self {
76 Self {
77 node_id,
78 state: GlobalState::default(),
79 vote_rule: VoteRule::SimpleMajority,
80 }
81 }
82
83 #[cfg(feature = "std")]
84 pub fn new_random() -> Self {
86 Self::new(NodeId::random())
87 }
88
89 pub fn node_id(&self) -> NodeId {
91 self.node_id
92 }
93
94 pub fn state(&self) -> &GlobalState {
96 &self.state
97 }
98
99 pub fn submit_proposal(&mut self, proposal: Proposal) {
103 let id = proposal.id;
104 self.state.proposals.insert(id, proposal);
105 self.state.decisions.insert(id, Decision::Pending);
106 }
107
108 pub fn apply(&mut self, msg: Message) -> Vec<RuntimeEvent> {
113 let mut out = Vec::new();
114
115 match msg {
116 Message::Hello { node } | Message::Heartbeat { node } => {
117 if self.state.peers.insert(node) {
118 out.push(RuntimeEvent::PeerDiscovered(node));
119 }
120 }
121 Message::Proposal(p) => {
122 let id = p.id;
123 self.state.proposals.entry(id).or_insert(p);
124 self.state.decisions.entry(id).or_insert(Decision::Pending);
125 out.push(RuntimeEvent::ProposalReceived(id));
126 }
127 Message::Vote { from: _, vote } => {
128 let pid = vote.proposal_id;
129 self.state.votes.entry(pid).or_default().push(vote);
130 out.push(RuntimeEvent::VoteReceived(pid));
131 }
132 }
133
134 self.recompute_decisions(&mut out);
135 out
136 }
137
138 fn recompute_decisions(&mut self, out: &mut Vec<RuntimeEvent>) {
139 let eligible = self.state.peers.len().max(1);
143
144 let proposal_ids: Vec<ProposalId> = self.state.proposals.keys().copied().collect();
145 for pid in proposal_ids {
146 let votes = self
147 .state
148 .votes
149 .get(&pid)
150 .map(|v| v.as_slice())
151 .unwrap_or(&[]);
152 let decision = self.vote_rule.decide(pid, votes, eligible);
153
154 let prev = self.state.decisions.get(&pid).copied();
155 if prev != Some(decision) {
156 self.state.decisions.insert(pid, decision);
157 out.push(RuntimeEvent::DecisionUpdated {
158 proposal_id: pid,
159 decision,
160 });
161 }
162 }
163 }
164}