tsoracle_core/peer.rs
1//
2// ░▀█▀░█▀▀░█▀█░█▀▄░█▀█░█▀▀░█░░░█▀▀
3// ░░█░░▀▀█░█░█░█▀▄░█▀█░█░░░█░░░█▀▀
4// ░░▀░░▀▀▀░▀▀▀░▀░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀
5//
6// tsoracle — Distributed Timestamp Oracle
7// https://www.tsoracle.rs
8//
9// Copyright (c) 2026 Prisma Risk
10//
11// Licensed under the Apache License, Version 2.0 (the "License");
12// you may not use this file except in compliance with the License.
13// You may obtain a copy of the License at
14//
15// https://www.apache.org/licenses/LICENSE-2.0
16//
17// Unless required by applicable law or agreed to in writing, software
18// distributed under the License is distributed on an "AS IS" BASIS,
19// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20// See the License for the specific language governing permissions and
21// limitations under the License.
22//
23
24//! A consensus peer and the tsoracle service endpoint it advertises.
25
26/// A known peer node and the network endpoint where its TSO service can be
27/// reached for follower-redirect hints.
28///
29/// `node_id` is the consensus node identity (the OmniPaxos `NodeId`/pid or the
30/// openraft `NodeId`). `endpoint` is the advertised tsoracle service address
31/// surfaced via `LeaderState::Follower::leader_endpoint` when this peer is the
32/// elected leader. This is the cross-backend shape consensus drivers share so
33/// the `leader_endpoint` derivation reads the same regardless of the engine
34/// underneath.
35#[derive(Clone, Debug, PartialEq, Eq, Hash)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37pub struct TsoPeer {
38 pub node_id: u64,
39 pub endpoint: String,
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 #[test]
47 fn carries_node_id_and_endpoint() {
48 let peer = TsoPeer {
49 node_id: 2,
50 endpoint: "http://node-2:50051".to_string(),
51 };
52 assert_eq!(peer.node_id, 2);
53 assert_eq!(peer.endpoint, "http://node-2:50051");
54 }
55
56 #[test]
57 fn equality_is_by_node_id_and_endpoint() {
58 let left = TsoPeer {
59 node_id: 1,
60 endpoint: "http://a".to_string(),
61 };
62 let same = TsoPeer {
63 node_id: 1,
64 endpoint: "http://a".to_string(),
65 };
66 let different_endpoint = TsoPeer {
67 node_id: 1,
68 endpoint: "http://b".to_string(),
69 };
70 assert_eq!(left, same);
71 assert_ne!(left, different_endpoint);
72 }
73
74 #[test]
75 fn is_usable_as_a_hash_set_member() {
76 use std::collections::HashSet;
77
78 let mut peers = HashSet::new();
79 peers.insert(TsoPeer {
80 node_id: 1,
81 endpoint: "http://a".to_string(),
82 });
83 let duplicate = peers.insert(TsoPeer {
84 node_id: 1,
85 endpoint: "http://a".to_string(),
86 });
87 assert!(!duplicate, "an equal peer must hash to the same bucket");
88 assert_eq!(peers.len(), 1);
89 }
90}