use std::marker::PhantomData;
use std::time::{Duration, Instant};
use amari_core::Multivector;
#[derive(Debug, Clone, Copy)]
pub struct WasmConstraints {
pub js_interop: bool,
pub max_linear_memory: u64,
pub max_overhead_us: u64,
pub browser_env: BrowserEnvironment,
pub cross_origin_restricted: bool,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BrowserEnvironment {
Modern,
Limited,
NodeJs,
WebWorker,
ServiceWorker,
}
#[derive(Debug, Clone)]
pub enum WasmVerificationError {
TypeErasure { operation: String, expected_type: String },
JsInteropFailure { function: String, details: String },
LinearMemoryExhaustion { requested: u64, available: u64 },
SecurityRestriction { policy: String, operation: String },
AsyncConsistencyFailure { operation: String, state: String },
CrossOriginViolation { origin: String, operation: String },
BoundaryInvariantViolation { operation: String, details: String },
CompatibilityError { browser: String, feature: String },
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum WasmVerificationLevel {
None,
Interface,
Statistical { sample_rate: f32 },
Development,
Progressive,
}
pub struct WasmVerificationContext<const P: usize, const Q: usize, const R: usize> {
constraints: WasmConstraints,
level: WasmVerificationLevel,
js_verification_enabled: bool,
wasm_verification_enabled: bool,
operation_count: u64,
total_overhead: Duration,
browser_features: BrowserFeatures,
_phantom: PhantomData<(P, Q, R)>,
}
#[derive(Debug, Clone)]
pub struct BrowserFeatures {
pub webassembly_support: bool,
pub shared_array_buffer: bool,
pub atomic_operations: bool,
pub wasm_threads: bool,
pub simd: bool,
pub bulk_memory: bool,
}
impl<const P: usize, const Q: usize, const R: usize> WasmVerificationContext<P, Q, R> {
pub fn new(constraints: WasmConstraints) -> Self {
let browser_features = Self::detect_browser_features();
let level = Self::determine_optimal_wasm_level(constraints, &browser_features);
Self {
constraints,
level,
js_verification_enabled: constraints.js_interop,
wasm_verification_enabled: true,
operation_count: 0,
total_overhead: Duration::ZERO,
browser_features,
_phantom: PhantomData,
}
}
fn detect_browser_features() -> BrowserFeatures {
BrowserFeatures {
webassembly_support: true,
shared_array_buffer: false, atomic_operations: false,
wasm_threads: false,
simd: true,
bulk_memory: true,
}
}
fn determine_optimal_wasm_level(
constraints: WasmConstraints,
features: &BrowserFeatures,
) -> WasmVerificationLevel {
match constraints.browser_env {
BrowserEnvironment::Modern if features.webassembly_support => {
if constraints.js_interop {
WasmVerificationLevel::Statistical { sample_rate: 0.1 }
} else {
WasmVerificationLevel::Interface
}
}
BrowserEnvironment::Limited => WasmVerificationLevel::Interface,
BrowserEnvironment::NodeJs => WasmVerificationLevel::Development,
BrowserEnvironment::WebWorker | BrowserEnvironment::ServiceWorker => {
if constraints.max_overhead_us < 100 {
WasmVerificationLevel::None
} else {
WasmVerificationLevel::Interface
}
}
}
}
pub async fn verify_js_to_wasm_transfer(
&mut self,
js_data: &[f64],
expected_multivectors: usize,
) -> Result<Vec<Multivector<P, Q, R>>, WasmVerificationError> {
let start = Instant::now();
self.operation_count += 1;
if js_data.len() != expected_multivectors * 8 {
return Err(WasmVerificationError::TypeErasure {
operation: "js_to_wasm_transfer".to_string(),
expected_type: format!("{}x8 coefficients", expected_multivectors),
});
}
let required_memory = js_data.len() * std::mem::size_of::<f64>();
if required_memory as u64 > self.constraints.max_linear_memory {
return Err(WasmVerificationError::LinearMemoryExhaustion {
requested: required_memory as u64,
available: self.constraints.max_linear_memory,
});
}
let mut multivectors = Vec::with_capacity(expected_multivectors);
for i in 0..expected_multivectors {
let start_idx = i * 8;
let coeffs = &js_data[start_idx..start_idx + 8];
for (j, &coeff) in coeffs.iter().enumerate() {
if !coeff.is_finite() {
return Err(WasmVerificationError::JsInteropFailure {
function: "coefficient_transfer".to_string(),
details: format!("Non-finite coefficient at multivector {} basis {}: {}", i, j, coeff),
});
}
}
let mv = Multivector::<P, Q, R>::from_coefficients(coeffs.to_vec());
if let Err(e) = self.verify_multivector_properties(&mv).await {
return Err(e);
}
multivectors.push(mv);
}
self.total_overhead += start.elapsed();
Ok(multivectors)
}
pub async fn verify_wasm_to_js_transfer(
&mut self,
multivectors: &[Multivector<P, Q, R>],
) -> Result<Vec<f64>, WasmVerificationError> {
let start = Instant::now();
let mut js_data = Vec::with_capacity(multivectors.len() * 8);
for (i, mv) in multivectors.iter().enumerate() {
if let Err(e) = self.verify_multivector_properties(mv).await {
return Err(e);
}
for j in 0..8 {
let coeff = mv.get(j);
if !coeff.is_finite() {
return Err(WasmVerificationError::BoundaryInvariantViolation {
operation: "wasm_to_js_transfer".to_string(),
details: format!("Non-finite coefficient at multivector {} basis {}: {}", i, j, coeff),
});
}
if coeff.abs() > f64::MAX / 2.0 {
return Err(WasmVerificationError::JsInteropFailure {
function: "coefficient_transfer".to_string(),
details: format!("Coefficient too large for JavaScript: {}", coeff),
});
}
js_data.push(coeff);
}
}
self.total_overhead += start.elapsed();
Ok(js_data)
}
pub async fn verify_async_operation<F, T>(
&mut self,
operation_name: &str,
operation: F,
) -> Result<T, WasmVerificationError>
where
F: std::future::Future<Output = T>,
{
let start = Instant::now();
match self.level {
WasmVerificationLevel::None => {
let result = operation.await;
Ok(result)
}
WasmVerificationLevel::Interface => {
self.verify_async_preconditions(operation_name).await?;
let result = operation.await;
self.verify_async_postconditions(operation_name).await?;
Ok(result)
}
WasmVerificationLevel::Statistical { sample_rate } => {
if self.should_sample_operation(sample_rate) {
self.verify_async_preconditions(operation_name).await?;
}
let result = operation.await;
if self.should_sample_operation(sample_rate) {
self.verify_async_postconditions(operation_name).await?;
}
Ok(result)
}
WasmVerificationLevel::Development => {
self.verify_async_preconditions(operation_name).await?;
let result = operation.await;
self.verify_async_postconditions(operation_name).await?;
self.log_operation_performance(operation_name, start.elapsed());
Ok(result)
}
WasmVerificationLevel::Progressive => {
if self.browser_features.webassembly_support {
self.verify_async_preconditions(operation_name).await?;
}
let result = operation.await;
if self.browser_features.webassembly_support {
self.verify_async_postconditions(operation_name).await?;
}
Ok(result)
}
}
}
pub async fn verify_batch_operation(
&mut self,
operation_name: &str,
input_batch: &[Multivector<P, Q, R>],
output_batch: &[Multivector<P, Q, R>],
) -> Result<(), WasmVerificationError> {
if input_batch.len() != output_batch.len() {
return Err(WasmVerificationError::BoundaryInvariantViolation {
operation: operation_name.to_string(),
details: "Batch size mismatch".to_string(),
});
}
let batch_memory = input_batch.len() * std::mem::size_of::<Multivector<P, Q, R>>() * 2;
if batch_memory as u64 > self.constraints.max_linear_memory / 2 {
return Err(WasmVerificationError::LinearMemoryExhaustion {
requested: batch_memory as u64,
available: self.constraints.max_linear_memory / 2,
});
}
match self.level {
WasmVerificationLevel::None => Ok(()),
WasmVerificationLevel::Interface => {
if !input_batch.is_empty() {
self.verify_multivector_properties(&input_batch[0]).await?;
self.verify_multivector_properties(&output_batch[0]).await?;
if input_batch.len() > 1 {
let last = input_batch.len() - 1;
self.verify_multivector_properties(&input_batch[last]).await?;
self.verify_multivector_properties(&output_batch[last]).await?;
}
}
Ok(())
}
WasmVerificationLevel::Statistical { sample_rate } => {
let sample_count = ((input_batch.len() as f32) * sample_rate).ceil() as usize;
let step = input_batch.len() / sample_count.max(1);
for i in (0..input_batch.len()).step_by(step) {
self.verify_multivector_properties(&input_batch[i]).await?;
self.verify_multivector_properties(&output_batch[i]).await?;
}
Ok(())
}
WasmVerificationLevel::Development => {
for (input, output) in input_batch.iter().zip(output_batch.iter()) {
self.verify_multivector_properties(input).await?;
self.verify_multivector_properties(output).await?;
}
Ok(())
}
WasmVerificationLevel::Progressive => {
if self.browser_features.bulk_memory {
self.verify_bulk_properties(input_batch).await?;
self.verify_bulk_properties(output_batch).await?;
} else {
if !input_batch.is_empty() {
self.verify_multivector_properties(&input_batch[0]).await?;
self.verify_multivector_properties(&output_batch[0]).await?;
}
}
Ok(())
}
}
}
pub async fn verify_cross_origin_operation(
&self,
operation_name: &str,
origin: &str,
) -> Result<(), WasmVerificationError> {
if self.constraints.cross_origin_restricted {
if !self.is_origin_allowed(origin) {
return Err(WasmVerificationError::CrossOriginViolation {
origin: origin.to_string(),
operation: operation_name.to_string(),
});
}
}
Ok(())
}
fn is_origin_allowed(&self, origin: &str) -> bool {
origin.starts_with("https://") || origin == "null" }
async fn verify_multivector_properties(
&self,
mv: &Multivector<P, Q, R>,
) -> Result<(), WasmVerificationError> {
let magnitude = mv.magnitude();
if !magnitude.is_finite() || magnitude < 0.0 {
return Err(WasmVerificationError::BoundaryInvariantViolation {
operation: "multivector_property_check".to_string(),
details: format!("Invalid magnitude: {}", magnitude),
});
}
for i in 0..8 {
let coeff = mv.get(i);
if !coeff.is_finite() {
return Err(WasmVerificationError::BoundaryInvariantViolation {
operation: "coefficient_validation".to_string(),
details: format!("Non-finite coefficient at basis {}: {}", i, coeff),
});
}
}
for i in 0..8 {
let coeff = mv.get(i);
if coeff.abs() > 1.7976931348623157e+308 { return Err(WasmVerificationError::JsInteropFailure {
function: "number_range_check".to_string(),
details: format!("Coefficient exceeds JavaScript number range: {}", coeff),
});
}
}
Ok(())
}
async fn verify_bulk_properties(
&self,
multivectors: &[Multivector<P, Q, R>],
) -> Result<(), WasmVerificationError> {
if self.browser_features.simd {
for mv in multivectors {
self.verify_multivector_properties(mv).await?;
}
} else {
for mv in multivectors {
self.verify_multivector_properties(mv).await?;
}
}
Ok(())
}
fn should_sample_operation(&self, sample_rate: f32) -> bool {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
self.operation_count.hash(&mut hasher);
let hash = hasher.finish();
(hash as f64 / u64::MAX as f64) < sample_rate as f64
}
async fn verify_async_preconditions(&self, operation_name: &str) -> Result<(), WasmVerificationError> {
match self.constraints.browser_env {
BrowserEnvironment::Limited => {
if operation_name.contains("simd") && !self.browser_features.simd {
return Err(WasmVerificationError::CompatibilityError {
browser: "limited".to_string(),
feature: "SIMD".to_string(),
});
}
}
BrowserEnvironment::WebWorker => {
if operation_name.contains("shared") && !self.browser_features.shared_array_buffer {
return Err(WasmVerificationError::SecurityRestriction {
policy: "SharedArrayBuffer disabled".to_string(),
operation: operation_name.to_string(),
});
}
}
_ => {}
}
Ok(())
}
async fn verify_async_postconditions(&self, _operation_name: &str) -> Result<(), WasmVerificationError> {
Ok(())
}
fn log_operation_performance(&self, operation_name: &str, duration: Duration) {
if matches!(self.level, WasmVerificationLevel::Development) {
eprintln!("WASM Operation '{}' took {:?}", operation_name, duration);
}
}
pub fn get_wasm_stats(&self) -> WasmVerificationStats {
WasmVerificationStats {
level: self.level,
operation_count: self.operation_count,
total_overhead: self.total_overhead,
js_verification_enabled: self.js_verification_enabled,
wasm_verification_enabled: self.wasm_verification_enabled,
browser_features: self.browser_features.clone(),
constraints: self.constraints,
}
}
pub fn enable_progressive_enhancement(&mut self, detected_features: BrowserFeatures) {
self.browser_features = detected_features;
if self.browser_features.webassembly_support && self.browser_features.simd {
self.level = WasmVerificationLevel::Statistical { sample_rate: 0.1 };
} else if self.browser_features.webassembly_support {
self.level = WasmVerificationLevel::Interface;
} else {
self.level = WasmVerificationLevel::None;
}
}
}
#[derive(Debug)]
pub struct WasmVerificationStats {
pub level: WasmVerificationLevel,
pub operation_count: u64,
pub total_overhead: Duration,
pub js_verification_enabled: bool,
pub wasm_verification_enabled: bool,
pub browser_features: BrowserFeatures,
pub constraints: WasmConstraints,
}
pub struct WasmVerifiedMultivector<const P: usize, const Q: usize, const R: usize> {
inner: Multivector<P, Q, R>,
js_safe: bool,
wasm_verified: bool,
verification_level: WasmVerificationLevel,
_phantom: PhantomData<(P, Q, R)>,
}
impl<const P: usize, const Q: usize, const R: usize> WasmVerifiedMultivector<P, Q, R> {
pub fn new_wasm_verified(
multivector: Multivector<P, Q, R>,
context: &WasmVerificationContext<P, Q, R>,
) -> Result<Self, WasmVerificationError> {
if let Err(e) = futures::executor::block_on(context.verify_multivector_properties(&multivector)) {
return Err(e);
}
Ok(Self {
inner: multivector,
js_safe: context.js_verification_enabled,
wasm_verified: true,
verification_level: context.level,
_phantom: PhantomData,
})
}
pub async fn to_js_coefficients(
&self,
context: &mut WasmVerificationContext<P, Q, R>,
) -> Result<Vec<f64>, WasmVerificationError> {
context.verify_wasm_to_js_transfer(&[self.inner]).await.map(|coeffs| coeffs)
}
pub async fn from_js_coefficients(
coefficients: &[f64],
context: &mut WasmVerificationContext<P, Q, R>,
) -> Result<Self, WasmVerificationError> {
let multivectors = context.verify_js_to_wasm_transfer(coefficients, 1).await?;
Self::new_wasm_verified(multivectors[0], context)
}
pub async fn batch_geometric_product_wasm_verified(
a_batch: &[Self],
b_batch: &[Self],
context: &mut WasmVerificationContext<P, Q, R>,
) -> Result<Vec<Self>, WasmVerificationError> {
let a_mvs: Vec<Multivector<P, Q, R>> = a_batch.iter().map(|v| v.inner).collect();
let b_mvs: Vec<Multivector<P, Q, R>> = b_batch.iter().map(|v| v.inner).collect();
let mut result_mvs = Vec::with_capacity(a_mvs.len());
for (a, b) in a_mvs.iter().zip(b_mvs.iter()) {
result_mvs.push(a.geometric_product(b));
}
context.verify_batch_operation("batch_geometric_product", &a_mvs, &result_mvs).await?;
let verified_results: Result<Vec<Self>, _> = result_mvs
.into_iter()
.map(|mv| Self::new_wasm_verified(mv, context))
.collect();
verified_results
}
pub fn inner(&self) -> &Multivector<P, Q, R> {
&self.inner
}
pub fn is_js_safe(&self) -> bool {
self.js_safe
}
pub fn is_wasm_verified(&self) -> bool {
self.wasm_verified
}
pub fn verification_level(&self) -> WasmVerificationLevel {
self.verification_level
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_wasm_verification_context_creation() {
let constraints = WasmConstraints {
js_interop: true,
max_linear_memory: 1024 * 1024, max_overhead_us: 1000, browser_env: BrowserEnvironment::Modern,
cross_origin_restricted: true,
};
let context = WasmVerificationContext::<3, 0, 0>::new(constraints);
assert!(context.js_verification_enabled);
assert_eq!(context.level, WasmVerificationLevel::Statistical { sample_rate: 0.1 });
}
#[tokio::test]
async fn test_js_to_wasm_transfer_validation() {
let constraints = WasmConstraints {
js_interop: true,
max_linear_memory: 1024 * 1024,
max_overhead_us: 1000,
browser_env: BrowserEnvironment::Modern,
cross_origin_restricted: false,
};
let mut context = WasmVerificationContext::<3, 0, 0>::new(constraints);
let js_data = vec![1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let result = context.verify_js_to_wasm_transfer(&js_data, 1).await;
assert!(result.is_ok());
let invalid_data = vec![1.0, 2.0, 3.0];
let result = context.verify_js_to_wasm_transfer(&invalid_data, 1).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_wasm_verified_multivector() {
let constraints = WasmConstraints {
js_interop: true,
max_linear_memory: 1024 * 1024,
max_overhead_us: 1000,
browser_env: BrowserEnvironment::Modern,
cross_origin_restricted: false,
};
let context = WasmVerificationContext::<3, 0, 0>::new(constraints);
let mv = Multivector::<3, 0, 0>::basis_vector(0);
let verified_mv = WasmVerifiedMultivector::new_wasm_verified(mv, &context);
assert!(verified_mv.is_ok());
let verified = verified_mv.unwrap();
assert!(verified.is_js_safe());
assert!(verified.is_wasm_verified());
}
#[tokio::test]
async fn test_nan_infinity_rejection() {
let constraints = WasmConstraints {
js_interop: true,
max_linear_memory: 1024 * 1024,
max_overhead_us: 1000,
browser_env: BrowserEnvironment::Modern,
cross_origin_restricted: false,
};
let mut context = WasmVerificationContext::<3, 0, 0>::new(constraints);
let nan_data = vec![f64::NAN, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let result = context.verify_js_to_wasm_transfer(&nan_data, 1).await;
assert!(result.is_err());
let inf_data = vec![f64::INFINITY, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let result = context.verify_js_to_wasm_transfer(&inf_data, 1).await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_cross_origin_verification() {
let constraints = WasmConstraints {
js_interop: true,
max_linear_memory: 1024 * 1024,
max_overhead_us: 1000,
browser_env: BrowserEnvironment::Modern,
cross_origin_restricted: true,
};
let context = WasmVerificationContext::<3, 0, 0>::new(constraints);
let result = context.verify_cross_origin_operation("test_op", "https://example.com").await;
assert!(result.is_ok());
let result = context.verify_cross_origin_operation("test_op", "http://malicious.com").await;
assert!(result.is_err());
}
#[test]
fn test_browser_feature_detection() {
let features = WasmVerificationContext::<3, 0, 0>::detect_browser_features();
assert!(features.webassembly_support);
}
}