Skip to main content

tsoracle_core/
peer.rs

1//
2//  ░▀█▀░█▀▀░█▀█░█▀▄░█▀█░█▀▀░█░░░█▀▀
3//  ░░█░░▀▀█░█░█░█▀▄░█▀█░█░░░█░░░█▀▀
4//  ░░▀░░▀▀▀░▀▀▀░▀░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀
5//
6//  tsoracle — Distributed Timestamp Oracle
7//
8//  Copyright (c) 2026 Prisma Risk
9//  Licensed under the Apache License, Version 2.0
10//  https://github.com/prisma-risk/tsoracle
11//
12
13//! A consensus peer and the tsoracle service endpoint it advertises.
14
15/// A known peer node and the network endpoint where its TSO service can be
16/// reached for follower-redirect hints.
17///
18/// `node_id` is the consensus node identity (the OmniPaxos `NodeId`/pid or the
19/// openraft `NodeId`). `endpoint` is the advertised tsoracle service address
20/// surfaced via `LeaderState::Follower::leader_endpoint` when this peer is the
21/// elected leader. This is the cross-backend shape consensus drivers share so
22/// the `leader_endpoint` derivation reads the same regardless of the engine
23/// underneath.
24#[derive(Clone, Debug, PartialEq, Eq, Hash)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub struct TsoPeer {
27    pub node_id: u64,
28    pub endpoint: String,
29}
30
31#[cfg(test)]
32mod tests {
33    use super::*;
34
35    #[test]
36    fn carries_node_id_and_endpoint() {
37        let peer = TsoPeer {
38            node_id: 2,
39            endpoint: "http://node-2:50051".to_string(),
40        };
41        assert_eq!(peer.node_id, 2);
42        assert_eq!(peer.endpoint, "http://node-2:50051");
43    }
44
45    #[test]
46    fn equality_is_by_node_id_and_endpoint() {
47        let left = TsoPeer {
48            node_id: 1,
49            endpoint: "http://a".to_string(),
50        };
51        let same = TsoPeer {
52            node_id: 1,
53            endpoint: "http://a".to_string(),
54        };
55        let different_endpoint = TsoPeer {
56            node_id: 1,
57            endpoint: "http://b".to_string(),
58        };
59        assert_eq!(left, same);
60        assert_ne!(left, different_endpoint);
61    }
62
63    #[test]
64    fn is_usable_as_a_hash_set_member() {
65        use std::collections::HashSet;
66
67        let mut peers = HashSet::new();
68        peers.insert(TsoPeer {
69            node_id: 1,
70            endpoint: "http://a".to_string(),
71        });
72        let duplicate = peers.insert(TsoPeer {
73            node_id: 1,
74            endpoint: "http://a".to_string(),
75        });
76        assert!(!duplicate, "an equal peer must hash to the same bucket");
77        assert_eq!(peers.len(), 1);
78    }
79}