1+ use bdk_core:: CheckPoint ;
2+ use bitcoin:: BlockHash ;
3+ use bitcoin:: hashes:: Hash ;
4+
5+ #[ test]
6+ fn test_skiplist_indices ( ) {
7+ // Create a long chain to test skiplist
8+ let mut cp = CheckPoint :: new ( 0 , BlockHash :: all_zeros ( ) ) ;
9+ assert_eq ! ( cp. index( ) , 0 ) ;
10+
11+ for height in 1 ..=500 {
12+ let hash = BlockHash :: from_byte_array ( [ height as u8 ; 32 ] ) ;
13+ cp = cp. push ( height, hash) . unwrap ( ) ;
14+ assert_eq ! ( cp. index( ) , height) ;
15+ }
16+
17+ // Test that skip pointers are set correctly
18+ // At index 100, 200, 300, 400, 500 we should have skip pointers
19+ assert_eq ! ( cp. index( ) , 500 ) ;
20+
21+ // Navigate to index 400 and check skip pointer
22+ let mut current = cp. clone ( ) ;
23+ for _ in 0 ..100 {
24+ current = current. prev ( ) . unwrap ( ) ;
25+ }
26+ assert_eq ! ( current. index( ) , 400 ) ;
27+
28+ // Check that skip pointer exists at index 400
29+ if let Some ( skip) = current. skip ( ) {
30+ assert_eq ! ( skip. index( ) , 300 ) ;
31+ } else {
32+ panic ! ( "Expected skip pointer at index 400" ) ;
33+ }
34+
35+ // Navigate to index 300 and check skip pointer
36+ for _ in 0 ..100 {
37+ current = current. prev ( ) . unwrap ( ) ;
38+ }
39+ assert_eq ! ( current. index( ) , 300 ) ;
40+
41+ if let Some ( skip) = current. skip ( ) {
42+ assert_eq ! ( skip. index( ) , 200 ) ;
43+ } else {
44+ panic ! ( "Expected skip pointer at index 300" ) ;
45+ }
46+
47+ // Navigate to index 100 and check skip pointer
48+ for _ in 0 ..200 {
49+ current = current. prev ( ) . unwrap ( ) ;
50+ }
51+ assert_eq ! ( current. index( ) , 100 ) ;
52+
53+ if let Some ( skip) = current. skip ( ) {
54+ assert_eq ! ( skip. index( ) , 0 ) ;
55+ } else {
56+ panic ! ( "Expected skip pointer at index 100" ) ;
57+ }
58+ }
59+
60+ #[ test]
61+ fn test_skiplist_get_performance ( ) {
62+ // Create a very long chain
63+ let mut cp = CheckPoint :: new ( 0 , BlockHash :: all_zeros ( ) ) ;
64+
65+ for height in 1 ..=1000 {
66+ let hash = BlockHash :: from_byte_array ( [ ( height % 256 ) as u8 ; 32 ] ) ;
67+ cp = cp. push ( height, hash) . unwrap ( ) ;
68+ }
69+
70+ // Test that get() can find checkpoints efficiently
71+ // This should use skip pointers to navigate quickly
72+
73+ // Verify the chain was built correctly
74+ assert_eq ! ( cp. height( ) , 1000 ) ;
75+ assert_eq ! ( cp. index( ) , 1000 ) ;
76+
77+ // Find checkpoint near the beginning
78+ if let Some ( found) = cp. get ( 50 ) {
79+ assert_eq ! ( found. height( ) , 50 ) ;
80+ assert_eq ! ( found. index( ) , 50 ) ;
81+ } else {
82+ // Debug: print the first few checkpoints
83+ let mut current = cp. clone ( ) ;
84+ println ! ( "First 10 checkpoints:" ) ;
85+ for _ in 0 ..10 {
86+ println ! ( "Height: {}, Index: {}" , current. height( ) , current. index( ) ) ;
87+ if let Some ( prev) = current. prev ( ) {
88+ current = prev;
89+ } else {
90+ break ;
91+ }
92+ }
93+ panic ! ( "Could not find checkpoint at height 50" ) ;
94+ }
95+
96+ // Find checkpoint in the middle
97+ if let Some ( found) = cp. get ( 500 ) {
98+ assert_eq ! ( found. height( ) , 500 ) ;
99+ assert_eq ! ( found. index( ) , 500 ) ;
100+ } else {
101+ panic ! ( "Could not find checkpoint at height 500" ) ;
102+ }
103+
104+ // Find checkpoint near the end
105+ if let Some ( found) = cp. get ( 950 ) {
106+ assert_eq ! ( found. height( ) , 950 ) ;
107+ assert_eq ! ( found. index( ) , 950 ) ;
108+ } else {
109+ panic ! ( "Could not find checkpoint at height 950" ) ;
110+ }
111+
112+ // Test non-existent checkpoint
113+ assert ! ( cp. get( 1001 ) . is_none( ) ) ;
114+ }
115+
116+ #[ test]
117+ fn test_skiplist_floor_at ( ) {
118+ let mut cp = CheckPoint :: new ( 0 , BlockHash :: all_zeros ( ) ) ;
119+
120+ // Create sparse chain with gaps
121+ for height in [ 10 , 50 , 100 , 150 , 200 , 300 , 400 , 500 ] {
122+ let hash = BlockHash :: from_byte_array ( [ height as u8 ; 32 ] ) ;
123+ cp = cp. push ( height, hash) . unwrap ( ) ;
124+ }
125+
126+ // Test floor_at with skip pointers
127+ let floor = cp. floor_at ( 250 ) . unwrap ( ) ;
128+ assert_eq ! ( floor. height( ) , 200 ) ;
129+
130+ let floor = cp. floor_at ( 99 ) . unwrap ( ) ;
131+ assert_eq ! ( floor. height( ) , 50 ) ;
132+
133+ let floor = cp. floor_at ( 500 ) . unwrap ( ) ;
134+ assert_eq ! ( floor. height( ) , 500 ) ;
135+
136+ let floor = cp. floor_at ( 600 ) . unwrap ( ) ;
137+ assert_eq ! ( floor. height( ) , 500 ) ;
138+ }
139+
140+ #[ test]
141+ fn test_skiplist_insert_maintains_indices ( ) {
142+ let mut cp = CheckPoint :: new ( 0 , BlockHash :: all_zeros ( ) ) ;
143+
144+ // Build initial chain
145+ for height in [ 10 , 20 , 30 , 40 , 50 ] {
146+ let hash = BlockHash :: from_byte_array ( [ height as u8 ; 32 ] ) ;
147+ cp = cp. push ( height, hash) . unwrap ( ) ;
148+ }
149+
150+ // Insert a block in the middle
151+ let hash = BlockHash :: from_byte_array ( [ 25 ; 32 ] ) ;
152+ cp = cp. insert ( 25 , hash) ;
153+
154+ // Check that indices are maintained correctly
155+ let check = cp. get ( 50 ) . unwrap ( ) ;
156+ assert_eq ! ( check. index( ) , 6 ) ; // 0, 10, 20, 25, 30, 40, 50
157+
158+ let check = cp. get ( 25 ) . unwrap ( ) ;
159+ assert_eq ! ( check. index( ) , 3 ) ;
160+
161+ // Check the full chain has correct indices
162+ let mut current = cp. clone ( ) ;
163+ let expected_heights = vec ! [ 50 , 40 , 30 , 25 , 20 , 10 , 0 ] ;
164+ let expected_indices = vec ! [ 6 , 5 , 4 , 3 , 2 , 1 , 0 ] ;
165+
166+ for ( expected_height, expected_index) in expected_heights. iter ( ) . zip ( expected_indices. iter ( ) ) {
167+ assert_eq ! ( current. height( ) , * expected_height) ;
168+ assert_eq ! ( current. index( ) , * expected_index) ;
169+ if * expected_height > 0 {
170+ current = current. prev ( ) . unwrap ( ) ;
171+ }
172+ }
173+ }
174+
175+ #[ test]
176+ fn test_skiplist_range_uses_skip_pointers ( ) {
177+ let mut cp = CheckPoint :: new ( 0 , BlockHash :: all_zeros ( ) ) ;
178+
179+ // Create a chain with 500 checkpoints
180+ for height in 1 ..=500 {
181+ let hash = BlockHash :: from_byte_array ( [ ( height % 256 ) as u8 ; 32 ] ) ;
182+ cp = cp. push ( height, hash) . unwrap ( ) ;
183+ }
184+
185+ // Test range iteration
186+ let range_items: Vec < _ > = cp. range ( 100 ..=200 ) . collect ( ) ;
187+ assert_eq ! ( range_items. len( ) , 101 ) ;
188+ assert_eq ! ( range_items. first( ) . unwrap( ) . height( ) , 200 ) ;
189+ assert_eq ! ( range_items. last( ) . unwrap( ) . height( ) , 100 ) ;
190+
191+ // Test open range
192+ let range_items: Vec < _ > = cp. range ( 450 ..) . collect ( ) ;
193+ assert_eq ! ( range_items. len( ) , 51 ) ;
194+ assert_eq ! ( range_items. first( ) . unwrap( ) . height( ) , 500 ) ;
195+ assert_eq ! ( range_items. last( ) . unwrap( ) . height( ) , 450 ) ;
196+ }
0 commit comments