commonware_broadcast/linked/signer/
tip_manager.rs1use crate::linked::parsed;
2use commonware_cryptography::Scheme;
3use commonware_utils::Array;
4use std::collections::{hash_map::Entry, HashMap};
5
6#[derive(Default, Debug)]
8pub struct TipManager<C: Scheme, D: Array> {
9 tips: HashMap<C::PublicKey, parsed::Node<C, D>>,
15}
16
17impl<C: Scheme, D: Array> TipManager<C, D> {
18 pub fn new() -> Self {
20 Self {
21 tips: HashMap::new(),
22 }
23 }
24
25 pub fn put(&mut self, node: &parsed::Node<C, D>) -> bool {
28 match self.tips.entry(node.chunk.sequencer.clone()) {
29 Entry::Vacant(e) => {
30 e.insert(node.clone());
31 true
32 }
33 Entry::Occupied(mut e) => {
34 let old = e.get();
35 if old.chunk.height > node.chunk.height {
36 panic!("Attempted to insert a lower-height tip");
37 }
38 if old.chunk.height == node.chunk.height {
39 assert!(
40 old.chunk.payload == node.chunk.payload,
41 "New tip has the same height but a different payload"
42 );
43 return false;
44 }
45 e.insert(node.clone());
46 true
47 }
48 }
49 }
50
51 pub fn get(&self, sequencer: &C::PublicKey) -> Option<parsed::Node<C, D>> {
53 self.tips.get(sequencer).cloned()
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60 use crate::linked::parsed;
61 use bytes::Bytes;
62 use commonware_cryptography::{
63 ed25519::{self, Ed25519, PublicKey, Signature},
64 sha256::{self, Digest},
65 };
66 use commonware_utils::Array;
67 use commonware_utils::SizedSerialize;
68 use rand::SeedableRng;
69
70 mod helpers {
72 use super::*;
73
74 pub fn create_dummy_node(
76 sequencer: PublicKey,
77 height: u64,
78 payload: &str,
79 ) -> parsed::Node<Ed25519, Digest> {
80 let signature = {
81 let mut data = Bytes::from(vec![3u8; Signature::SERIALIZED_LEN]);
82 Signature::read_from(&mut data).unwrap()
83 };
84 parsed::Node::<Ed25519, Digest> {
85 chunk: parsed::Chunk {
86 sequencer,
87 height,
88 payload: sha256::hash(payload.as_bytes()),
89 },
90 signature,
91 parent: None,
92 }
93 }
94
95 pub fn deterministic_public_key(seed: u64) -> ed25519::PublicKey {
97 let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
98 ed25519::Ed25519::new(&mut rng).public_key()
99 }
100
101 pub fn insert_tip(
103 manager: &mut TipManager<Ed25519, Digest>,
104 key: ed25519::PublicKey,
105 height: u64,
106 payload: &str,
107 ) -> parsed::Node<Ed25519, Digest> {
108 let node = create_dummy_node(key.clone(), height, payload);
109 manager.put(&node);
110 node
111 }
112 }
113
114 #[test]
116 fn test_put_new_tip() {
117 let mut manager = TipManager::<Ed25519, Digest>::new();
118 let key = helpers::deterministic_public_key(1);
119 let node = helpers::create_dummy_node(key.clone(), 1, "payload");
120 assert!(manager.put(&node));
121 let got = manager.get(&key).unwrap();
122 assert_eq!(got.chunk, node.chunk);
123 assert_eq!(got.signature, node.signature);
124 assert_eq!(got.parent, node.parent);
125 }
126
127 #[test]
129 fn test_put_same_height_same_payload() {
130 let mut manager = TipManager::<Ed25519, Digest>::new();
131 let key = helpers::deterministic_public_key(2);
132 let node = helpers::create_dummy_node(key.clone(), 1, "payload");
133 assert!(manager.put(&node));
134 assert!(!manager.put(&node));
135 let got = manager.get(&key).unwrap();
136 assert_eq!(got.chunk, node.chunk);
137 assert_eq!(got.signature, node.signature);
138 assert_eq!(got.parent, node.parent);
139 }
140
141 #[test]
143 fn test_put_higher_tip() {
144 let mut manager = TipManager::<Ed25519, Digest>::new();
145 let key = helpers::deterministic_public_key(3);
146 let node1 = helpers::create_dummy_node(key.clone(), 1, "payload1");
147 assert!(manager.put(&node1));
148 let node2 = helpers::create_dummy_node(key.clone(), 2, "payload2");
149 assert!(manager.put(&node2));
150 let got = manager.get(&key).unwrap();
151 assert_eq!(got.chunk, node2.chunk);
152 assert_eq!(got.signature, node2.signature);
153 assert_eq!(got.parent, node2.parent);
154 }
155
156 #[test]
158 #[should_panic(expected = "Attempted to insert a lower-height tip")]
159 fn test_put_lower_tip_panics() {
160 let mut manager = TipManager::<Ed25519, Digest>::new();
161 let key = helpers::deterministic_public_key(4);
162 let node1 = helpers::create_dummy_node(key.clone(), 2, "payload");
163 assert!(manager.put(&node1));
164 let node2 = helpers::create_dummy_node(key.clone(), 1, "payload");
165 manager.put(&node2);
166 }
167
168 #[test]
170 #[should_panic]
171 fn test_put_same_height_different_payload_panics() {
172 let mut manager = TipManager::<Ed25519, Digest>::new();
173 let key = helpers::deterministic_public_key(5);
174 let node1 = helpers::create_dummy_node(key.clone(), 1, "payload1");
175 assert!(manager.put(&node1));
176 let node2 = helpers::create_dummy_node(key.clone(), 1, "payload2");
177 manager.put(&node2);
178 }
179
180 #[test]
182 fn test_get_nonexistent() {
183 let manager = TipManager::<Ed25519, Digest>::new();
184 let key = helpers::deterministic_public_key(6);
185 assert!(manager.get(&key).is_none());
186 }
187
188 #[test]
190 fn test_multiple_sequencers() {
191 let mut manager = TipManager::<Ed25519, Digest>::new();
192 let key1 = helpers::deterministic_public_key(10);
193 let key2 = helpers::deterministic_public_key(20);
194 let node1 = helpers::insert_tip(&mut manager, key1.clone(), 1, "payload1");
195 let node2 = helpers::insert_tip(&mut manager, key2.clone(), 2, "payload2");
196
197 let got1 = manager.get(&key1).unwrap();
198 let got2 = manager.get(&key2).unwrap();
199 assert_eq!(got1.chunk, node1.chunk);
200 assert_eq!(got2.chunk, node2.chunk);
201 }
202
203 #[test]
205 fn test_put_multiple_updates() {
206 let mut manager = TipManager::<Ed25519, Digest>::new();
207 let key = helpers::deterministic_public_key(7);
208
209 let node1 = helpers::insert_tip(&mut manager, key.clone(), 1, "payload1");
211 let got1 = manager.get(&key).unwrap();
212 assert_eq!(got1.chunk.height, 1);
213 assert_eq!(got1.chunk.payload, node1.chunk.payload);
214
215 let node2 = helpers::insert_tip(&mut manager, key.clone(), 2, "payload2");
217 let got2 = manager.get(&key).unwrap();
218 assert_eq!(got2.chunk.height, 2);
219 assert_eq!(got2.chunk.payload, node2.chunk.payload);
220
221 let node3 = helpers::insert_tip(&mut manager, key.clone(), 3, "payload3");
223 let got3 = manager.get(&key).unwrap();
224 assert_eq!(got3.chunk.height, 3);
225 assert_eq!(got3.chunk.payload, node3.chunk.payload);
226
227 assert!(!manager.put(&node3));
229
230 let node4 = helpers::insert_tip(&mut manager, key.clone(), 4, "payload4");
232 let got4 = manager.get(&key).unwrap();
233 assert_eq!(got4.chunk.height, 4);
234 assert_eq!(got4.chunk.payload, node4.chunk.payload);
235 }
236}