use crate::{
Blob, BlobData, BlobIndex, ContractAction, ContractName, Identity, ProgramId, Verifier,
};
pub const RETH: &str = "reth";
pub const CAIRO_M: &str = "cairo-m";
pub const RISC0_3: &str = "risc0-3";
pub const NOIR: &str = "noir";
pub const SP1_4: &str = "sp1-4";
#[derive(Debug, Copy, Clone)]
pub enum NativeVerifiers {
Blst,
Sha3_256,
Secp256k1,
}
pub const NATIVE_VERIFIERS_CONTRACT_LIST: &[&str] = &["blst", "sha3_256", "secp256k1"];
impl From<NativeVerifiers> for ProgramId {
fn from(value: NativeVerifiers) -> Self {
match value {
NativeVerifiers::Blst => ProgramId("blst".as_bytes().to_vec()),
NativeVerifiers::Sha3_256 => ProgramId("sha3_256".as_bytes().to_vec()),
NativeVerifiers::Secp256k1 => ProgramId("secp256k1".as_bytes().to_vec()),
}
}
}
impl TryFrom<&Verifier> for NativeVerifiers {
type Error = String;
fn try_from(value: &Verifier) -> Result<Self, Self::Error> {
match value.0.as_str() {
"blst" => Ok(Self::Blst),
"sha3_256" => Ok(Self::Sha3_256),
"secp256k1" => Ok(Self::Secp256k1),
_ => Err(format!("Unknown native verifier: {value}")),
}
}
}
impl From<NativeVerifiers> for Verifier {
fn from(value: NativeVerifiers) -> Self {
match value {
NativeVerifiers::Blst => Self("blst".into()),
NativeVerifiers::Sha3_256 => Self("sha3_256".into()),
NativeVerifiers::Secp256k1 => Self("secp256k1".into()),
}
}
}
#[cfg(feature = "full")]
mod program_id {
use super::*;
#[derive(serde::Deserialize)]
#[allow(unused)]
struct SP1VK {
vk: SP1StarkVK,
}
#[derive(serde::Deserialize)]
#[allow(unused)]
struct SP1StarkVK {
commit: serde_json::Value,
pc_start: serde_json::Value,
initial_global_cumulative_sum: serde_json::Value,
chip_information: serde_json::Value,
chip_ordering: serde_json::Value,
}
pub fn validate_program_id(
verifier: &Verifier,
program_id: &ProgramId,
) -> Result<(), anyhow::Error> {
match verifier.0.as_str() {
RISC0_3 => (!program_id.0.is_empty() && program_id.0.len() <= 32)
.then_some(())
.ok_or_else(|| {
anyhow::anyhow!("Invalid Risc0 image ID: length must be between 1 and 32 bytes")
}),
SP1_4 => {
serde_json::from_slice::<SP1VK>(program_id.0.as_slice())
.map_err(|e| anyhow::anyhow!("Invalid SP1 image ID: {}", e))?;
Ok(())
}
_ => Ok(()),
}
}
}
#[cfg(feature = "full")]
pub use program_id::validate_program_id;
#[derive(Debug, borsh::BorshSerialize, borsh::BorshDeserialize)]
pub struct BlstSignatureBlob {
pub identity: Identity,
pub data: Vec<u8>,
pub signature: Vec<u8>,
pub public_key: Vec<u8>,
}
impl BlstSignatureBlob {
pub fn as_blob(&self) -> Blob {
<Self as ContractAction>::as_blob(self, "blst".into(), None, None)
}
}
impl ContractAction for BlstSignatureBlob {
fn as_blob(
&self,
contract_name: ContractName,
_caller: Option<BlobIndex>,
_callees: Option<Vec<BlobIndex>>,
) -> Blob {
#[allow(clippy::expect_used)]
Blob {
contract_name,
data: BlobData(borsh::to_vec(self).expect("failed to encode BlstSignatureBlob")),
}
}
}
#[derive(Debug, borsh::BorshSerialize, borsh::BorshDeserialize)]
pub struct ShaBlob {
pub identity: Identity,
pub data: Vec<u8>,
pub sha: Vec<u8>,
}
impl ShaBlob {
pub fn as_blob(&self, contract_name: ContractName) -> Blob {
<Self as ContractAction>::as_blob(self, contract_name, None, None)
}
}
impl ContractAction for ShaBlob {
fn as_blob(
&self,
contract_name: ContractName,
_caller: Option<BlobIndex>,
_callees: Option<Vec<BlobIndex>>,
) -> Blob {
#[allow(clippy::expect_used)]
Blob {
contract_name,
data: BlobData(borsh::to_vec(self).expect("failed to encode ShaBlob")),
}
}
}
#[derive(Debug, borsh::BorshSerialize, borsh::BorshDeserialize)]
pub struct Secp256k1Blob {
pub identity: Identity,
pub data: [u8; 32],
pub public_key: [u8; 33],
pub signature: [u8; 64],
}
impl Secp256k1Blob {
#[cfg(all(feature = "full", not(target_arch = "wasm32")))]
pub fn new(
identity: Identity,
data: &[u8],
public_key: &str,
signature: &str,
) -> anyhow::Result<Self> {
use anyhow::Context;
use sha2::Digest;
let public_key = secp256k1::PublicKey::from_slice(
&hex::decode(public_key).context("invalid public_key format")?,
)
.context("cannot parse public_key")?
.serialize();
let signature = secp256k1::ecdsa::Signature::from_der(
&hex::decode(signature).context("invalid signature format")?,
)
.context("cannot parse signature")?
.serialize_compact();
let mut hasher = sha2::Sha256::new();
hasher.update(data);
let data: [u8; 32] = hasher.finalize().into();
Ok(Self {
identity,
data,
public_key,
signature,
})
}
pub fn as_blob(&self) -> Blob {
<Self as ContractAction>::as_blob(self, "secp256k1".into(), None, None)
}
}
impl ContractAction for Secp256k1Blob {
fn as_blob(
&self,
contract_name: ContractName,
_caller: Option<BlobIndex>,
_callees: Option<Vec<BlobIndex>>,
) -> Blob {
#[allow(clippy::expect_used)]
Blob {
contract_name,
data: BlobData(borsh::to_vec(self).expect("failed to encode Secp256k1Blob")),
}
}
}