1use super::peer_id::from_core_peer_id;
23use log::{debug, info};
24use mixnet::core::{
25 Mixnet, Mixnode as CoreMixnode, MixnodesErr as CoreMixnodesErr, RelSessionIndex,
26 SessionPhase as CoreSessionPhase, SessionStatus as CoreSessionStatus,
27};
28use sc_network_types::{
29 multiaddr::{multiaddr, Multiaddr, Protocol},
30 PeerId,
31};
32use sp_api::{ApiError, ApiRef};
33use sp_mixnet::{
34 runtime_api::MixnetApi,
35 types::{
36 Mixnode as RuntimeMixnode, MixnodesErr as RuntimeMixnodesErr,
37 SessionPhase as RuntimeSessionPhase, SessionStatus as RuntimeSessionStatus,
38 },
39};
40use sp_runtime::traits::Block;
41
42const LOG_TARGET: &str = "mixnet";
43
44fn to_core_session_status(status: RuntimeSessionStatus) -> CoreSessionStatus {
50 CoreSessionStatus {
51 current_index: status.current_index,
52 phase: match status.phase {
53 RuntimeSessionPhase::CoverToCurrent => CoreSessionPhase::CoverToCurrent,
54 RuntimeSessionPhase::RequestsToCurrent => CoreSessionPhase::RequestsToCurrent,
55 RuntimeSessionPhase::CoverToPrev => CoreSessionPhase::CoverToPrev,
56 RuntimeSessionPhase::DisconnectFromPrev => CoreSessionPhase::DisconnectFromPrev,
57 },
58 }
59}
60
61fn parse_external_addresses(external_addresses: Vec<Vec<u8>>) -> Vec<Multiaddr> {
62 external_addresses
63 .into_iter()
64 .flat_map(|addr| {
65 let addr = match String::from_utf8(addr) {
66 Ok(addr) => addr,
67 Err(addr) => {
68 debug!(
69 target: LOG_TARGET,
70 "Mixnode external address {:x?} is not valid UTF-8",
71 addr.into_bytes(),
72 );
73 return None
74 },
75 };
76 match addr.parse() {
77 Ok(addr) => Some(addr),
78 Err(err) => {
79 debug!(
80 target: LOG_TARGET,
81 "Could not parse mixnode address {addr}: {err}",
82 );
83 None
84 },
85 }
86 })
87 .collect()
88}
89
90fn fixup_external_addresses(external_addresses: &mut Vec<Multiaddr>, peer_id: &PeerId) {
93 external_addresses.retain_mut(|addr| match PeerId::try_from_multiaddr(addr) {
95 Some(addr_peer_id) if addr_peer_id == *peer_id => true,
96 Some(_) => {
97 debug!(
98 target: LOG_TARGET,
99 "Mixnode address {} does not match mixnode peer ID {}, ignoring",
100 addr,
101 peer_id
102 );
103 false
104 },
105 None if matches!(addr.iter().last(), Some(Protocol::P2p(_))) => {
106 debug!(
107 target: LOG_TARGET,
108 "Mixnode address {} has unrecognised P2P protocol, ignoring",
109 addr
110 );
111 false
112 },
113 None => {
114 addr.push(Protocol::P2p(*peer_id.as_ref()));
115 true
116 },
117 });
118
119 if external_addresses.is_empty() {
121 external_addresses.push(multiaddr!(P2p(*peer_id.as_ref())));
122 }
123}
124
125fn into_core_mixnode(mixnode: RuntimeMixnode) -> CoreMixnode<Vec<Multiaddr>> {
133 let external_addresses = if let Some(peer_id) = from_core_peer_id(&mixnode.peer_id) {
134 let mut external_addresses = parse_external_addresses(mixnode.external_addresses);
135 fixup_external_addresses(&mut external_addresses, &peer_id);
136 external_addresses
137 } else {
138 debug!(
139 target: LOG_TARGET,
140 "Failed to convert mixnet peer ID {:x?} to libp2p peer ID",
141 mixnode.peer_id,
142 );
143 Vec::new()
144 };
145
146 CoreMixnode {
147 kx_public: mixnode.kx_public,
148 peer_id: mixnode.peer_id,
149 extra: external_addresses,
150 }
151}
152
153fn maybe_set_mixnodes(
154 mixnet: &mut Mixnet<Vec<Multiaddr>>,
155 rel_session_index: RelSessionIndex,
156 mixnodes: &dyn Fn() -> Result<Result<Vec<RuntimeMixnode>, RuntimeMixnodesErr>, ApiError>,
157) {
158 let current_session_index = mixnet.session_status().current_index;
159 mixnet.maybe_set_mixnodes(rel_session_index, &mut || {
160 let session_index = rel_session_index + current_session_index;
163 match mixnodes() {
164 Ok(Ok(mixnodes)) => Ok(mixnodes.into_iter().map(into_core_mixnode).collect()),
165 Ok(Err(err)) => {
166 info!(target: LOG_TARGET, "Session {session_index}: Mixnet disabled: {err}");
167 Err(CoreMixnodesErr::Permanent) },
169 Err(err) => {
170 debug!(
171 target: LOG_TARGET,
172 "Session {session_index}: Error getting mixnodes from runtime: {err}"
173 );
174 Err(CoreMixnodesErr::Transient) },
176 }
177 });
178}
179
180pub fn sync_with_runtime<B, A>(mixnet: &mut Mixnet<Vec<Multiaddr>>, api: ApiRef<A>, hash: B::Hash)
181where
182 B: Block,
183 A: MixnetApi<B>,
184{
185 let session_status = match api.session_status(hash) {
186 Ok(session_status) => session_status,
187 Err(err) => {
188 debug!(target: LOG_TARGET, "Error getting session status from runtime: {err}");
189 return
190 },
191 };
192 mixnet.set_session_status(to_core_session_status(session_status));
193
194 maybe_set_mixnodes(mixnet, RelSessionIndex::Prev, &|| api.prev_mixnodes(hash));
195 maybe_set_mixnodes(mixnet, RelSessionIndex::Current, &|| api.current_mixnodes(hash));
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn fixup_empty_external_addresses() {
204 let peer_id = PeerId::random();
205 let mut external_addresses = Vec::new();
206 fixup_external_addresses(&mut external_addresses, &peer_id);
207 assert_eq!(external_addresses, vec![multiaddr!(P2p(peer_id))]);
208 }
209
210 #[test]
211 fn fixup_misc_external_addresses() {
212 let peer_id = PeerId::random();
213 let other_peer_id = PeerId::random();
214 let mut external_addresses = vec![
215 multiaddr!(Tcp(0u16), P2p(peer_id)),
216 multiaddr!(Tcp(1u16), P2p(other_peer_id)),
217 multiaddr!(Tcp(2u16)),
218 Multiaddr::empty(),
219 ];
220 fixup_external_addresses(&mut external_addresses, &peer_id);
221 assert_eq!(
222 external_addresses,
223 vec![
224 multiaddr!(Tcp(0u16), P2p(peer_id)),
225 multiaddr!(Tcp(2u16), P2p(peer_id)),
226 multiaddr!(P2p(peer_id)),
227 ]
228 );
229 }
230}