use std::fmt;
use std::marker::PhantomData;
use std::mem;
use piecrust_uplink::ContractId;
#[derive(Debug, Clone, Copy)]
pub struct CallTreeElem {
pub contract_id: ContractId,
pub limit: u64,
pub spent: u64,
pub mem_len: usize,
}
#[derive(Default)]
pub struct CallTree(Option<*mut CallTreeNode>, usize);
impl fmt::Debug for CallTree {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe fn format_node_pretty(
f: &mut fmt::Formatter<'_>,
node: *mut CallTreeNode,
cursor: *mut CallTreeNode,
prefix: &str,
is_root: bool,
is_last: bool,
) -> fmt::Result {
unsafe {
let node_ref = &*node;
let is_cursor = node == cursor;
let id_bytes = node_ref.elem.contract_id.to_bytes();
let short_id = format!(
"{:02x}{:02x}{:02x}{:02x}",
id_bytes[0], id_bytes[1], id_bytes[2], id_bytes[3]
);
if !is_root {
write!(f, "{}", prefix)?;
write!(f, "{}", if is_last { "└── " } else { "├── " })?;
}
if is_cursor {
writeln!(f, "*0x{}", short_id)?;
} else {
writeln!(f, "0x{}", short_id)?;
}
let child_count = node_ref.children.len();
for (i, &child) in node_ref.children.iter().enumerate() {
let is_last_child = i == child_count - 1;
let new_prefix = if is_root {
String::new()
} else {
format!(
"{}{} ",
prefix,
if is_last { " " } else { "│" }
)
};
format_node_pretty(
f,
child,
cursor,
&new_prefix,
false,
is_last_child,
)?;
}
Ok(())
}
}
if f.alternate() {
match self.0 {
None => write!(f, "[]"),
Some(root) => {
let mut actual_root = root;
while let Some(parent) = unsafe { (*actual_root).parent } {
actual_root = parent;
}
unsafe {
format_node_pretty(f, actual_root, root, "", true, true)
}
}
}
} else {
f.debug_tuple("CallTree").field(&self.0).finish()
}
}
}
impl CallTree {
pub(crate) const fn new() -> Self {
Self(None, 0)
}
pub(crate) fn push(&mut self, elem: CallTreeElem) {
match self.0 {
None => {
self.0 = Some(CallTreeNode::new(elem));
self.1 = 1;
}
Some(inner) => unsafe {
let node = CallTreeNode::with_parent(elem, inner);
(*inner).children.push(node);
self.0 = Some(node);
self.1 += 1;
},
}
}
pub(crate) fn move_up(&mut self, spent: u64) -> Option<CallTreeElem> {
self.0.map(|inner| unsafe {
(*inner).elem.spent = spent;
let elem = (*inner).elem;
let parent = (*inner).parent;
if parent.is_none() {
free_tree(inner);
self.1 = 0;
} else {
self.1 -= 1;
}
self.0 = parent;
elem
})
}
pub(crate) fn move_up_prune(&mut self) -> Option<CallTreeElem> {
self.0.map(|inner| unsafe {
let elem = (*inner).elem;
let parent = (*inner).parent;
if let Some(parent) = parent {
(*parent).children.pop();
self.1 -= 1;
} else {
self.1 = 0;
}
free_tree(inner);
self.0 = parent;
elem
})
}
pub(crate) fn update_spent(&mut self, spent: u64) {
if let Some(inner) = self.0 {
unsafe {
(*inner).elem.spent = spent;
update_spent(inner);
}
}
}
pub(crate) fn nth_parent(&self, n: usize) -> Option<CallTreeElem> {
let mut current = self.0;
let mut i = 0;
while i < n {
current = current.and_then(|inner| unsafe { (*inner).parent });
i += 1;
}
current.map(|inner| unsafe { (*inner).elem })
}
pub(crate) fn depth(&self) -> usize {
self.1
}
pub(crate) fn call_ids(&self) -> Vec<&ContractId> {
let mut v = Vec::new();
let mut current = self.0;
while current.is_some() {
let p = *current.as_ref().unwrap();
v.push(unsafe { &(*p).elem.contract_id });
current = current.and_then(|inner| unsafe { (*inner).parent });
}
v
}
pub(crate) fn clear(&mut self) {
unsafe {
if let Some(inner) = self.0 {
let mut root = inner;
while let Some(parent) = (*root).parent {
root = parent;
}
self.0 = None;
self.1 = 0;
free_tree(root);
}
}
}
pub fn iter(&self) -> impl Iterator<Item = &CallTreeElem> {
CallTreeIter {
tree: self.0.map(|root| unsafe {
let mut node = root;
while !(*node).children.is_empty() {
let last_index = (*node).children.len() - 1;
node = (&(*node).children)[last_index];
}
Subtree { root, node }
}),
_marker: PhantomData,
}
}
}
struct Subtree {
root: *mut CallTreeNode,
node: *mut CallTreeNode,
}
impl Drop for CallTree {
fn drop(&mut self) {
self.clear()
}
}
unsafe fn update_spent(node: *mut CallTreeNode) {
unsafe {
let node = &mut *node;
node.children.iter_mut().for_each(|&mut child| {
node.elem.spent -= (*child).elem.spent;
update_spent(child);
});
}
}
unsafe fn free_tree(root: *mut CallTreeNode) {
unsafe {
let mut node = Box::from_raw(root);
let mut children = Vec::new();
mem::swap(&mut node.children, &mut children);
for child in children {
free_tree(child);
}
}
}
struct CallTreeNode {
elem: CallTreeElem,
children: Vec<*mut Self>,
parent: Option<*mut Self>,
}
impl CallTreeNode {
fn new(elem: CallTreeElem) -> *mut Self {
Box::leak(Box::new(Self {
elem,
children: Vec::new(),
parent: None,
}))
}
fn with_parent(elem: CallTreeElem, parent: *mut Self) -> *mut Self {
Box::leak(Box::new(Self {
elem,
children: Vec::new(),
parent: Some(parent),
}))
}
}
struct CallTreeIter<'a> {
tree: Option<Subtree>,
_marker: PhantomData<&'a ()>,
}
impl<'a> Iterator for CallTreeIter<'a> {
type Item = &'a CallTreeElem;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let tree = self.tree.as_mut()?;
let node = tree.node;
let elem = &(*node).elem;
if node == tree.root {
self.tree = None;
return Some(elem);
}
let parent = (*node).parent.expect(
"There should always be a parent in a subtree before the root",
);
tree.node = {
let node_index = (*parent)
.children
.iter()
.position(|&n| n == node)
.expect("The child must be the its parent's child");
if node_index == 0 {
parent
} else {
let sibling_index = node_index - 1;
let mut next_node = (&(*parent).children)[sibling_index];
while !(*next_node).children.is_empty() {
let last_index = (*next_node).children.len() - 1;
next_node = (&(*next_node).children)[last_index];
}
next_node
}
};
Some(elem)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn contract_id(n: u8) -> ContractId {
let mut bytes = [0u8; 32];
bytes[0] = n;
ContractId::from_bytes(bytes)
}
fn elem(id: u8, limit: u64, spent: u64, mem_len: usize) -> CallTreeElem {
CallTreeElem {
contract_id: contract_id(id),
limit,
spent,
mem_len,
}
}
#[test]
fn test_basic_operations() {
let mut tree = CallTree::new();
assert_eq!(tree.depth(), 0);
assert!(tree.nth_parent(0).is_none());
assert!(tree.call_ids().is_empty());
assert_eq!(tree.iter().count(), 0);
tree.push(elem(1, 999999, 0, 8192));
assert_eq!(tree.depth(), 1);
let current = tree.nth_parent(0).unwrap();
assert_eq!(current.contract_id, contract_id(1));
assert_eq!(current.limit, 999999);
assert_eq!(current.spent, 0);
assert_eq!(current.mem_len, 8192);
let returned = tree.move_up(500).unwrap();
assert_eq!(returned.spent, 500);
assert_eq!(tree.depth(), 0);
assert!(tree.nth_parent(0).is_none());
}
#[test]
fn test_depth_tracking() {
let mut tree = CallTree::new();
assert_eq!(tree.depth(), 0);
tree.push(elem(1, 1000, 0, 100));
assert_eq!(tree.depth(), 1);
tree.push(elem(2, 900, 0, 200));
assert_eq!(tree.depth(), 2);
tree.push(elem(3, 800, 0, 300));
assert_eq!(tree.depth(), 3);
tree.move_up(30);
assert_eq!(tree.depth(), 2);
tree.move_up_prune();
assert_eq!(tree.depth(), 1);
tree.clear();
assert_eq!(tree.depth(), 0);
}
#[test]
fn test_linear_chain_navigation() {
let mut tree = CallTree::new();
for i in 1u8..=5 {
let limit = 1000u64 - (i as u64) * 100;
tree.push(elem(i, limit, 0, i as usize * 100));
}
assert_eq!(tree.nth_parent(0).unwrap().contract_id, contract_id(5));
assert_eq!(tree.nth_parent(1).unwrap().contract_id, contract_id(4));
assert_eq!(tree.nth_parent(4).unwrap().contract_id, contract_id(1));
assert!(tree.nth_parent(5).is_none());
let ids = tree.call_ids();
assert_eq!(ids.len(), 5);
for i in 0..5 {
assert_eq!(*ids[i], contract_id((5 - i) as u8));
}
for i in (1..=5).rev() {
let e = tree.move_up(i * 100).unwrap();
assert_eq!(e.contract_id, contract_id(i as u8));
assert_eq!(e.spent, i * 100);
}
assert!(tree.nth_parent(0).is_none());
}
#[test]
fn test_iterator_behavior() {
let mut tree = CallTree::new();
tree.push(elem(1, 1000, 0, 100));
tree.push(elem(2, 900, 0, 200));
tree.push(elem(3, 800, 0, 300));
assert_eq!(tree.iter().count(), 1);
tree.move_up(30);
let elements: Vec<_> = tree.iter().collect();
assert_eq!(elements.len(), 2);
assert_eq!(elements[0].contract_id, contract_id(3));
assert_eq!(elements[1].contract_id, contract_id(2));
}
#[test]
fn test_iterator_advanced() {
let mut tree = CallTree::new();
assert_eq!(tree.iter().count(), 0);
tree.push(elem(1, 1000, 0, 100)); tree.push(elem(2, 900, 0, 200)); tree.push(elem(4, 800, 0, 400)); tree.move_up(40);
tree.move_up(20);
tree.push(elem(3, 700, 0, 300)); tree.move_up(30);
let elements: Vec<_> = tree.iter().collect();
assert_eq!(elements.len(), 4);
assert_eq!(elements[0].contract_id, contract_id(3));
assert_eq!(elements[3].contract_id, contract_id(1));
let iter1 = tree.iter();
let iter2 = tree.iter();
assert_eq!(iter1.count(), iter2.count());
tree.clear();
assert_eq!(tree.iter().count(), 0);
}
#[test]
fn test_tree_with_multiple_siblings() {
let mut tree = CallTree::new();
tree.push(elem(1, 1000, 0, 100)); tree.push(elem(2, 900, 0, 200)); tree.push(elem(4, 800, 0, 400)); tree.move_up(100);
tree.push(elem(5, 700, 0, 500)); tree.move_up(150); tree.move_up(250);
tree.push(elem(3, 600, 0, 300));
assert_eq!(tree.nth_parent(0).unwrap().contract_id, contract_id(3)); assert_eq!(tree.nth_parent(1).unwrap().contract_id, contract_id(1));
let elements: Vec<_> = tree.iter().collect();
assert_eq!(elements.len(), 1);
assert_eq!(elements[0].contract_id, contract_id(3));
tree.move_up(200);
let elements: Vec<_> = tree.iter().collect();
assert_eq!(elements.len(), 5);
assert_eq!(elements[0].contract_id, contract_id(3)); assert_eq!(elements[1].contract_id, contract_id(5)); assert_eq!(elements[2].contract_id, contract_id(4)); assert_eq!(elements[3].contract_id, contract_id(2)); assert_eq!(elements[4].contract_id, contract_id(1));
tree.clear(); assert!(tree.nth_parent(0).is_none());
tree.push(elem(10, 1000, 0, 100)); tree.push(elem(11, 900, 0, 200)); tree.move_up(20); tree.push(elem(12, 800, 0, 300)); tree.move_up(30); tree.push(elem(13, 700, 0, 400));
assert_eq!(tree.nth_parent(0).unwrap().contract_id, contract_id(13));
tree.move_up(40);
let elements: Vec<_> = tree.iter().collect();
assert_eq!(elements.len(), 4);
assert_eq!(elements[0].contract_id, contract_id(13)); assert_eq!(elements[1].contract_id, contract_id(12)); assert_eq!(elements[2].contract_id, contract_id(11)); assert_eq!(elements[3].contract_id, contract_id(10)); }
#[test]
fn test_complex_nested_tree() {
let mut tree = CallTree::new();
tree.push(elem(1, 10000, 0, 100));
tree.push(elem(2, 9000, 0, 200));
tree.push(elem(5, 8000, 0, 500));
tree.push(elem(8, 7000, 0, 800));
tree.move_up(80); tree.move_up(50);
tree.push(elem(6, 6000, 0, 600));
tree.move_up(60); tree.move_up(20);
tree.push(elem(3, 5000, 0, 300));
tree.push(elem(7, 4000, 0, 700));
tree.move_up(70); tree.move_up(30);
tree.push(elem(4, 3000, 0, 400));
assert_eq!(tree.nth_parent(0).unwrap().contract_id, contract_id(4)); assert_eq!(tree.nth_parent(1).unwrap().contract_id, contract_id(1));
let ids = tree.call_ids();
assert_eq!(ids.len(), 2);
assert_eq!(*ids[0], contract_id(4)); assert_eq!(*ids[1], contract_id(1)); }
#[test]
fn test_pruning_operations() {
let mut tree = CallTree::new();
tree.push(elem(1, 1000, 0, 100));
tree.push(elem(2, 900, 0, 200));
tree.push(elem(3, 800, 0, 300));
let pruned = tree.move_up_prune().unwrap();
assert_eq!(pruned.contract_id, contract_id(3));
assert_eq!(tree.nth_parent(0).unwrap().contract_id, contract_id(2));
assert_eq!(tree.iter().count(), 1);
tree.move_up(20);
tree.push(elem(4, 700, 0, 400)); tree.push(elem(5, 600, 0, 500)); tree.move_up(50);
tree.move_up(40);
tree.push(elem(6, 500, 0, 600)); tree.move_up(60);
tree.push(elem(7, 400, 0, 700)); tree.push(elem(8, 300, 0, 800)); tree.move_up(80);
let pruned = tree.move_up_prune().unwrap();
assert_eq!(pruned.contract_id, contract_id(7)); assert_eq!(tree.nth_parent(0).unwrap().contract_id, contract_id(1));
tree.clear();
tree.push(elem(10, 1000, 0, 100));
let pruned = tree.move_up_prune().unwrap();
assert_eq!(pruned.contract_id, contract_id(10));
assert!(tree.nth_parent(0).is_none());
assert_eq!(tree.iter().count(), 0);
}
#[test]
fn test_prune_with_siblings() {
let mut tree = CallTree::new();
tree.push(elem(1, 1000, 0, 100)); tree.push(elem(2, 900, 0, 200)); tree.push(elem(3, 850, 0, 250)); tree.move_up_prune(); tree.move_up(20);
tree.push(elem(4, 800, 0, 300)); tree.push(elem(5, 750, 0, 350)); tree.move_up_prune(); tree.move_up(40);
tree.push(elem(6, 700, 0, 400)); tree.move_up_prune();
assert_eq!(tree.nth_parent(0).unwrap().contract_id, contract_id(1));
let elements: Vec<_> = tree.iter().collect();
assert_eq!(elements.len(), 3); assert_eq!(elements[0].contract_id, contract_id(4)); assert_eq!(elements[1].contract_id, contract_id(2)); assert_eq!(elements[2].contract_id, contract_id(1)); }
#[test]
fn test_gas_accounting() {
let mut tree = CallTree::new();
tree.push(elem(1, 1000, 0, 100));
tree.push(elem(2, 500, 0, 200));
tree.move_up(300);
tree.update_spent(700);
assert_eq!(tree.nth_parent(0).unwrap().spent, 400);
tree.clear();
tree.push(elem(1, 10000, 0, 100));
tree.push(elem(2, 9000, 0, 200));
tree.move_up(300);
tree.push(elem(3, 8000, 0, 300));
tree.move_up(200);
tree.update_spent(1000);
assert_eq!(tree.nth_parent(0).unwrap().spent, 500);
tree.clear();
tree.push(elem(1, 10000, 0, 100));
tree.push(elem(2, 9000, 0, 200));
tree.push(elem(3, 8000, 0, 300));
tree.push(elem(4, 7000, 0, 400));
tree.push(elem(5, 6000, 0, 500));
tree.move_up(100);
tree.move_up(200);
tree.move_up(300);
tree.move_up(400);
tree.update_spent(1500);
assert_eq!(tree.nth_parent(0).unwrap().spent, 1100); assert_eq!(tree.nth_parent(0).unwrap().limit, 10000);
tree.clear();
tree.push(elem(4, 1000, 0, 100));
tree.push(elem(5, 900, 0, 200));
tree.move_up(0);
tree.update_spent(0);
assert_eq!(tree.nth_parent(0).unwrap().spent, 0);
}
#[test]
fn test_edge_cases() {
let mut tree = CallTree::new();
tree.clear(); assert!(tree.move_up(100).is_none());
assert!(tree.move_up_prune().is_none());
tree.update_spent(1000); assert!(tree.nth_parent(0).is_none());
assert!(tree.nth_parent(100).is_none());
assert!(tree.call_ids().is_empty());
tree.push(elem(1, 1000, 0, 100));
tree.push(elem(2, 900, 0, 200));
tree.push(elem(3, 800, 0, 300));
assert!(tree.nth_parent(100).is_none());
tree.move_up(30);
tree.move_up(20);
tree.move_up(10);
assert!(tree.nth_parent(0).is_none());
assert!(tree.move_up(100).is_none());
tree.push(elem(4, 700, 0, 400));
assert_eq!(tree.nth_parent(0).unwrap().contract_id, contract_id(4));
tree.clear();
tree.clear();
tree.clear();
assert!(tree.nth_parent(0).is_none());
tree.push(elem(1, u64::MAX, 0, 100));
tree.push(elem(2, u64::MAX - 1, 0, 200));
tree.move_up(u64::MAX - 1000);
tree.update_spent(u64::MAX);
assert!(tree.nth_parent(0).unwrap().spent <= u64::MAX);
}
#[test]
fn test_tree_clearing() {
let mut tree = CallTree::new();
tree.push(elem(1, 10000, 0, 100));
for i in 2..=10 {
tree.push(elem(i, 9000, 0, i as usize * 100));
}
tree.clear();
assert!(tree.nth_parent(0).is_none());
assert_eq!(tree.iter().count(), 0);
tree.push(elem(1, 1000, 0, 100));
tree.push(elem(2, 900, 0, 200));
tree.move_up_prune();
tree.clear();
assert!(tree.nth_parent(0).is_none());
}
#[test]
fn test_memory_safety() {
let mut tree = CallTree::new();
tree.push(elem(1, 1000, 0, 100));
tree.push(elem(2, 900, 0, 200));
tree.move_up(20);
tree.push(elem(3, 800, 0, 300));
let c = tree.nth_parent(0).unwrap();
assert_eq!(c.contract_id, contract_id(3));
let root = tree.nth_parent(1).unwrap();
assert_eq!(root.contract_id, contract_id(1));
assert_eq!(tree.call_ids().len(), 2);
let ids = tree.call_ids();
let id_copy = *ids[0];
assert_eq!(id_copy, contract_id(3));
tree.clear();
let mem_lens = vec![1024, 2048, 4096, 8192];
for (i, &mem_len) in mem_lens.iter().enumerate() {
tree.push(elem((i + 1) as u8, 1000, 0, mem_len));
}
for (i, &expected) in mem_lens.iter().enumerate() {
assert_eq!(
tree.nth_parent(mem_lens.len() - 1 - i).unwrap().mem_len,
expected
);
}
}
#[test]
fn test_revert_and_stress() {
let mut tree = CallTree::new();
tree.push(elem(1, 10000, 0, 1000));
tree.push(elem(2, 9000, 0, 2000));
tree.push(elem(3, 8000, 0, 3000));
tree.push(elem(4, 7000, 0, 4000));
tree.move_up(400);
tree.move_up(300);
let mem_lens: Vec<_> = tree.iter().map(|e| e.mem_len).collect();
assert!(mem_lens.contains(&3000) && mem_lens.contains(&2000));
tree.clear();
for i in 1..=100 {
tree.push(elem(i as u8, 10000 - i * 10, 0, i as usize * 10));
}
for n in 0..100 {
assert!(tree.nth_parent(n).is_some());
}
assert!(tree.nth_parent(100).is_none());
tree.clear();
tree.push(elem(1, 1000, 0, 100));
tree.push(elem(2, 900, 0, 200));
let _ = tree.iter().count();
tree.push(elem(3, 800, 0, 300));
assert_eq!(tree.call_ids().len(), 3);
tree.move_up(30);
tree.push(elem(4, 700, 0, 400));
tree.move_up_prune();
tree.update_spent(500);
tree.clear();
}
#[test]
fn test_drop_behavior() {
{
let mut tree = CallTree::new();
tree.push(elem(1, 1000, 0, 100));
tree.push(elem(2, 900, 0, 200));
tree.push(elem(3, 800, 0, 300));
} assert!(true);
}
#[test]
fn test_contract_call_patterns() {
let mut tree = CallTree::new();
tree.push(elem(1, 10000, 0, 1000));
tree.push(elem(2, 8000, 0, 2000));
tree.push(elem(3, 6000, 0, 3000));
let c = tree.move_up(1500).unwrap();
assert_eq!(c.spent, 1500);
let b = tree.move_up(3000).unwrap();
assert_eq!(b.spent, 3000);
let a = tree.move_up(5000).unwrap();
assert_eq!(a.spent, 5000);
assert!(tree.nth_parent(0).is_none());
tree.push(elem(1, 10000, 0, 1000));
tree.push(elem(2, 5000, 0, 2000));
tree.move_up_prune(); assert_eq!(tree.nth_parent(0).unwrap().contract_id, contract_id(1));
tree.move_up(1000);
tree.push(elem(1, 10000, 0, 1000));
tree.push(elem(2, 3000, 0, 2000));
tree.move_up(500);
tree.push(elem(3, 3000, 0, 3000));
tree.move_up(600);
tree.push(elem(4, 3000, 0, 4000));
tree.move_up(700);
assert_eq!(tree.iter().count(), 4);
tree.clear();
tree.push(elem(10, 10000, 0, 1000));
tree.push(elem(20, 9000, 0, 2000));
tree.push(elem(10, 8000, 0, 1000));
tree.push(elem(30, 7000, 0, 3000)); tree.move_up(300); tree.move_up(100);
tree.push(elem(40, 6000, 0, 4000)); tree.move_up(400); tree.move_up(200);
assert_eq!(tree.nth_parent(0).unwrap().contract_id, contract_id(10));
let elements: Vec<_> = tree.iter().collect();
assert_eq!(elements.len(), 5);
assert_eq!(elements[0].contract_id, contract_id(40)); assert_eq!(elements[1].contract_id, contract_id(30)); assert_eq!(elements[2].contract_id, contract_id(10)); assert_eq!(elements[3].contract_id, contract_id(20)); assert_eq!(elements[4].contract_id, contract_id(10)); }
#[test]
fn test_complex_call_scenarios() {
let mut tree = CallTree::new();
for i in 1u8..=5 {
let limit = 10000u64 - (i as u64) * 1000;
tree.push(elem(i, limit, 0, i as usize * 1000));
}
assert_eq!(tree.call_ids().len(), 5);
for i in (1..=5).rev() {
let e = tree.move_up(i * 100).unwrap();
assert_eq!(e.contract_id, contract_id(i as u8));
}
tree.push(elem(1, 10000, 0, 1000));
tree.push(elem(2, 3000, 0, 2000));
tree.move_up(500); tree.push(elem(3, 3000, 0, 3000));
tree.move_up_prune(); tree.push(elem(4, 3000, 0, 4000));
tree.move_up(700);
let elements: Vec<_> = tree.iter().collect();
let ids: Vec<_> = elements
.iter()
.map(|e| e.contract_id.to_bytes()[0])
.collect();
assert!(ids.contains(&4) && ids.contains(&2) && !ids.contains(&3));
}
}