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