1use crate::{
2 Blob, BlobData, BlobIndex, ContractAction, ContractName, Identity, ProgramId, Verifier,
3};
4use alloc::{format, string::String, vec::Vec};
5
6pub const RETH: &str = "reth";
7pub const CAIRO_M: &str = "cairo-m";
8pub const RISC0_3: &str = "risc0-3";
9pub const NOIR: &str = "noir";
10pub const SP1_4: &str = "sp1-4";
11
12#[derive(Debug, Copy, Clone)]
13pub enum NativeVerifiers {
14 Blst,
15 Sha3_256,
16 Secp256k1,
17}
18
19pub const NATIVE_VERIFIERS_CONTRACT_LIST: &[&str] = &["blst", "sha3_256", "secp256k1"];
20
21impl From<NativeVerifiers> for ProgramId {
22 fn from(value: NativeVerifiers) -> Self {
23 match value {
24 NativeVerifiers::Blst => ProgramId("blst".as_bytes().to_vec()),
25 NativeVerifiers::Sha3_256 => ProgramId("sha3_256".as_bytes().to_vec()),
26 NativeVerifiers::Secp256k1 => ProgramId("secp256k1".as_bytes().to_vec()),
27 }
28 }
29}
30
31impl TryFrom<&Verifier> for NativeVerifiers {
32 type Error = String;
33 fn try_from(value: &Verifier) -> Result<Self, Self::Error> {
34 match value.0.as_str() {
35 "blst" => Ok(Self::Blst),
36 "sha3_256" => Ok(Self::Sha3_256),
37 "secp256k1" => Ok(Self::Secp256k1),
38 _ => Err(format!("Unknown native verifier: {value}")),
39 }
40 }
41}
42
43impl From<NativeVerifiers> for Verifier {
44 fn from(value: NativeVerifiers) -> Self {
45 match value {
46 NativeVerifiers::Blst => Self("blst".into()),
47 NativeVerifiers::Sha3_256 => Self("sha3_256".into()),
48 NativeVerifiers::Secp256k1 => Self("secp256k1".into()),
49 }
50 }
51}
52
53#[cfg(feature = "full")]
54mod program_id {
55 use super::*;
56 #[derive(serde::Deserialize)]
58 #[allow(unused)]
59 struct SP1VK {
60 vk: SP1StarkVK,
61 }
62 #[derive(serde::Deserialize)]
63 #[allow(unused)]
64 struct SP1StarkVK {
65 commit: serde_json::Value,
66 pc_start: serde_json::Value,
67 initial_global_cumulative_sum: serde_json::Value,
68 chip_information: serde_json::Value,
69 chip_ordering: serde_json::Value,
70 }
71
72 pub fn validate_program_id(
73 verifier: &Verifier,
74 program_id: &ProgramId,
75 ) -> Result<(), anyhow::Error> {
76 match verifier.0.as_str() {
77 RISC0_3 => (!program_id.0.is_empty() && program_id.0.len() <= 32)
78 .then_some(())
79 .ok_or_else(|| {
80 anyhow::anyhow!("Invalid Risc0 image ID: length must be between 1 and 32 bytes")
81 }),
82 SP1_4 => {
83 serde_json::from_slice::<SP1VK>(program_id.0.as_slice())
84 .map_err(|e| anyhow::anyhow!("Invalid SP1 image ID: {}", e))?;
85 Ok(())
86 }
87 _ => Ok(()),
88 }
89 }
90}
91
92#[cfg(feature = "full")]
93pub use program_id::validate_program_id;
94
95#[derive(Debug, borsh::BorshSerialize, borsh::BorshDeserialize)]
97pub struct BlstSignatureBlob {
98 pub identity: Identity,
99 pub data: Vec<u8>,
100 pub signature: Vec<u8>,
102 pub public_key: Vec<u8>,
103}
104
105impl BlstSignatureBlob {
106 pub fn as_blob(&self) -> Blob {
107 <Self as ContractAction>::as_blob(self, "blst".into(), None, None)
108 }
109}
110
111impl ContractAction for BlstSignatureBlob {
112 fn as_blob(
113 &self,
114 contract_name: ContractName,
115 _caller: Option<BlobIndex>,
116 _callees: Option<Vec<BlobIndex>>,
117 ) -> Blob {
118 #[allow(clippy::expect_used)]
119 Blob {
120 contract_name,
121 data: BlobData(borsh::to_vec(self).expect("failed to encode BlstSignatureBlob")),
122 }
123 }
124}
125
126#[derive(Debug, borsh::BorshSerialize, borsh::BorshDeserialize)]
128pub struct ShaBlob {
129 pub identity: Identity,
130 pub data: Vec<u8>,
131 pub sha: Vec<u8>,
132}
133
134impl ShaBlob {
135 pub fn as_blob(&self, contract_name: ContractName) -> Blob {
136 <Self as ContractAction>::as_blob(self, contract_name, None, None)
137 }
138}
139
140impl ContractAction for ShaBlob {
141 fn as_blob(
142 &self,
143 contract_name: ContractName,
144 _caller: Option<BlobIndex>,
145 _callees: Option<Vec<BlobIndex>>,
146 ) -> Blob {
147 #[allow(clippy::expect_used)]
148 Blob {
149 contract_name,
150 data: BlobData(borsh::to_vec(self).expect("failed to encode ShaBlob")),
151 }
152 }
153}
154
155#[derive(Debug, borsh::BorshSerialize, borsh::BorshDeserialize)]
157pub struct Secp256k1Blob {
158 pub identity: Identity,
159 pub data: [u8; 32],
160 pub public_key: [u8; 33],
161 pub signature: [u8; 64],
162}
163
164impl Secp256k1Blob {
165 #[cfg(all(feature = "full", not(target_arch = "wasm32")))]
166 pub fn new(
168 identity: Identity,
169 data: &[u8],
170 public_key: &str,
171 signature: &str,
172 ) -> anyhow::Result<Self> {
173 use anyhow::Context;
174 use sha2::Digest;
175
176 let public_key = secp256k1::PublicKey::from_slice(
177 &hex::decode(public_key).context("invalid public_key format")?,
178 )
179 .context("cannot parse public_key")?
180 .serialize();
181
182 let signature = secp256k1::ecdsa::Signature::from_der(
183 &hex::decode(signature).context("invalid signature format")?,
184 )
185 .context("cannot parse signature")?
186 .serialize_compact();
187
188 let mut hasher = sha2::Sha256::new();
189 hasher.update(data);
190 let data: [u8; 32] = hasher.finalize().into();
191
192 Ok(Self {
193 identity,
194 data,
195 public_key,
196 signature,
197 })
198 }
199
200 pub fn as_blob(&self) -> Blob {
201 <Self as ContractAction>::as_blob(self, "secp256k1".into(), None, None)
202 }
203}
204
205impl ContractAction for Secp256k1Blob {
206 fn as_blob(
207 &self,
208 contract_name: ContractName,
209 _caller: Option<BlobIndex>,
210 _callees: Option<Vec<BlobIndex>>,
211 ) -> Blob {
212 #[allow(clippy::expect_used)]
213 Blob {
214 contract_name,
215 data: BlobData(borsh::to_vec(self).expect("failed to encode Secp256k1Blob")),
216 }
217 }
218}