bobcat_features/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3pub use bobcat_storage::{
4    const_keccak256_two_off_curve, keccak256, storage_load, storage_store, U,
5};
6
7pub use bobcat_entry::block_timestamp;
8
9#[macro_export]
10macro_rules! bobcat_features {
11    ($($feature_name:ident),* $(,)?) => {
12        pub const _FEATURE_COUNT: u8 = 0 $(+ { let _ = stringify!($feature_name); 1 })*;
13
14        $(
15            paste::paste! {
16                pub const [<FEATURE_ID_ $feature_name:upper>]: $crate::U =
17                    $crate::const_keccak256_two_off_curve(
18                        b"bobcat.features.",
19                        stringify!($feature_name).as_bytes()
20                    );
21
22                #[allow(unused)]
23                pub fn [<feature_is_ $feature_name:lower>]() -> bool {
24                    $crate::storage_load(&[<FEATURE_ID_ $feature_name:upper>]).is_some()
25                }
26
27                pub fn [<feature_set_ $feature_name:lower>](v: bool) {
28                    $crate::storage_store(
29                        &[<FEATURE_ID_ $feature_name:upper>],
30                        &$crate::U::from(v)
31                    )
32                }
33
34                #[allow(unused)]
35                macro_rules! [<FEATURE_IF_ $feature_name:upper>] {
36                    ($on_block:block else $off_block:block) => {
37                        if [<feature_is_ $feature_name:lower>]() {
38                            $on_block
39                        } else {
40                            $off_block
41                        }
42                    };
43                }
44            }
45        )*
46
47        #[allow(unused)]
48        pub fn feature_pack() -> $crate::U {
49            let mut r = $crate::U::default();
50            #[allow(unused_assignments)]
51            let mut i = 0;
52            $(
53                paste::paste! {
54                    if [<feature_is_ $feature_name:lower>]() {
55                        let byte_index = 31 - (i / 8);
56                        let bit_position = i % 8;
57                        r[byte_index] |= 1 << bit_position;
58                    }
59                    i += 1;
60                }
61            )*
62            r
63        }
64    };
65}
66
67#[macro_export]
68macro_rules! FEATURE_PICK {
69    ($($feature_name:ident($weight:expr)),* $(,)?) => {
70        {
71            let choice = u16::from($crate::keccak256(&$crate::block_timestamp().to_be_bytes())) % 100;
72            let mut cum = 0u16;
73            let mut found = false;
74            $(
75                if !found {
76                    if choice < cum + $weight {
77                        paste::paste! {
78                            [<feature_set_ $feature_name:lower>](true);
79                        }
80                        found = true;
81                    } else {
82                        cum += $weight;
83                    }
84                }
85            )*
86        }
87    };
88}
89
90#[macro_export]
91macro_rules! FEATURE_MATCH {
92    ($($feature:ident => $expr:expr),+ , * => $default:expr $(,)?) => {
93        paste::paste! {
94            $(
95                if [<feature_is_ $feature:lower>]() {
96                    $expr
97                } else
98            )+
99            {
100                $default
101            }
102        }
103    };
104    ($($feature:ident => $expr:expr),+ , _ => $default:expr $(,)?) => {
105        FEATURE_MATCH! {
106            $($feature => $expr,)+
107            * => $default
108        }
109    };
110    ($($feature:ident => $expr:expr),+ $(,)?) => {
111        FEATURE_MATCH! {
112            $($feature => $expr,)+
113            * => ()
114        }
115    };
116}
117
118#[cfg(all(test, feature = "std"))]
119mod test_1 {
120    use bobcat_entry::{entry_host::set_block_timestamp, U};
121
122    use bobcat_storage::storage_host::storage_clear;
123
124    bobcat_features!(test123, swag);
125
126    #[test]
127    fn test_features_1() {
128        assert_eq!(2, _FEATURE_COUNT);
129        feature_set_test123(true);
130        assert!(FEATURE_IF_TEST123!({ true } else { false }));
131        feature_set_test123(false);
132        assert!(!feature_is_test123());
133        let mut test123_count = 0;
134        let mut swag_count = 0;
135        let mut else_count = 0;
136        for i in 0..100_000 {
137            set_block_timestamp(i);
138            FEATURE_PICK!(test123(40), swag(30));
139            FEATURE_MATCH! {
140                test123 => test123_count += 1,
141                swag => swag_count += 1,
142                * => else_count += 1
143            };
144            feature_set_test123(false);
145            feature_set_swag(false);
146        }
147        assert!(40_000 >= test123_count || 39_900 <= test123_count);
148        assert!(30_000 >= swag_count || 29_900 <= swag_count);
149        assert!(50_000 >= else_count || 49_900 <= else_count);
150        feature_set_test123(true);
151        feature_set_swag(true);
152        assert_eq!(U::from(3u32), feature_pack());
153        feature_set_swag(false);
154        assert_eq!(U::ONE, feature_pack());
155        storage_clear();
156    }
157}
158
159#[cfg(all(test, feature = "std", feature = "alloy-enabled"))]
160mod test_2 {
161    use std::str::FromStr;
162
163    use bobcat_entry::U;
164
165    use bobcat_storage::storage_host::storage_clear;
166
167    bobcat_features!(
168        F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, F20,
169        F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31, F32, F33, F34, F35, F36, F37, F38,
170        F39, F40, F41, F42, F43, F44, F45, F46, F47, F48, F49, F50, F51, F52, F53, F54, F55, F56,
171        F57, F58, F59, F60, F61, F62, F63, F64, F65, F66, F67, F68, F69, F70, F71, F72, F73, F74,
172        F75, F76, F77, F78, F79, F80, F81, F82, F83, F84, F85, F86, F87, F88, F89, F90, F91, F92,
173        F93, F94, F95, F96, F97, F98, F99, F100, F101, F102, F103, F104, F105, F106, F107, F108,
174        F109, F110, F111, F112, F113, F114, F115, F116, F117, F118, F119, F120, F121, F122, F123,
175        F124, F125, F126, F127, F128, F129, F130, F131, F132, F133, F134, F135, F136, F137, F138,
176        F139, F140, F141, F142, F143, F144, F145, F146, F147, F148, F149, F150, F151, F152, F153,
177        F154, F155, F156, F157, F158, F159, F160, F161, F162, F163, F164, F165, F166, F167, F168,
178        F169, F170, F171, F172, F173, F174, F175, F176, F177, F178, F179, F180, F181, F182, F183,
179        F184, F185, F186, F187, F188, F189, F190, F191, F192, F193, F194, F195, F196, F197, F198,
180        F199, F200, F201, F202, F203, F204, F205, F206, F207, F208, F209, F210, F211, F212, F213,
181        F214, F215, F216, F217, F218, F219, F220, F221, F222, F223, F224, F225, F226, F227, F228,
182        F229, F230, F231, F232, F233, F234, F235, F236, F237, F238, F239, F240, F241, F242, F243,
183        F244, F245, F246, F247, F248, F249, F250, F251, F252, F253, F254, F255,
184    );
185
186    #[test]
187    fn test() {
188        feature_set_f1(true);
189        feature_set_f2(true);
190        feature_set_f3(true);
191        feature_set_f4(true);
192        feature_set_f5(true);
193        feature_set_f6(true);
194        feature_set_f7(true);
195        feature_set_f8(true);
196        feature_set_f9(true);
197        feature_set_f10(true);
198        feature_set_f11(true);
199        feature_set_f12(true);
200        feature_set_f13(true);
201        feature_set_f14(true);
202        feature_set_f15(true);
203        feature_set_f16(true);
204        feature_set_f17(true);
205        feature_set_f18(true);
206        feature_set_f19(true);
207        feature_set_f20(true);
208        feature_set_f21(true);
209        feature_set_f22(true);
210        feature_set_f23(true);
211        feature_set_f24(true);
212        feature_set_f25(true);
213        feature_set_f26(true);
214        feature_set_f27(true);
215        feature_set_f28(true);
216        feature_set_f29(true);
217        feature_set_f30(true);
218        feature_set_f31(true);
219        feature_set_f32(true);
220        feature_set_f33(true);
221        feature_set_f34(true);
222        feature_set_f35(true);
223        feature_set_f36(true);
224        feature_set_f37(true);
225        feature_set_f38(true);
226        feature_set_f39(true);
227        feature_set_f40(true);
228        feature_set_f41(true);
229        feature_set_f42(true);
230        feature_set_f43(true);
231        feature_set_f44(true);
232        feature_set_f45(true);
233        feature_set_f46(true);
234        feature_set_f47(true);
235        feature_set_f48(true);
236        feature_set_f49(true);
237        feature_set_f50(true);
238        feature_set_f51(true);
239        feature_set_f52(true);
240        feature_set_f53(true);
241        feature_set_f54(true);
242        feature_set_f55(true);
243        feature_set_f56(true);
244        feature_set_f57(true);
245        feature_set_f58(true);
246        feature_set_f59(true);
247        feature_set_f60(true);
248        feature_set_f61(true);
249        feature_set_f62(true);
250        feature_set_f63(true);
251        feature_set_f64(true);
252        feature_set_f65(true);
253        feature_set_f66(true);
254        feature_set_f67(true);
255        feature_set_f68(true);
256        feature_set_f69(true);
257        feature_set_f70(true);
258        feature_set_f71(true);
259        feature_set_f72(true);
260        feature_set_f73(true);
261        feature_set_f74(true);
262        feature_set_f75(true);
263        feature_set_f76(true);
264        feature_set_f77(true);
265        feature_set_f78(true);
266        feature_set_f79(true);
267        feature_set_f80(true);
268        feature_set_f81(true);
269        feature_set_f82(true);
270        feature_set_f83(true);
271        feature_set_f84(true);
272        feature_set_f85(true);
273        feature_set_f86(true);
274        feature_set_f87(true);
275        feature_set_f88(true);
276        feature_set_f89(true);
277        feature_set_f90(true);
278        feature_set_f91(true);
279        feature_set_f92(true);
280        feature_set_f93(true);
281        feature_set_f94(true);
282        feature_set_f95(true);
283        feature_set_f96(true);
284        feature_set_f97(true);
285        feature_set_f98(true);
286        feature_set_f99(true);
287        feature_set_f100(true);
288        feature_set_f101(true);
289        feature_set_f102(true);
290        feature_set_f103(true);
291        feature_set_f104(true);
292        feature_set_f105(true);
293        feature_set_f106(true);
294        feature_set_f107(true);
295        feature_set_f108(true);
296        feature_set_f109(true);
297        feature_set_f110(true);
298        feature_set_f111(true);
299        feature_set_f112(true);
300        feature_set_f113(true);
301        feature_set_f114(true);
302        feature_set_f115(true);
303        feature_set_f116(true);
304        feature_set_f117(true);
305        feature_set_f118(true);
306        feature_set_f119(true);
307        feature_set_f120(true);
308        feature_set_f121(true);
309        feature_set_f122(true);
310        feature_set_f123(true);
311        feature_set_f124(true);
312        feature_set_f125(true);
313        feature_set_f126(true);
314        feature_set_f127(true);
315        feature_set_f128(true);
316        feature_set_f129(true);
317        feature_set_f130(true);
318        feature_set_f131(true);
319        feature_set_f132(true);
320        feature_set_f133(true);
321        feature_set_f134(true);
322        feature_set_f135(true);
323        feature_set_f136(true);
324        feature_set_f137(true);
325        feature_set_f138(true);
326        feature_set_f139(true);
327        feature_set_f140(true);
328        feature_set_f141(true);
329        feature_set_f142(true);
330        feature_set_f143(true);
331        feature_set_f144(true);
332        feature_set_f145(true);
333        feature_set_f146(true);
334        feature_set_f147(true);
335        feature_set_f148(true);
336        feature_set_f149(true);
337        feature_set_f150(true);
338        feature_set_f151(true);
339        feature_set_f152(true);
340        feature_set_f153(true);
341        feature_set_f154(true);
342        feature_set_f155(true);
343        feature_set_f156(true);
344        feature_set_f157(true);
345        feature_set_f158(true);
346        feature_set_f159(true);
347        feature_set_f160(true);
348        feature_set_f161(true);
349        feature_set_f162(true);
350        feature_set_f163(true);
351        feature_set_f164(true);
352        feature_set_f165(true);
353        feature_set_f166(true);
354        feature_set_f167(true);
355        feature_set_f168(true);
356        feature_set_f169(true);
357        feature_set_f170(true);
358        feature_set_f171(true);
359        feature_set_f172(true);
360        feature_set_f173(true);
361        feature_set_f174(true);
362        feature_set_f175(true);
363        feature_set_f176(true);
364        feature_set_f177(true);
365        feature_set_f178(true);
366        feature_set_f179(true);
367        feature_set_f180(true);
368        feature_set_f181(true);
369        feature_set_f182(true);
370        feature_set_f183(true);
371        feature_set_f184(true);
372        feature_set_f185(true);
373        feature_set_f186(true);
374        feature_set_f187(true);
375        feature_set_f188(true);
376        feature_set_f189(true);
377        feature_set_f190(true);
378        feature_set_f191(true);
379        feature_set_f192(true);
380        feature_set_f193(true);
381        feature_set_f194(true);
382        feature_set_f195(true);
383        feature_set_f196(true);
384        feature_set_f197(true);
385        feature_set_f198(true);
386        feature_set_f199(true);
387        feature_set_f200(true);
388        feature_set_f201(true);
389        feature_set_f202(true);
390        feature_set_f203(true);
391        feature_set_f204(true);
392        feature_set_f205(true);
393        feature_set_f206(true);
394        feature_set_f207(true);
395        feature_set_f208(true);
396        feature_set_f209(true);
397        feature_set_f210(true);
398        feature_set_f211(true);
399        feature_set_f212(true);
400        feature_set_f213(true);
401        feature_set_f214(true);
402        feature_set_f215(true);
403        feature_set_f216(true);
404        feature_set_f217(true);
405        feature_set_f218(true);
406        feature_set_f219(true);
407        feature_set_f220(true);
408        feature_set_f221(true);
409        feature_set_f222(true);
410        feature_set_f223(true);
411        feature_set_f224(true);
412        feature_set_f225(true);
413        feature_set_f226(true);
414        feature_set_f227(true);
415        feature_set_f228(true);
416        feature_set_f229(true);
417        feature_set_f230(true);
418        feature_set_f231(true);
419        feature_set_f232(true);
420        feature_set_f233(true);
421        feature_set_f234(true);
422        feature_set_f235(true);
423        feature_set_f236(true);
424        feature_set_f237(true);
425        feature_set_f238(true);
426        feature_set_f239(true);
427        feature_set_f240(true);
428        feature_set_f241(true);
429        feature_set_f242(true);
430        feature_set_f243(true);
431        feature_set_f244(true);
432        feature_set_f245(true);
433        feature_set_f246(true);
434        feature_set_f247(true);
435        feature_set_f248(true);
436        feature_set_f249(true);
437        feature_set_f250(true);
438        feature_set_f251(true);
439        feature_set_f252(true);
440        feature_set_f253(true);
441        feature_set_f254(true);
442        feature_set_f255(true);
443        assert_eq!(
444            U::from_str(
445                "57896044618658097711785492504343953926634992332820282019728792003956564819967"
446            )
447            .unwrap(),
448            feature_pack()
449        );
450        storage_clear();
451    }
452}