Skip to main content

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}