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