Skip to main content

boundless_market/
selector.rs

1// Copyright 2026 Boundless Foundation, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Selector utility functions.
16
17use std::collections::HashMap;
18use std::fmt::{self, Display, Formatter};
19
20use alloy_primitives::FixedBytes;
21use clap::ValueEnum;
22use risc0_aggregation::SetInclusionReceiptVerifierParameters;
23use risc0_ethereum_contracts::selector::Selector;
24use risc0_zkvm::sha::{Digest, Digestible};
25use thiserror::Error;
26
27use crate::contracts::UNSPECIFIED_SELECTOR;
28use crate::util::is_dev_mode;
29
30#[derive(Debug, Error)]
31#[non_exhaustive]
32/// Errors related to extended selectors.
33pub enum SelectorExtError {
34    /// Unsupported selector error.
35    #[error("Unsupported selector")]
36    UnsupportedSelector,
37    /// No verifier parameters error.
38    #[error("Selector {0} does not have verifier parameters")]
39    NoVerifierParameters(SelectorExt),
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43#[non_exhaustive]
44/// Types of extended selectors.
45pub enum SelectorExtType {
46    /// A fake receipt selector used for testing and development.
47    FakeReceipt,
48    /// Groth16 proof selector.
49    Groth16,
50    /// Set verifier selector.
51    SetVerifier,
52    /// Blake3 Groth16 selector.
53    Blake3Groth16,
54    /// Fake Blake3 Groth16 selector
55    FakeBlake3Groth16,
56}
57
58#[repr(u32)]
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60#[non_exhaustive]
61/// Extended selectors for various proof types.
62pub enum SelectorExt {
63    /// A fake receipt selector used for testing and development.
64    FakeReceipt = 0xFFFFFFFF,
65    /// A fake Blake3 Groth16 proof selector.
66    FakeBlake3Groth16 = 0xFFFF0000,
67    /// Groth16 proof selector version 3.0.
68    Groth16V3_0 = Selector::Groth16V3_0 as u32,
69    /// Set verifier selector version 0.9.
70    SetVerifierV0_9 = Selector::SetVerifierV0_9 as u32,
71    /// Blake3 Groth16 selector version 0.1.
72    Blake3Groth16V0_1 = 0x62f049f6,
73}
74
75impl Display for SelectorExt {
76    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
77        write!(f, "{:#010x}", *self as u32)
78    }
79}
80
81impl TryFrom<u32> for SelectorExt {
82    type Error = SelectorExtError;
83
84    fn try_from(value: u32) -> Result<Self, Self::Error> {
85        match value {
86            0xFFFFFFFF => Ok(SelectorExt::FakeReceipt),
87            0xFFFF0000 => Ok(SelectorExt::FakeBlake3Groth16),
88            0x73c457ba => Ok(SelectorExt::Groth16V3_0),
89            0x242f9d5b => Ok(SelectorExt::SetVerifierV0_9),
90            0x62f049f6 => Ok(SelectorExt::Blake3Groth16V0_1),
91            _ => Err(SelectorExtError::UnsupportedSelector),
92        }
93    }
94}
95
96impl TryFrom<Selector> for SelectorExt {
97    type Error = SelectorExtError;
98
99    fn try_from(value: Selector) -> Result<Self, Self::Error> {
100        Self::try_from(value as u32)
101    }
102}
103
104impl SelectorExt {
105    /// Get the latest groth16 selector.
106    pub fn groth16_latest() -> SelectorExt {
107        SelectorExt::Groth16V3_0
108    }
109
110    /// Get the latest set verifier selector.
111    pub fn set_inclusion_latest() -> SelectorExt {
112        SelectorExt::SetVerifierV0_9
113    }
114
115    /// Get the latest blake3 groth16 selector.
116    pub fn blake3_groth16_latest() -> SelectorExt {
117        // Currently, we only have one blake3 groth16 selector.
118        SelectorExt::Blake3Groth16V0_1
119    }
120
121    /// Create a `SelectorExt` from a 4-byte array.
122    pub fn from_bytes(bytes: [u8; 4]) -> Option<Self> {
123        Self::try_from(u32::from_be_bytes(bytes)).ok()
124    }
125
126    /// Returns the type of the selector.
127    pub fn get_type(self) -> SelectorExtType {
128        match self {
129            SelectorExt::FakeReceipt => SelectorExtType::FakeReceipt,
130            SelectorExt::FakeBlake3Groth16 => SelectorExtType::FakeBlake3Groth16,
131            SelectorExt::Groth16V3_0 => SelectorExtType::Groth16,
132            SelectorExt::SetVerifierV0_9 => SelectorExtType::SetVerifier,
133            SelectorExt::Blake3Groth16V0_1 => SelectorExtType::Blake3Groth16,
134        }
135    }
136}
137
138/// Define the selector types.
139///
140/// This is used to indicate the type of proof that is being requested.
141#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, ValueEnum)]
142#[non_exhaustive]
143pub enum ProofType {
144    /// Any proof type.
145    #[default]
146    Any,
147    /// Groth16 proof type.
148    Groth16,
149    /// Inclusion proof type.
150    Inclusion,
151    /// BitVM compatible blake3 Groth16 proof type.
152    Blake3Groth16,
153}
154
155/// A struct to hold the supported selectors.
156#[derive(Clone, Debug)]
157pub struct SupportedSelectors {
158    /// A map of selectors to their proof type.
159    pub selectors: HashMap<FixedBytes<4>, ProofType>,
160}
161
162impl Default for SupportedSelectors {
163    fn default() -> Self {
164        let mut supported_selectors = Self::new()
165            .with_selector(UNSPECIFIED_SELECTOR, ProofType::Any)
166            .with_selector(
167                FixedBytes::from(SelectorExt::groth16_latest() as u32),
168                ProofType::Groth16,
169            )
170            .with_selector(
171                FixedBytes::from(SelectorExt::blake3_groth16_latest() as u32),
172                ProofType::Blake3Groth16,
173            );
174        if is_dev_mode() {
175            supported_selectors = supported_selectors
176                .with_selector(FixedBytes::from(SelectorExt::FakeReceipt as u32), ProofType::Any)
177                .with_selector(
178                    FixedBytes::from(SelectorExt::FakeBlake3Groth16 as u32),
179                    ProofType::Blake3Groth16,
180                )
181        }
182        supported_selectors
183    }
184}
185
186impl SupportedSelectors {
187    /// Create a new `SupportedSelectors` struct.
188    pub fn new() -> Self {
189        Self { selectors: HashMap::new() }
190    }
191
192    /// Add a selector to the supported selectors, taking ownership.
193    pub fn with_selector(mut self, selector: FixedBytes<4>, proof_type: ProofType) -> Self {
194        self.add_selector(selector, proof_type);
195        self
196    }
197
198    /// Add a selector to the supported selectors.
199    pub fn add_selector(&mut self, selector: FixedBytes<4>, proof_type: ProofType) -> &mut Self {
200        self.selectors.insert(selector, proof_type);
201        self
202    }
203
204    /// Remove a selector from the supported selectors.
205    pub fn remove(&mut self, selector: FixedBytes<4>) {
206        if self.selectors.contains_key(&selector) {
207            self.selectors.remove(&selector);
208        }
209    }
210
211    /// Check if a selector is supported.
212    pub fn is_supported(&self, selector: FixedBytes<4>) -> bool {
213        self.selectors.contains_key(&selector)
214    }
215
216    /// Check the proof type, returning `None` if unsupported.
217    pub fn proof_type(&self, selector: FixedBytes<4>) -> Option<ProofType> {
218        self.selectors.get(&selector).cloned()
219    }
220
221    /// Add a selector calculated from the given set builder image ID.
222    ///
223    /// The selector is calculated by constructing the [SetInclusionReceiptVerifierParameters]
224    /// using the given image ID. The resulting selector has [ProofType::Inclusion].
225    pub fn with_set_builder_image_id(&self, set_builder_image_id: impl Into<Digest>) -> Self {
226        let verifier_params =
227            SetInclusionReceiptVerifierParameters { image_id: set_builder_image_id.into() }
228                .digest();
229        let set_builder_selector: FixedBytes<4> =
230            verifier_params.as_bytes()[0..4].try_into().unwrap();
231        let mut selectors = self.selectors.clone();
232        selectors.insert(set_builder_selector, ProofType::Inclusion);
233
234        Self { selectors }
235    }
236}
237
238/// Check if a selector is a groth16 selector.
239pub fn is_groth16_selector(selector: FixedBytes<4>) -> bool {
240    let sel = SelectorExt::from_bytes(selector.into());
241    match sel {
242        Some(selector) => {
243            selector.get_type() == SelectorExtType::FakeReceipt
244                || selector.get_type() == SelectorExtType::Groth16
245        }
246        None => false,
247    }
248}
249
250/// Check if a selector is a blake3 groth16 selector.
251pub fn is_blake3_groth16_selector(selector: FixedBytes<4>) -> bool {
252    let sel = SelectorExt::from_bytes(selector.into());
253    match sel {
254        Some(selector) => {
255            selector.get_type() == SelectorExtType::FakeBlake3Groth16
256                || selector.get_type() == SelectorExtType::Blake3Groth16
257        }
258        None => false,
259    }
260}
261
262#[cfg(test)]
263mod tests {
264    use super::*;
265
266    #[test]
267    fn test_supported_selectors() {
268        let mut supported_selectors = SupportedSelectors::new();
269        let selector = FixedBytes::from(SelectorExt::groth16_latest() as u32);
270        supported_selectors = supported_selectors.with_selector(selector, ProofType::Groth16);
271        assert!(supported_selectors.is_supported(selector));
272        supported_selectors.remove(selector);
273        assert!(!supported_selectors.is_supported(selector));
274    }
275
276    #[test]
277    fn test_is_groth16_selector() {
278        let selector = FixedBytes::from(SelectorExt::groth16_latest() as u32);
279        assert!(is_groth16_selector(selector));
280        let fake_selector = FixedBytes::from(SelectorExt::FakeReceipt as u32);
281        assert!(is_groth16_selector(fake_selector));
282    }
283
284    #[test]
285    fn test_is_blake3_groth16_selector() {
286        let selector = FixedBytes::from(SelectorExt::blake3_groth16_latest() as u32);
287        assert!(is_blake3_groth16_selector(selector));
288        let fake_selector = FixedBytes::from(SelectorExt::FakeBlake3Groth16 as u32);
289        assert!(is_blake3_groth16_selector(fake_selector));
290    }
291}