Skip to main content

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