1use solana_sdk::{
12 instruction::{AccountMeta, Instruction},
13 pubkey::Pubkey,
14};
15
16pub const IX_INIT_TREE: u8 = 0;
18pub const IX_INSERT: u8 = 2;
19pub const IX_FIND: u8 = 3;
20pub const IX_DELETE: u8 = 8;
21pub const IX_INSERT_FAST: u8 = 16;
22pub const IX_UPDATE_FAST: u8 = 17;
23pub const IX_DELETE_FAST: u8 = 18;
24
25pub const ERR_NEED_SPLIT_SLOT: u32 = 102; pub const ERR_DUPLICATE_KEY: u32 = 103;
28pub const ERR_KEY_NOT_FOUND: u32 = 104;
29pub const ERR_BAD_PATH: u32 = 105; pub const KEY_SIZE: usize = 32;
33pub const NODE_HDR: usize = 44;
34pub const TREE_HEADER_SIZE: usize = 146;
35pub const ALLOC_SIZE: usize = 32;
36
37const H_VALUE_SIZE: usize = 46;
39const H_FANOUT: usize = 48;
40const H_NODE_SIZE: usize = 50;
41const H_ROOT: usize = 54;
42const H_HEIGHT: usize = 62;
43const H_LEFTMOST: usize = 66;
44const H_RIGHTMOST: usize = 74;
45const H_EPOCH: usize = 82;
46const H_AUTHORITY: usize = 90;
47const N_KEY_COUNT: usize = 2;
49const N_NEXT_LEAF: usize = 20;
50const A_HIGH_WATER: usize = 8;
52
53fn rd_u16(d: &[u8], o: usize) -> u16 { u16::from_le_bytes(d[o..o + 2].try_into().unwrap()) }
54fn rd_u32(d: &[u8], o: usize) -> u32 { u32::from_le_bytes(d[o..o + 4].try_into().unwrap()) }
55fn rd_u64(d: &[u8], o: usize) -> u64 { u64::from_le_bytes(d[o..o + 8].try_into().unwrap()) }
56
57pub trait AccountReader {
59 fn account_data(&self, key: &Pubkey) -> Option<Vec<u8>>;
60}
61
62#[derive(Clone, Copy, Debug)]
64pub struct Header {
65 pub value_size: u16,
66 pub fanout: u16,
67 pub node_size: u32,
68 pub root: u64,
69 pub height: u32,
70 pub leftmost: u64,
71 pub rightmost: u64,
72 pub structure_epoch: u64,
75 pub authority: Pubkey,
76}
77
78impl Header {
79 fn parse(d: &[u8]) -> Header {
80 Header {
81 value_size: rd_u16(d, H_VALUE_SIZE),
82 fanout: rd_u16(d, H_FANOUT),
83 node_size: rd_u32(d, H_NODE_SIZE),
84 root: rd_u64(d, H_ROOT),
85 height: rd_u32(d, H_HEIGHT),
86 leftmost: rd_u64(d, H_LEFTMOST),
87 rightmost: rd_u64(d, H_RIGHTMOST),
88 structure_epoch: rd_u64(d, H_EPOCH),
89 authority: Pubkey::new_from_array(d[H_AUTHORITY..H_AUTHORITY + 32].try_into().unwrap()),
90 }
91 }
92}
93
94#[derive(Clone, Copy, Debug)]
96pub struct Tree {
97 pub program: Pubkey,
98 pub creator: Pubkey,
99 pub tree_id: u32,
100}
101
102impl Tree {
103 pub fn new(program: Pubkey, creator: Pubkey, tree_id: u32) -> Self {
104 Tree { program, creator, tree_id }
105 }
106
107 pub fn header_pda(&self) -> (Pubkey, u8) {
108 Pubkey::find_program_address(&[b"thdr", self.creator.as_ref(), &self.tree_id.to_le_bytes()], &self.program)
109 }
110 pub fn alloc_pda(&self) -> (Pubkey, u8) {
111 Pubkey::find_program_address(&[b"talloc", self.creator.as_ref(), &self.tree_id.to_le_bytes()], &self.program)
112 }
113 pub fn node_pda(&self, idx: u64) -> (Pubkey, u8) {
114 Pubkey::find_program_address(
115 &[b"tnode", self.creator.as_ref(), &self.tree_id.to_le_bytes(), &idx.to_le_bytes()], &self.program)
116 }
117
118 pub fn header(&self, r: &dyn AccountReader) -> Option<Header> {
119 r.account_data(&self.header_pda().0).map(|d| Header::parse(&d))
120 }
121 fn high_water(&self, r: &dyn AccountReader) -> Option<u64> {
122 r.account_data(&self.alloc_pda().0).map(|d| rd_u64(&d, A_HIGH_WATER))
123 }
124
125 pub fn path(&self, r: &dyn AccountReader, key: &[u8; 32]) -> Option<Vec<u64>> {
128 let h = self.header(r)?;
129 if h.height == 0 { return Some(vec![]); }
130 let f = h.fanout as usize;
131 let kids_off = NODE_HDR + (f + 1) * KEY_SIZE;
132 let mut cur = h.root;
133 let mut path = vec![cur];
134 for _ in 0..h.height - 1 {
135 let d = r.account_data(&self.node_pda(cur).0)?;
136 let cnt = rd_u16(&d, N_KEY_COUNT) as usize;
137 let (mut lo, mut hi) = (0usize, cnt);
139 while lo < hi {
140 let m = (lo + hi) / 2;
141 let mk = &d[NODE_HDR + m * KEY_SIZE..NODE_HDR + m * KEY_SIZE + KEY_SIZE];
142 if mk < &key[..] { lo = m + 1; } else { hi = m; }
143 }
144 let eq = lo < cnt && &d[NODE_HDR + lo * KEY_SIZE..NODE_HDR + lo * KEY_SIZE + KEY_SIZE] == &key[..];
145 let slot = if eq { lo + 1 } else { lo };
146 cur = rd_u64(&d, kids_off + slot * 8);
147 path.push(cur);
148 }
149 Some(path)
150 }
151
152 fn path_metas(&self, path: &[u64], leaf_writable: bool) -> Vec<AccountMeta> {
153 path.iter().enumerate().map(|(i, &n)| {
154 let pk = self.node_pda(n).0;
155 if leaf_writable && i == path.len() - 1 { AccountMeta::new(pk, false) }
156 else { AccountMeta::new_readonly(pk, false) }
157 }).collect()
158 }
159
160 pub fn insert_fast_ix(&self, r: &dyn AccountReader, authority: Pubkey, key: &[u8; 32], value: &[u8]) -> Option<Instruction> {
165 let path = self.path(r, key)?;
166 let mut data = vec![IX_INSERT_FAST];
167 data.extend_from_slice(key); data.extend_from_slice(value); data.push(path.len() as u8);
168 Some(self.fast_ix(authority, data, &path))
169 }
170 pub fn update_fast_ix(&self, r: &dyn AccountReader, authority: Pubkey, key: &[u8; 32], value: &[u8]) -> Option<Instruction> {
172 let path = self.path(r, key)?;
173 let mut data = vec![IX_UPDATE_FAST];
174 data.extend_from_slice(key); data.extend_from_slice(value); data.push(path.len() as u8);
175 Some(self.fast_ix(authority, data, &path))
176 }
177 pub fn delete_fast_ix(&self, r: &dyn AccountReader, authority: Pubkey, key: &[u8; 32]) -> Option<Instruction> {
179 let path = self.path(r, key)?;
180 let mut data = vec![IX_DELETE_FAST];
181 data.extend_from_slice(key); data.push(path.len() as u8);
182 Some(self.fast_ix(authority, data, &path))
183 }
184 fn fast_ix(&self, authority: Pubkey, data: Vec<u8>, path: &[u64]) -> Instruction {
185 let mut metas = vec![
186 AccountMeta::new_readonly(self.header_pda().0, false),
187 AccountMeta::new_readonly(authority, true),
188 ];
189 metas.extend(self.path_metas(path, true));
190 Instruction::new_with_bytes(self.program, &data, metas)
191 }
192
193 pub fn find_ix(&self, r: &dyn AccountReader, key: &[u8; 32]) -> Option<Instruction> {
195 let path = self.path(r, key)?;
196 let mut data = vec![IX_FIND]; data.extend_from_slice(key); data.push(path.len() as u8);
197 let mut metas = vec![AccountMeta::new_readonly(self.header_pda().0, false)];
198 metas.extend(self.path_metas(&path, false));
199 Some(Instruction::new_with_bytes(self.program, &data, metas))
200 }
201
202 pub fn insert_ix(&self, r: &dyn AccountReader, payer: Pubkey, key: &[u8; 32], value: &[u8], rent_node: u64) -> Option<Instruction> {
208 let h = self.header(r)?;
209 let hw = self.high_water(r)?;
210 let path = self.path(r, key)?;
211 let spare_n = h.height as usize + 2;
212 let mut data = vec![IX_INSERT];
213 data.extend_from_slice(key); data.extend_from_slice(value);
214 data.push(path.len() as u8); data.push(spare_n as u8);
215 data.extend_from_slice(&rent_node.to_le_bytes());
216 let mut spares = Vec::with_capacity(spare_n);
217 for i in 0..spare_n as u64 {
218 let (pk, b) = self.node_pda(hw + 1 + i);
219 data.push(b); spares.push(pk);
220 }
221 let mut metas = vec![
222 AccountMeta::new(self.header_pda().0, false),
223 AccountMeta::new(payer, true),
224 AccountMeta::new(self.alloc_pda().0, false),
225 AccountMeta::new_readonly(Pubkey::default(), false),
226 ];
227 metas.extend(self.path_metas(&path, false).into_iter().map(|m| AccountMeta::new(m.pubkey, false)));
228 for s in spares { metas.push(AccountMeta::new(s, false)); }
229 Some(Instruction::new_with_bytes(self.program, &data, metas))
230 }
231
232 pub fn delete_ix(&self, r: &dyn AccountReader, payer: Pubkey, key: &[u8; 32]) -> Option<Instruction> {
237 let h = self.header(r)?;
238 if h.height == 0 { return None; }
239 let path = self.path(r, key)?;
240 let height = path.len();
241 let ko = NODE_HDR + (h.fanout as usize + 1) * KEY_SIZE;
242 let mut sides = vec![0u8; height];
243 let mut sib_idxs: Vec<u64> = Vec::new();
244 for level in 1..height {
245 let node_idx = path[level];
246 let pd = r.account_data(&self.node_pda(path[level - 1]).0)?;
247 let pcnt = rd_u16(&pd, N_KEY_COUNT) as usize;
248 let kid = |i: usize| rd_u64(&pd, ko + i * 8);
249 let mut our = 0usize;
250 for i in 0..=pcnt { if kid(i) == node_idx { our = i; break; } }
251 if our < pcnt { sides[level] = 1; sib_idxs.push(kid(our + 1)); } else { sides[level] = 2; sib_idxs.push(kid(our - 1)); } }
254 let mut data = vec![IX_DELETE];
255 data.extend_from_slice(key);
256 data.push(height as u8);
257 data.extend_from_slice(&sides);
258 let mut metas = vec![
259 AccountMeta::new(self.header_pda().0, false),
260 AccountMeta::new(payer, true),
261 ];
262 for &n in &path { metas.push(AccountMeta::new(self.node_pda(n).0, false)); }
263 for &s in &sib_idxs { metas.push(AccountMeta::new(self.node_pda(s).0, false)); }
264 Some(Instruction::new_with_bytes(self.program, &data, metas))
265 }
266
267 pub fn cold_plan(&self, r: &dyn AccountReader, key: &[u8; 32]) -> Option<(Vec<u64>, Vec<(Pubkey, u8)>)> {
271 let h = self.header(r)?;
272 let hw = self.high_water(r)?;
273 let path = self.path(r, key)?;
274 let spare_n = h.height as u64 + 2;
275 let spares = (0..spare_n).map(|i| self.node_pda(hw + 1 + i)).collect();
276 Some((path, spares))
277 }
278
279 pub fn init_tree_ix(&self, payer: Pubkey, value_size: u16, fanout: u16, rent_hdr: u64, rent_alloc: u64) -> Instruction {
281 let (hdr, hb) = self.header_pda();
282 let (alc, ab) = self.alloc_pda();
283 let mut data = vec![IX_INIT_TREE];
284 data.extend_from_slice(&self.tree_id.to_le_bytes());
285 data.push(hb); data.push(ab);
286 data.extend_from_slice(&value_size.to_le_bytes());
287 data.extend_from_slice(&fanout.to_le_bytes());
288 data.extend_from_slice(&rent_hdr.to_le_bytes());
289 data.extend_from_slice(&rent_alloc.to_le_bytes());
290 Instruction::new_with_bytes(self.program, &data, vec![
291 AccountMeta::new(payer, true),
292 AccountMeta::new(hdr, false),
293 AccountMeta::new(alc, false),
294 AccountMeta::new_readonly(Pubkey::default(), false),
295 ])
296 }
297
298 pub fn scan(&self, r: &dyn AccountReader, max: usize) -> Vec<([u8; 32], Vec<u8>)> {
304 let h = match self.header(r) { Some(h) if h.height > 0 => h, _ => return vec![] };
305 let (f, vs) = (h.fanout as usize, h.value_size as usize);
306 let voff = NODE_HDR + (f + 1) * KEY_SIZE;
307 let mut idx = h.leftmost;
308 let mut out = Vec::new();
309 while idx != 0 && out.len() < max {
310 let d = match r.account_data(&self.node_pda(idx).0) { Some(d) => d, None => break };
311 let cnt = rd_u16(&d, N_KEY_COUNT) as usize;
312 for i in 0..cnt {
313 if out.len() >= max { break; }
314 let key: [u8; 32] = d[NODE_HDR + i * KEY_SIZE..NODE_HDR + i * KEY_SIZE + KEY_SIZE].try_into().unwrap();
315 let val = d[voff + i * vs..voff + i * vs + vs].to_vec();
316 out.push((key, val));
317 }
318 idx = rd_u64(&d, N_NEXT_LEAF);
319 }
320 out
321 }
322
323 pub fn best(&self, r: &dyn AccountReader) -> Option<([u8; 32], Vec<u8>)> {
325 self.scan(r, 1).into_iter().next()
326 }
327
328 pub fn get(&self, r: &dyn AccountReader, key: &[u8; 32]) -> Option<Vec<u8>> {
330 let h = self.header(r)?;
331 if h.height == 0 { return None; }
332 let leaf = *self.path(r, key)?.last()?;
333 let d = r.account_data(&self.node_pda(leaf).0)?;
334 let (f, vs) = (h.fanout as usize, h.value_size as usize);
335 let cnt = rd_u16(&d, N_KEY_COUNT) as usize;
336 let (mut lo, mut hi) = (0usize, cnt);
337 while lo < hi {
338 let m = (lo + hi) / 2;
339 if &d[NODE_HDR + m * KEY_SIZE..NODE_HDR + m * KEY_SIZE + KEY_SIZE] < &key[..] { lo = m + 1; } else { hi = m; }
340 }
341 if lo < cnt && &d[NODE_HDR + lo * KEY_SIZE..NODE_HDR + lo * KEY_SIZE + KEY_SIZE] == &key[..] {
342 let voff = NODE_HDR + (f + 1) * KEY_SIZE;
343 Some(d[voff + lo * vs..voff + lo * vs + vs].to_vec())
344 } else { None }
345 }
346
347 pub fn scan_accounts(&self, r: &dyn AccountReader, max_entries: usize) -> Vec<Pubkey> {
352 let h = match self.header(r) { Some(h) if h.height > 0 => h, _ => return vec![] };
353 let mut out = vec![self.header_pda().0];
354 let (mut idx, mut seen) = (h.leftmost, 0usize);
355 while idx != 0 && seen < max_entries {
356 let pk = self.node_pda(idx).0;
357 out.push(pk);
358 let d = match r.account_data(&pk) { Some(d) => d, None => break };
359 seen += rd_u16(&d, N_KEY_COUNT) as usize;
360 idx = rd_u64(&d, N_NEXT_LEAF);
361 }
362 out
363 }
364}
365
366pub enum Attempt<T, E> {
368 Done(T), Stale, Fatal(E), }
372
373pub fn retry<T, E>(attempts: u32, mut f: impl FnMut() -> Attempt<T, E>) -> Option<Result<T, E>> {
380 for _ in 0..attempts {
381 match f() {
382 Attempt::Done(t) => return Some(Ok(t)),
383 Attempt::Fatal(e) => return Some(Err(e)),
384 Attempt::Stale => continue,
385 }
386 }
387 None
388}
389
390pub mod keys {
398 use solana_sdk::pubkey::Pubkey;
399
400 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
401 pub enum Side { Ask, Bid }
402
403 pub fn order_key(side: Side, price: u64, slot: u64, maker: &Pubkey, nonce: u64) -> [u8; 32] {
404 let p = match side { Side::Ask => price, Side::Bid => u64::MAX - price };
405 let mut k = [0u8; 32];
406 k[0..8].copy_from_slice(&p.to_be_bytes());
407 k[8..16].copy_from_slice(&slot.to_be_bytes());
408 k[16..24].copy_from_slice(&maker.to_bytes()[0..8]);
409 k[24..32].copy_from_slice(&nonce.to_be_bytes());
410 k
411 }
412 pub fn price_of(side: Side, key: &[u8; 32]) -> u64 {
413 let p = u64::from_be_bytes(key[0..8].try_into().unwrap());
414 match side { Side::Ask => p, Side::Bid => u64::MAX - p }
415 }
416 pub fn slot_of(key: &[u8; 32]) -> u64 { u64::from_be_bytes(key[8..16].try_into().unwrap()) }
417}
418
419#[cfg(test)]
420mod tests {
421 use super::keys::*;
422 use solana_sdk::pubkey::Pubkey;
423
424 #[test]
425 fn order_key_price_time_priority() {
426 let m = Pubkey::new_unique();
427 assert!(order_key(Side::Ask, 100, 5, &m, 0) < order_key(Side::Ask, 200, 5, &m, 0));
429 assert!(order_key(Side::Bid, 200, 5, &m, 0) < order_key(Side::Bid, 100, 5, &m, 0));
431 assert!(order_key(Side::Ask, 100, 1, &m, 0) < order_key(Side::Ask, 100, 9, &m, 0));
433 let a = order_key(Side::Ask, 12345, 7, &m, 3);
435 assert_eq!(price_of(Side::Ask, &a), 12345);
436 assert_eq!(slot_of(&a), 7);
437 assert_eq!(price_of(Side::Bid, &order_key(Side::Bid, 999, 0, &m, 0)), 999);
438 }
439
440 #[test]
441 fn retry_resolves_after_stale() {
442 use super::{retry, Attempt};
443 let mut n = 0;
445 let r: Option<Result<i32, ()>> = retry(5, || { n += 1; if n < 3 { Attempt::Stale } else { Attempt::Done(n) } });
446 assert_eq!(r, Some(Ok(3)));
447 let r: Option<Result<(), &str>> = retry(5, || Attempt::Fatal("boom"));
449 assert_eq!(r, Some(Err("boom")));
450 let r: Option<Result<(), ()>> = retry(2, || Attempt::Stale);
452 assert!(r.is_none());
453 }
454}
455