1use bitflags::bitflags;
2use clvmr::MEMPOOL_MODE as CLVM_MEMPOOL_MODE;
3
4#[cfg(feature = "py-bindings")]
5use pyo3::{Bound, FromPyObject, IntoPyObject, PyAny, PyErr, PyResult, Python, types::PyInt};
6
7bitflags! {
8 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
16 pub struct ConsensusFlags: u32 {
17 const CANONICAL_INTS = 0x0001;
21 const NO_UNKNOWN_OPS = 0x0002;
22 const LIMIT_HEAP = 0x0004;
23 const RELAXED_BLS = 0x0008;
24 const ENABLE_KECCAK_OPS_OUTSIDE_GUARD = 0x0100;
25 const DISABLE_OP = 0x0200;
26 const ENABLE_SHA256_TREE = 0x0400;
27 const ENABLE_SECP_OPS = 0x0800;
28
29 const DONT_VALIDATE_SIGNATURE = 0x1_0000;
32
33 const NO_UNKNOWN_CONDS = 0x2_0000;
35
36 const COMPUTE_FINGERPRINT = 0x4_0000;
38
39 const STRICT_ARGS_COUNT = 0x8_0000;
41
42 const COST_CONDITIONS = 0x80_0000;
44
45 const SIMPLE_GENERATOR = 0x100_0000;
47 }
48}
49
50impl ConsensusFlags {
51 #[must_use]
54 const fn from_clvm_flags(clvm: clvmr::chia_dialect::ClvmFlags) -> Self {
55 use clvmr::chia_dialect::ClvmFlags;
56 let mut out = ConsensusFlags::empty();
57 if clvm.contains(ClvmFlags::CANONICAL_INTS) {
58 out = out.union(ConsensusFlags::CANONICAL_INTS);
59 }
60 if clvm.contains(ClvmFlags::NO_UNKNOWN_OPS) {
61 out = out.union(ConsensusFlags::NO_UNKNOWN_OPS);
62 }
63 if clvm.contains(ClvmFlags::LIMIT_HEAP) {
64 out = out.union(ConsensusFlags::LIMIT_HEAP);
65 }
66 if clvm.contains(ClvmFlags::RELAXED_BLS) {
67 out = out.union(ConsensusFlags::RELAXED_BLS);
68 }
69 if clvm.contains(ClvmFlags::ENABLE_KECCAK_OPS_OUTSIDE_GUARD) {
70 out = out.union(ConsensusFlags::ENABLE_KECCAK_OPS_OUTSIDE_GUARD);
71 }
72 if clvm.contains(ClvmFlags::DISABLE_OP) {
73 out = out.union(ConsensusFlags::DISABLE_OP);
74 }
75 if clvm.contains(ClvmFlags::ENABLE_SHA256_TREE) {
76 out = out.union(ConsensusFlags::ENABLE_SHA256_TREE);
77 }
78 if clvm.contains(ClvmFlags::ENABLE_SECP_OPS) {
79 out = out.union(ConsensusFlags::ENABLE_SECP_OPS);
80 }
81 out
82 }
83
84 pub fn to_clvm_flags(self) -> clvmr::chia_dialect::ClvmFlags {
87 use clvmr::chia_dialect::ClvmFlags;
88 let mut out = ClvmFlags::empty();
89 if self.contains(ConsensusFlags::CANONICAL_INTS) {
90 out.insert(ClvmFlags::CANONICAL_INTS);
91 }
92 if self.contains(ConsensusFlags::NO_UNKNOWN_OPS) {
93 out.insert(ClvmFlags::NO_UNKNOWN_OPS);
94 }
95 if self.contains(ConsensusFlags::LIMIT_HEAP) {
96 out.insert(ClvmFlags::LIMIT_HEAP);
97 }
98 if self.contains(ConsensusFlags::RELAXED_BLS) {
99 out.insert(ClvmFlags::RELAXED_BLS);
100 }
101 if self.contains(ConsensusFlags::ENABLE_KECCAK_OPS_OUTSIDE_GUARD) {
102 out.insert(ClvmFlags::ENABLE_KECCAK_OPS_OUTSIDE_GUARD);
103 }
104 if self.contains(ConsensusFlags::DISABLE_OP) {
105 out.insert(ClvmFlags::DISABLE_OP);
106 }
107 if self.contains(ConsensusFlags::ENABLE_SHA256_TREE) {
108 out.insert(ClvmFlags::ENABLE_SHA256_TREE);
109 }
110 if self.contains(ConsensusFlags::ENABLE_SECP_OPS) {
111 out.insert(ClvmFlags::ENABLE_SECP_OPS);
112 }
113 out
114 }
115}
116
117pub const MEMPOOL_MODE: ConsensusFlags = ConsensusFlags::from_clvm_flags(CLVM_MEMPOOL_MODE)
119 .union(ConsensusFlags::NO_UNKNOWN_CONDS)
120 .union(ConsensusFlags::STRICT_ARGS_COUNT);
121
122impl Default for ConsensusFlags {
123 fn default() -> Self {
124 Self::empty()
125 }
126}
127
128#[cfg(feature = "py-bindings")]
129impl<'py> FromPyObject<'py, 'py> for ConsensusFlags {
130 type Error = PyErr;
131
132 fn extract(obj: pyo3::Borrowed<'py, 'py, PyAny>) -> PyResult<Self> {
133 let b: u32 = obj.extract()?;
134 Ok(ConsensusFlags::from_bits_truncate(b))
135 }
136}
137
138#[cfg(feature = "py-bindings")]
139impl<'py> IntoPyObject<'py> for ConsensusFlags {
140 type Target = PyInt;
141 type Output = Bound<'py, Self::Target>;
142 type Error = std::convert::Infallible;
143
144 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
145 Ok(PyInt::new(py, self.bits()))
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::ConsensusFlags;
152 use bitflags::Flags;
153 use clvmr::chia_dialect::ClvmFlags;
154
155 #[test]
157 fn no_overlapping_bits() {
158 let all = ConsensusFlags::FLAGS;
159 for (i, a) in all.iter().enumerate() {
160 for b in &all[i + 1..] {
161 let a_bits = a.value().bits();
162 let b_bits = b.value().bits();
163 assert_eq!(
164 a_bits & b_bits,
165 0,
166 "overlapping bits between {:?} ({a_bits:x}) and {:?} ({b_bits:x})",
167 a.value(),
168 b.value(),
169 );
170 }
171 }
172 }
173
174 #[test]
177 fn clvm_flags_bits_match_consensus_flags() {
178 let clvm_flags = ClvmFlags::FLAGS;
179 for flag in clvm_flags {
180 assert!(flag.is_named());
181 let name = flag.name();
182 let clvm_bits = flag.value().bits();
183 let Some(consensus) = ConsensusFlags::from_name(name) else {
184 panic!(
185 "ClvmFlags flag {name} has no corresponding ConsensusFlags; \
186 every ClvmFlags flag must exist in ConsensusFlags"
187 )
188 };
189 assert_eq!(
190 clvm_bits,
191 consensus.bits(),
192 "ClvmFlags and ConsensusFlags must have the same bits for flag {:?} (name = {name}); \
193 we rely on exact bit compatibility",
194 flag.value(),
195 );
196 }
197 }
198
199 #[test]
202 fn shared_flags_round_trip_through_conversion() {
203 for flag in ClvmFlags::FLAGS {
204 assert!(flag.is_named());
205 let clvm = *flag.value();
206 let name = flag.name();
207
208 let consensus = ConsensusFlags::from_clvm_flags(clvm);
209 let expected = ConsensusFlags::from_name(name).unwrap();
210 assert_eq!(
211 consensus, expected,
212 "from_clvm_flags did not convert ClvmFlags::{name} correctly"
213 );
214
215 let back = expected.to_clvm_flags();
216 assert_eq!(
217 back, clvm,
218 "to_clvm_flags did not convert ConsensusFlags::{name} back to ClvmFlags::{name}"
219 );
220 }
221
222 let consensus_only =
225 ConsensusFlags::all().difference(ConsensusFlags::from_clvm_flags(ClvmFlags::all()));
226 assert!(
227 !consensus_only.is_empty(),
228 "ConsensusFlags should be a strict superset of ClvmFlags"
229 );
230 assert!(
231 consensus_only.to_clvm_flags().is_empty(),
232 "consensus-only flags must not appear in to_clvm_flags output"
233 );
234 }
235}