use crate::{
sign::secp256k1,
types::{
predicate::{Directive, Predicate},
ConstraintBytecode, StateReadBytecode,
},
};
#[cfg(feature = "tracing")]
use essential_hash::contract_addr;
use essential_types::contract;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum InvalidSignedContract {
#[error("invalid signature: {0}")]
Signature(#[from] secp256k1::Error),
#[error("invalid contract: {0}")]
Set(#[from] InvalidContract),
}
#[derive(Debug, Error)]
pub enum InvalidContract {
#[error("the number of predicates ({0}) exceeds the limit ({MAX_PREDICATES})")]
TooManyPredicates(usize),
#[error("predicate at index {0} is invalid: {1}")]
Predicate(usize, InvalidPredicate),
}
#[derive(Debug, Error)]
pub enum InvalidPredicate {
#[error("invalid slots: {0}")]
Slots(#[from] InvalidSlots),
#[error("invalid directive: {0}")]
Directive(#[from] InvalidDirective),
#[error("invalid state reads: {0}")]
StateReads(#[from] InvalidStateReads),
#[error("invalid constraints: {0}")]
Constraints(#[from] InvalidConstraints),
}
#[derive(Debug, Error)]
pub enum InvalidSlots {
#[error("the number of decision vars ({0}) exceeds the limit ({MAX_DECISION_VARIABLES})")]
TooManyDecisionVariables(u32),
#[error("the number of state slots ({0}) exceeds the limit ({MAX_NUM_STATE_SLOTS})")]
TooManyStateSlots(usize),
#[error("the total length of all state slots ({0:?}) exceeds the limit ({MAX_STATE_LEN})")]
StateSlotLengthExceedsLimit(Option<u32>),
}
#[derive(Debug, Error)]
pub enum InvalidDirective {
#[error("the length of the bytecode ({0}) exceeds the limit ({MAX_DIRECTIVE_SIZE})")]
TooManyBytes(usize),
}
#[derive(Debug, Error)]
pub enum InvalidStateReads {
#[error("the number of state reads ({0}) exceeds the limit ({MAX_STATE_READS})")]
TooMany(usize),
#[error("state read at index {0} failed to validate: {1}")]
StateRead(usize, InvalidStateRead),
}
#[derive(Debug, Error)]
pub enum InvalidStateRead {
#[error("the length of the bytecode ({0}) exceeds the limit ({MAX_STATE_READ_SIZE_IN_BYTES}")]
TooManyBytes(usize),
}
#[derive(Debug, Error)]
pub enum InvalidConstraints {
#[error("the number of constraints ({0}) exceeds the limit ({MAX_CONSTRAINTS})")]
TooManyConstraints(usize),
#[error("constraint at index {0} failed to validate: {1}")]
Constraint(usize, InvalidConstraint),
}
#[derive(Debug, Error)]
pub enum InvalidConstraint {
#[error("the length of the bytecode ({0}) exceeds the limit ({MAX_CONSTRAINT_SIZE_IN_BYTES}")]
TooManyBytes(usize),
}
pub const MAX_PREDICATES: usize = 100;
pub const MAX_STATE_READS: usize = 100;
pub const MAX_STATE_READ_SIZE_IN_BYTES: usize = 10_000;
pub const MAX_CONSTRAINTS: usize = 100;
pub const MAX_CONSTRAINT_SIZE_IN_BYTES: usize = 10_000;
pub const MAX_DECISION_VARIABLES: u32 = 100;
pub const MAX_NUM_STATE_SLOTS: usize = 1000;
pub const MAX_STATE_LEN: u32 = 1000;
pub const MAX_DIRECTIVE_SIZE: usize = 1000;
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all, fields(addr = %contract_addr::from_contract(&signed_contract.contract)), err))]
pub fn check_signed_contract(
signed_contract: &contract::SignedContract,
) -> Result<(), InvalidSignedContract> {
essential_sign::contract::verify(signed_contract)?;
check_contract(signed_contract.contract.as_ref())?;
Ok(())
}
pub fn check_contract(predicates: &[Predicate]) -> Result<(), InvalidContract> {
if predicates.len() > MAX_PREDICATES {
return Err(InvalidContract::TooManyPredicates(predicates.len()));
}
for (ix, predicate) in predicates.iter().enumerate() {
check(predicate).map_err(|e| InvalidContract::Predicate(ix, e))?;
}
Ok(())
}
pub fn check(predicate: &Predicate) -> Result<(), InvalidPredicate> {
check_directive(&predicate.directive)?;
check_state_reads(&predicate.state_read)?;
check_constraints(&predicate.constraints)?;
Ok(())
}
pub fn check_directive(directive: &Directive) -> Result<(), InvalidDirective> {
if let Directive::Maximize(program) | Directive::Minimize(program) = directive {
if program.len() > MAX_DIRECTIVE_SIZE {
return Err(InvalidDirective::TooManyBytes(program.len()));
}
}
Ok(())
}
pub fn check_state_reads(state_reads: &[StateReadBytecode]) -> Result<(), InvalidStateReads> {
if state_reads.len() > MAX_STATE_READS {
return Err(InvalidStateReads::TooMany(state_reads.len()));
}
for (ix, state_read) in state_reads.iter().enumerate() {
check_state_read(state_read).map_err(|e| InvalidStateReads::StateRead(ix, e))?;
}
Ok(())
}
pub fn check_state_read(state_read: &[u8]) -> Result<(), InvalidStateRead> {
if state_read.len() > MAX_STATE_READ_SIZE_IN_BYTES {
return Err(InvalidStateRead::TooManyBytes(state_read.len()));
}
Ok(())
}
pub fn check_constraints(constraints: &[ConstraintBytecode]) -> Result<(), InvalidConstraints> {
if constraints.len() > MAX_CONSTRAINTS {
return Err(InvalidConstraints::TooManyConstraints(constraints.len()));
}
for (ix, constraint) in constraints.iter().enumerate() {
check_constraint(constraint).map_err(|e| InvalidConstraints::Constraint(ix, e))?;
}
Ok(())
}
pub fn check_constraint(constraint: &[u8]) -> Result<(), InvalidConstraint> {
if constraint.len() > MAX_CONSTRAINT_SIZE_IN_BYTES {
return Err(InvalidConstraint::TooManyBytes(constraint.len()));
}
Ok(())
}