diff --git a/program/Cargo.lock b/program/Cargo.lock index bdacd8f..0b0b680 100644 --- a/program/Cargo.lock +++ b/program/Cargo.lock @@ -56,7 +56,7 @@ dependencies = [ [[package]] name = "agnostic-orderbook" -version = "1.0.0" +version = "1.0.0-alpha.1" dependencies = [ "arrayref", "bonfida-utils", diff --git a/program/Cargo.toml b/program/Cargo.toml index d1e5b42..de0e50e 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "agnostic-orderbook" -version = "1.0.0" +version = "1.0.0-alpha.1" edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -14,6 +14,7 @@ quick-test = [] lib = [] utils = [] benchmarking = ["bonfida-utils/benchmarking"] +aarch64-test = [] [dependencies] solana-program = "1.8.0" diff --git a/program/src/state/critbit.rs b/program/src/state/critbit.rs index de84311..4a7d88b 100644 --- a/program/src/state/critbit.rs +++ b/program/src/state/critbit.rs @@ -39,7 +39,10 @@ pub struct Slab<'a, C> { #[repr(C)] pub struct LeafNode { /// The key is the associated order id + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] pub key: u128, + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + pub key: [u64; 2], /// The quantity of base asset associated with the underlying order pub base_quantity: u64, } @@ -49,12 +52,23 @@ impl LeafNode { /// Parse a leaf node's price pub fn price(&self) -> u64 { - Self::price_from_key(self.key) + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] + let res = Self::price_from_key(self.key); + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let res = Self::price_from_key(self.order_id()); + res } /// Get the associated order id pub fn order_id(&self) -> u128 { - self.key + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] + { + self.key + } + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + { + (self.key[0] as u128) + ((self.key[1] as u128) << 64) + } } /// Deduce an associated price from an order_id @@ -230,11 +244,18 @@ impl<'a, C> Slab<'a, C> { let shared_prefix_len = match Node::from_handle(root) { Node::Inner => { let root_node = &self.inner_nodes[(!root) as usize]; + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] let shared_prefix_len: u32 = (root_node.key ^ new_leaf.key).leading_zeros(); + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let shared_prefix_len: u32 = + (root_node.key ^ new_leaf.order_id()).leading_zeros(); let keep_old_root = shared_prefix_len >= root_node.prefix_len as u32; if keep_old_root { parent_node = Some(root); + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] let r = root_node.walk_down(new_leaf.key); + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let r = root_node.walk_down(new_leaf.order_id()); root = r.0; previous_critbit = Some(r.1); continue; @@ -250,7 +271,11 @@ impl<'a, C> Slab<'a, C> { *root_node = *new_leaf; return Ok((root, Some(leaf_copy))); } + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] let shared_prefix_len: u32 = (root_node.key ^ new_leaf.key).leading_zeros(); + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let shared_prefix_len: u32 = + (root_node.order_id() ^ new_leaf.order_id()).leading_zeros(); shared_prefix_len } @@ -258,7 +283,10 @@ impl<'a, C> Slab<'a, C> { // change the root in place to represent the LCA of [new_leaf] and [root] let crit_bit_mask: u128 = (1u128 << 127) >> shared_prefix_len; + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] let new_leaf_crit_bit = (crit_bit_mask & new_leaf.key) != 0; + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let new_leaf_crit_bit = (crit_bit_mask & new_leaf.order_id()) != 0; let old_root_crit_bit = !new_leaf_crit_bit; let new_leaf_handle = self.allocate_leaf().map_err(|_| AoError::SlabOutOfSpace)?; @@ -267,7 +295,14 @@ impl<'a, C> Slab<'a, C> { let new_root_node_handle = self.allocate_inner_node().unwrap(); let new_root_node = &mut self.inner_nodes[(!new_root_node_handle) as usize]; new_root_node.prefix_len = shared_prefix_len as u64; - new_root_node.key = new_leaf.key; + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] + { + new_root_node.key = new_leaf.key; + } + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + { + new_root_node.key = new_leaf.order_id(); + } new_root_node.children[new_leaf_crit_bit as usize] = new_leaf_handle; new_root_node.children[old_root_crit_bit as usize] = root; @@ -308,9 +343,14 @@ impl<'a, C> Slab<'a, C> { match Node::from_handle(parent_h) { Node::Leaf => { let leaf = &self.leaf_nodes[parent_h as usize]; + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] if leaf.key == search_key { remove_root = Some(*leaf); } + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + if leaf.order_id() == search_key { + remove_root = Some(*leaf); + } } Node::Inner => { let node = self.inner_nodes[(!parent_h) as usize]; @@ -342,9 +382,14 @@ impl<'a, C> Slab<'a, C> { } Node::Leaf => { let leaf = &self.leaf_nodes[child_h as usize]; + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] if leaf.key != search_key { return None; } + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + if leaf.order_id() != search_key { + return None; + } break; } @@ -465,13 +510,24 @@ impl<'a, C> Slab<'a, C> { Node::Leaf => { *leaf_count += 1; let node = &slab.leaf_nodes[h as usize]; - assert_eq!( - last_critbit, - (node.key & ((1u128 << 127) >> last_prefix_len)) != 0 - ); + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] + { + assert_eq!( + last_critbit, + (node.key & ((1u128 << 127) >> last_prefix_len)) != 0 + ); + } + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + { + assert_eq!( + last_critbit, + (node.order_id() & ((1u128 << 127) >> last_prefix_len)) != 0 + ); + } let prefix_mask = (((((1u128) << 127) as i128) >> last_prefix_len) as u128) << 1; - assert_eq!(last_prefix & prefix_mask, node.key & prefix_mask); + // assert_eq!(last_prefix & prefix_mask, node.key & prefix_mask); + assert_eq!(last_prefix & prefix_mask, node.order_id() & prefix_mask); } Node::Inner => { *inner_node_count += 1; @@ -553,11 +609,18 @@ impl<'a, C> Slab<'a, C> { match Node::from_handle(node_handle) { Node::Leaf => { let n = self.leaf_nodes[node_handle as usize]; + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] if search_key == n.key { return Some(node_handle); } else { return None; } + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + if search_key == n.order_id() { + return Some(node_handle); + } else { + return None; + } } Node::Inner => { let n = self.inner_nodes[(!node_handle as usize)]; @@ -697,8 +760,10 @@ mod tests { key, base_quantity: qty, }; - + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] println!("key : {:x}", key); + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + println!("key : {:x}", leaf.order_id()); println!("owner : {:?}", &owner.to_bytes()); println!("{}", i); let h = slab.insert_leaf(&leaf).unwrap().0; @@ -706,11 +771,22 @@ mod tests { key: owner.to_bytes(), }; *slab.get_callback_info_mut(h) = callback_info; - model - .insert(key, (leaf, callback_info)) - .ok_or(()) - .unwrap_err(); - all_keys.push(key); + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] + { + model + .insert(key, (leaf, callback_info)) + .ok_or(()) + .unwrap_err(); + all_keys.push(key); + } + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + { + model + .insert(leaf.order_id(), (leaf, callback_info)) + .ok_or(()) + .unwrap_err(); + all_keys.push(leaf.order_id()); + } // test find_by_key let valid_search_key = *all_keys.choose(&mut rng).unwrap(); @@ -805,16 +881,24 @@ mod tests { let owner = Pubkey::new_unique().to_bytes(); let qty = rng.gen(); let leaf = LeafNode { - key, + key: { + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] + let k = key; + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let k = [key as u64, (key >> 64) as u64]; + k + }, base_quantity: qty, }; let (leaf_h, old_leaf) = slab.insert_leaf(&leaf).unwrap(); let old_owner = *slab.get_callback_info(leaf_h); *slab.get_callback_info_mut(leaf_h) = owner; - println!("Insert {:x}", key); + // println!("Insert {:x}", key); + println!("Insert {:x}", leaf.order_id()); - all_keys.push(key); + // all_keys.push(key); + all_keys.push(leaf.order_id()); let slab_value = old_leaf.map(|l| (l, old_owner)); let model_value = model.insert(key, (leaf, owner)); if slab_value != model_value { diff --git a/program/src/state/event_queue.rs b/program/src/state/event_queue.rs index f2bacff..086ce53 100644 --- a/program/src/state/event_queue.rs +++ b/program/src/state/event_queue.rs @@ -27,7 +27,10 @@ pub struct FillEvent { /// The total quote size of the transaction pub quote_size: u64, /// The order id of the maker order + #[cfg(not(target_arch = "aarch64"))] pub maker_order_id: u128, + #[cfg(target_arch = "aarch64")] + pub maker_order_id: [u64; 2], /// The total base size of the transaction pub base_size: u64, } @@ -47,7 +50,10 @@ pub struct OutEvent { pub side: u8, pub(crate) _padding: [u8; 14], /// The order id of the maker order + #[cfg(not(target_arch = "aarch64"))] pub order_id: u128, + #[cfg(target_arch = "aarch64")] + pub order_id: [u64; 2], /// The total base size of the transaction pub base_size: u64, } @@ -337,6 +343,7 @@ mod tests { for _ in 0..100 { if parity_gen.next().unwrap() % 7 != 3 { + #[allow(clippy::let_and_return)] event_queue .push_back( FillEvent { @@ -344,7 +351,12 @@ mod tests { taker_side: Side::Ask as u8, _padding: [0; 6], quote_size: seq_gen.next().unwrap(), - maker_order_id: seq_gen.next().unwrap() as u128, + maker_order_id: { + let s = seq_gen.next().unwrap() as u128; + #[cfg(target_arch = "aarch64")] + let s = [s as u64, 0]; + s + }, base_size: seq_gen.next().unwrap(), }, Some(&[seq_gen.next().unwrap() as u8; 32]), @@ -352,6 +364,7 @@ mod tests { ) .unwrap(); } else { + #[allow(clippy::let_and_return)] event_queue .push_back( OutEvent { @@ -359,7 +372,12 @@ mod tests { side: Side::Ask as u8, _padding: [0; 14], base_size: seq_gen.next().unwrap(), - order_id: seq_gen.next().unwrap() as u128, + order_id: { + let s = seq_gen.next().unwrap() as u128; + #[cfg(target_arch = "aarch64")] + let s = [s as u64, 0]; + s + }, }, Some(&[seq_gen.next().unwrap() as u8; 32]), None, @@ -367,12 +385,18 @@ mod tests { .unwrap(); } } + #[allow(clippy::let_and_return)] let extra_event = FillEvent { tag: EventTag::Fill as u8, taker_side: Side::Ask as u8, _padding: [0; 6], quote_size: seq_gen.next().unwrap(), - maker_order_id: seq_gen.next().unwrap() as u128, + maker_order_id: { + let s = seq_gen.next().unwrap() as u128; + #[cfg(target_arch = "aarch64")] + let s = [s as u64, 0]; + s + }, base_size: seq_gen.next().unwrap(), }; assert_eq!( @@ -390,37 +414,53 @@ mod tests { match e { EventRef::Out(o) => { assert!(!is_fill); - assert_eq!( - o, - OutEventRef { - event: &OutEvent { - tag: EventTag::Out as u8, - side: Side::Ask as u8, - _padding: [0; 14], - base_size: seq_gen.next().unwrap(), - order_id: seq_gen.next().unwrap() as u128, - }, - callback_info: &[seq_gen.next().unwrap() as u8; 32] - } - ); + #[allow(clippy::let_and_return)] + { + assert_eq!( + o, + OutEventRef { + event: &OutEvent { + tag: EventTag::Out as u8, + side: Side::Ask as u8, + _padding: [0; 14], + base_size: seq_gen.next().unwrap(), + order_id: { + let s = seq_gen.next().unwrap() as u128; + #[cfg(target_arch = "aarch64")] + let s = [s as u64, 0]; + s + }, + }, + callback_info: &[seq_gen.next().unwrap() as u8; 32] + } + ); + } } EventRef::Fill(e) => { assert!(is_fill); - assert_eq!( - e, - FillEventRef { - event: &FillEvent { - tag: EventTag::Fill as u8, - taker_side: Side::Ask as u8, - _padding: [0; 6], - quote_size: seq_gen.next().unwrap(), - maker_order_id: seq_gen.next().unwrap() as u128, - base_size: seq_gen.next().unwrap(), - }, - maker_callback_info: &[seq_gen.next().unwrap() as u8; 32], - taker_callback_info: &[seq_gen.next().unwrap() as u8; 32] - } - ); + #[allow(clippy::let_and_return)] + { + assert_eq!( + e, + FillEventRef { + event: &FillEvent { + tag: EventTag::Fill as u8, + taker_side: Side::Ask as u8, + _padding: [0; 6], + quote_size: seq_gen.next().unwrap(), + maker_order_id: { + let s = seq_gen.next().unwrap() as u128; + #[cfg(target_arch = "aarch64")] + let s = [s as u64, 0]; + s + }, + base_size: seq_gen.next().unwrap(), + }, + maker_callback_info: &[seq_gen.next().unwrap() as u8; 32], + taker_callback_info: &[seq_gen.next().unwrap() as u8; 32] + } + ); + } assert_eq!(EventRef::Fill(e), event_queue.peek_at(i as u64).unwrap()); } } diff --git a/program/src/state/orderbook.rs b/program/src/state/orderbook.rs index 941706f..f6d72b4 100644 --- a/program/src/state/orderbook.rs +++ b/program/src/state/orderbook.rs @@ -190,9 +190,13 @@ where assert!(self_trade_behavior == SelfTradeBehavior::CancelProvide); let provide_out_callback_info = &opposite_slab.callback_infos[best_bo_h as usize]; + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] + let order_id = best_offer_id; + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let order_id = [best_offer_id as u64, (best_offer_id >> 64) as u64]; let provide_out = OutEvent { side: side.opposite() as u8, - order_id: best_offer_id, + order_id, base_size: best_bo_ref.base_quantity, tag: EventTag::Out as u8, _padding: [0; 14], @@ -213,9 +217,17 @@ where let maker_callback_info = &opposite_slab.callback_infos[best_bo_h as usize]; + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] + let maker_order_id = best_bo_ref.order_id(); + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let maker_order_id = { + let o = best_bo_ref.order_id(); + [o as u64, (o >> 64) as u64] + }; + let maker_fill = FillEvent { taker_side: side as u8, - maker_order_id: best_bo_ref.order_id(), + maker_order_id, quote_size: quote_maker_qty, base_size: base_trade_qty, tag: EventTag::Fill as u8, @@ -232,9 +244,13 @@ where if best_bo_ref.base_quantity < min_base_order_size { let best_offer_id = best_bo_ref.order_id(); let cur_side = side.opposite(); + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] + let order_id = best_offer_id; + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let order_id = [best_offer_id as u64, (best_offer_id >> 64) as u64]; let out_event = OutEvent { side: cur_side as u8, - order_id: best_offer_id, + order_id, base_size: best_bo_ref.base_quantity, tag: EventTag::Out as u8, _padding: [0; 14], @@ -268,7 +284,13 @@ where let new_leaf_order_id = event_queue.gen_order_id(limit_price, side); let new_leaf = LeafNode { - key: new_leaf_order_id, + key: { + #[cfg(all(not(target_arch = "aarch64"), not(feature = "aarch64")))] + let k = new_leaf_order_id; + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let k = [new_leaf_order_id as u64, (new_leaf_order_id >> 64) as u64]; + k + }, base_quantity: base_qty_to_post, }; let insert_result = self.get_tree(side).insert_leaf(&new_leaf); @@ -280,7 +302,8 @@ where Side::Bid => slab.find_min().unwrap(), Side::Ask => slab.find_max().unwrap(), }; - let boot_candidate_key = slab.leaf_nodes[boot_candidate as usize].key; + // let boot_candidate_key = slab.leaf_nodes[boot_candidate as usize].key; + let boot_candidate_key = slab.leaf_nodes[boot_candidate as usize].order_id(); let boot_candidate_price = LeafNode::price_from_key(boot_candidate_key); let should_boot = match side { Side::Bid => boot_candidate_price < limit_price, @@ -288,9 +311,15 @@ where }; if should_boot { let (order, callback_info_booted) = slab.remove_by_key(boot_candidate_key).unwrap(); + #[allow(clippy::let_and_return)] let out = OutEvent { side: side as u8, - order_id: order.order_id(), + order_id: { + let o = order.order_id(); + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let o = [o as u64, (o >> 64) as u64]; + o + }, base_size: order.base_quantity, tag: EventTag::Out as u8, _padding: [0; 14], @@ -508,35 +537,48 @@ mod tests { assert_eq!(event_queue.header.count, 2); let mut event_queue_iter = event_queue.iter(); - assert_eq!( - event_queue_iter.next().unwrap(), - EventRef::Fill(FillEventRef { - event: &FillEvent { - tag: EventTag::Fill as u8, - taker_side: Side::Ask as u8, - _padding: [0; 6], - quote_size: 500_000 * 15, - maker_order_id: bob_order_id_0.unwrap(), - base_size: 500_000 - }, - maker_callback_info: &bob, - taker_callback_info: &alice - }) - ); - - assert_eq!( - event_queue_iter.next().unwrap(), - EventRef::Out(OutEventRef { - event: &OutEvent { - tag: EventTag::Out as u8, - side: Side::Bid as u8, - _padding: [0; 14], - base_size: 0, - order_id: bob_order_id_0.unwrap() - }, - callback_info: &bob - }) - ); + #[allow(clippy::let_and_return)] + { + assert_eq!( + event_queue_iter.next().unwrap(), + EventRef::Fill(FillEventRef { + event: &FillEvent { + tag: EventTag::Fill as u8, + taker_side: Side::Ask as u8, + _padding: [0; 6], + quote_size: 500_000 * 15, + maker_order_id: { + let o = bob_order_id_0.unwrap(); + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let o = [o as u64, (o >> 64) as u64]; + o + }, + base_size: 500_000 + }, + maker_callback_info: &bob, + taker_callback_info: &alice + }) + ); + + assert_eq!( + event_queue_iter.next().unwrap(), + EventRef::Out(OutEventRef { + event: &OutEvent { + tag: EventTag::Out as u8, + side: Side::Bid as u8, + _padding: [0; 14], + base_size: 0, + order_id: { + let o = bob_order_id_0.unwrap(); + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let o = [o as u64, (o >> 64) as u64]; + o + } + }, + callback_info: &bob + }) + ); + } println!("Event queue head: {}", event_queue.header.head); event_queue.pop_n(2); println!("Event queue head: {}", event_queue.header.head); @@ -597,19 +639,27 @@ mod tests { assert_eq!(event_queue.header.count, 1); println!("Event queue head: {}", event_queue.header.head); - assert_eq!( - event_queue.iter().next().unwrap(), - EventRef::Out(OutEventRef { - event: &OutEvent { - tag: EventTag::Out as u8, - side: Side::Ask as u8, - _padding: [0; 14], - base_size: 250_000, - order_id: alice_order_id_0.unwrap() - }, - callback_info: &alice - }) - ); + #[allow(clippy::let_and_return)] + { + assert_eq!( + event_queue.iter().next().unwrap(), + EventRef::Out(OutEventRef { + event: &OutEvent { + tag: EventTag::Out as u8, + side: Side::Ask as u8, + _padding: [0; 14], + base_size: 250_000, + order_id: { + let o = alice_order_id_0.unwrap(); + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let o = [o as u64, (o >> 64) as u64]; + o + } + }, + callback_info: &alice + }) + ); + } event_queue.pop_n(1); @@ -728,19 +778,27 @@ mod tests { assert_eq!(total_base_qty_posted, 1_000_000); assert_eq!(event_queue.header.count, 1); - assert_eq!( - event_queue.iter().next().unwrap(), - EventRef::Out(OutEventRef { - event: &OutEvent { - tag: EventTag::Out as u8, - side: Side::Ask as u8, - _padding: [0; 14], - base_size: 6_000_000, - order_id: order_id_to_be_booted.unwrap() - }, - callback_info: &alice - }) - ); + #[allow(clippy::let_and_return)] + { + assert_eq!( + event_queue.iter().next().unwrap(), + EventRef::Out(OutEventRef { + event: &OutEvent { + tag: EventTag::Out as u8, + side: Side::Ask as u8, + _padding: [0; 14], + base_size: 6_000_000, + order_id: { + let o = order_id_to_be_booted.unwrap(); + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let o = [o as u64, (o >> 64) as u64]; + o + } + }, + callback_info: &alice + }) + ); + } event_queue.pop_n(1); @@ -925,19 +983,27 @@ mod tests { assert_eq!(total_base_qty_posted, 1_000_000); assert_eq!(event_queue.header.count, 1); - assert_eq!( - event_queue.iter().next().unwrap(), - EventRef::Out(OutEventRef { - event: &OutEvent { - tag: EventTag::Out as u8, - side: Side::Bid as u8, - _padding: [0; 14], - base_size: 6_000_000, - order_id: order_id_to_be_booted.unwrap() - }, - callback_info: &alice - }) - ); + #[allow(clippy::let_and_return)] + { + assert_eq!( + event_queue.iter().next().unwrap(), + EventRef::Out(OutEventRef { + event: &OutEvent { + tag: EventTag::Out as u8, + side: Side::Bid as u8, + _padding: [0; 14], + base_size: 6_000_000, + order_id: { + let o = order_id_to_be_booted.unwrap(); + #[cfg(any(target_arch = "aarch64", feature = "aarch64"))] + let o = [o as u64, (o >> 64) as u64]; + o + } + }, + callback_info: &alice + }) + ); + } event_queue.pop_n(1);