bobcat_features/
lib.rs

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