Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bins/validator-node/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,9 @@ async fn main() -> Result<()> {
};
std::fs::create_dir_all(&wasm_module_dir)?;

let challenges_subdir = wasm_module_dir.join("challenges");
std::fs::create_dir_all(&challenges_subdir)?;

let wasm_executor = match WasmChallengeExecutor::new(WasmExecutorConfig {
module_dir: wasm_module_dir.clone(),
max_memory_bytes: args.wasm_max_memory,
Expand Down
227 changes: 197 additions & 30 deletions bins/validator-node/src/wasm_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ use std::sync::Arc;
use std::time::Instant;
use tracing::{debug, info};
use wasm_runtime_interface::{
ExecPolicy, InstanceConfig, NetworkHostFunctions, NetworkPolicy, RuntimeConfig, TimePolicy,
WasmModule, WasmRuntime, WasmRuntimeError,
ExecPolicy, InMemoryStorageBackend, InstanceConfig, NetworkHostFunctions, NetworkPolicy,
RuntimeConfig, SandboxHostFunctions, SandboxPolicy, StorageHostConfig, TimePolicy, WasmModule,
WasmRuntime, WasmRuntimeError,
};

pub struct WasmExecutorConfig {
Expand Down Expand Up @@ -72,6 +73,21 @@ impl WasmChallengeExecutor {
module_path: &str,
network_policy: &NetworkPolicy,
input_data: &[u8],
) -> Result<(i64, ExecutionMetrics)> {
self.execute_evaluation_with_sandbox(
module_path,
network_policy,
&SandboxPolicy::default(),
input_data,
)
}

pub fn execute_evaluation_with_sandbox(
&self,
module_path: &str,
network_policy: &NetworkPolicy,
sandbox_policy: &SandboxPolicy,
input_data: &[u8],
) -> Result<(i64, ExecutionMetrics)> {
let start = Instant::now();

Expand All @@ -80,9 +96,11 @@ impl WasmChallengeExecutor {
.context("Failed to load WASM module")?;

let network_host_fns = Arc::new(NetworkHostFunctions::all());
let _sandbox_host_fns = Arc::new(SandboxHostFunctions::all());

let instance_config = InstanceConfig {
network_policy: network_policy.clone(),
sandbox_policy: sandbox_policy.clone(),
exec_policy: ExecPolicy::default(),
time_policy: TimePolicy::default(),
audit_logger: None,
Expand All @@ -101,20 +119,7 @@ impl WasmChallengeExecutor {

let initial_fuel = instance.fuel_remaining();

let alloc_result = instance.call_i32_i32_return_i32("allocate", input_data.len() as i32, 0);
let ptr = match alloc_result {
Ok(p) => p,
Err(_) => {
let mem_size = instance.memory().data_size(instance.store());
let offset = mem_size.saturating_sub(input_data.len() + 1024);
if offset == 0 {
return Err(anyhow::anyhow!(
"WASM module has insufficient memory for input data"
));
}
offset as i32
}
};
let ptr = self.allocate_input(&mut instance, input_data)?;

instance
.write_memory(ptr as usize, input_data)
Expand Down Expand Up @@ -171,9 +176,11 @@ impl WasmChallengeExecutor {
.context("Failed to load WASM module")?;

let network_host_fns = Arc::new(NetworkHostFunctions::all());
let _sandbox_host_fns = Arc::new(SandboxHostFunctions::all());

let instance_config = InstanceConfig {
network_policy: network_policy.clone(),
sandbox_policy: SandboxPolicy::default(),
exec_policy: ExecPolicy::default(),
time_policy: TimePolicy::default(),
audit_logger: None,
Expand All @@ -192,20 +199,7 @@ impl WasmChallengeExecutor {

let initial_fuel = instance.fuel_remaining();

let alloc_result = instance.call_i32_i32_return_i32("allocate", input_data.len() as i32, 0);
let ptr = match alloc_result {
Ok(p) => p,
Err(_) => {
let mem_size = instance.memory().data_size(instance.store());
let offset = mem_size.saturating_sub(input_data.len() + 1024);
if offset == 0 {
return Err(anyhow::anyhow!(
"WASM module has insufficient memory for input data"
));
}
offset as i32
}
};
let ptr = self.allocate_input(&mut instance, input_data)?;

instance
.write_memory(ptr as usize, input_data)
Expand Down Expand Up @@ -250,6 +244,179 @@ impl WasmChallengeExecutor {
Ok((valid, metrics))
}

fn allocate_input(
&self,
instance: &mut wasm_runtime_interface::ChallengeInstance,
input_data: &[u8],
) -> Result<i32> {
if let Ok(p) = instance.call_i32_return_i32("alloc", input_data.len() as i32) {
return Ok(p);
}

if let Ok(p) = instance.call_i32_i32_return_i32("allocate", input_data.len() as i32, 0) {
return Ok(p);
}

let mem_size = instance.memory().data_size(instance.store());
let offset = mem_size.saturating_sub(input_data.len() + 1024);
if offset == 0 {
return Err(anyhow::anyhow!(
"WASM module has insufficient memory for input data"
));
}
Ok(offset as i32)
}

#[allow(dead_code)]
pub fn execute_get_tasks(
&self,
module_path: &str,
network_policy: &NetworkPolicy,
sandbox_policy: &SandboxPolicy,
) -> Result<(Vec<u8>, ExecutionMetrics)> {
let start = Instant::now();

let module = self
.load_module(module_path)
.context("Failed to load WASM module")?;

let network_host_fns = Arc::new(NetworkHostFunctions::all());

let instance_config = InstanceConfig {
network_policy: network_policy.clone(),
sandbox_policy: sandbox_policy.clone(),
exec_policy: ExecPolicy::default(),
time_policy: TimePolicy::default(),
audit_logger: None,
memory_export: "memory".to_string(),
challenge_id: module_path.to_string(),
validator_id: "validator".to_string(),
restart_id: String::new(),
config_version: 0,
storage_host_config: StorageHostConfig::default(),
storage_backend: Arc::new(InMemoryStorageBackend::new()),
fixed_timestamp_ms: None,
};

let mut instance = self
.runtime
.instantiate(&module, instance_config, Some(network_host_fns))
.map_err(|e| anyhow::anyhow!("WASM instantiation failed: {}", e))?;

let initial_fuel = instance.fuel_remaining();

let result_ptr = instance
.call_return_i32("get_tasks")
.map_err(|e| anyhow::anyhow!("WASM get_tasks call failed: {}", e))?;

let result_data = if result_ptr > 0 {
let len = instance
.call_return_i32("get_tasks_result_len")
.unwrap_or(0);
if len > 0 {
instance
.read_memory(result_ptr as usize, len as usize)
.unwrap_or_default()
} else {
Vec::new()
}
} else {
Vec::new()
};

let fuel_consumed = match (initial_fuel, instance.fuel_remaining()) {
(Some(initial), Some(remaining)) => Some(initial.saturating_sub(remaining)),
_ => None,
};

let metrics = ExecutionMetrics {
execution_time_ms: start.elapsed().as_millis(),
memory_used_bytes: instance.memory().data_size(instance.store()) as u64,
network_requests_made: instance.network_requests_made(),
fuel_consumed,
};

info!(
module = module_path,
result_bytes = result_data.len(),
execution_time_ms = metrics.execution_time_ms,
"WASM get_tasks completed"
);

Ok((result_data, metrics))
}

#[allow(dead_code)]
pub fn execute_configure(
&self,
module_path: &str,
network_policy: &NetworkPolicy,
sandbox_policy: &SandboxPolicy,
config_data: &[u8],
) -> Result<(i32, ExecutionMetrics)> {
let start = Instant::now();

let module = self
.load_module(module_path)
.context("Failed to load WASM module")?;

let network_host_fns = Arc::new(NetworkHostFunctions::all());

let instance_config = InstanceConfig {
network_policy: network_policy.clone(),
sandbox_policy: sandbox_policy.clone(),
exec_policy: ExecPolicy::default(),
time_policy: TimePolicy::default(),
audit_logger: None,
memory_export: "memory".to_string(),
challenge_id: module_path.to_string(),
validator_id: "validator".to_string(),
restart_id: String::new(),
config_version: 0,
storage_host_config: StorageHostConfig::default(),
storage_backend: Arc::new(InMemoryStorageBackend::new()),
fixed_timestamp_ms: None,
};

let mut instance = self
.runtime
.instantiate(&module, instance_config, Some(network_host_fns))
.map_err(|e| anyhow::anyhow!("WASM instantiation failed: {}", e))?;

let initial_fuel = instance.fuel_remaining();

let ptr = self.allocate_input(&mut instance, config_data)?;

instance
.write_memory(ptr as usize, config_data)
.map_err(|e| anyhow::anyhow!("Failed to write config data to WASM memory: {}", e))?;

let result = instance
.call_i32_i32_return_i32("configure", ptr, config_data.len() as i32)
.map_err(|e| anyhow::anyhow!("WASM configure call failed: {}", e))?;

let fuel_consumed = match (initial_fuel, instance.fuel_remaining()) {
(Some(initial), Some(remaining)) => Some(initial.saturating_sub(remaining)),
_ => None,
};

let metrics = ExecutionMetrics {
execution_time_ms: start.elapsed().as_millis(),
memory_used_bytes: instance.memory().data_size(instance.store()) as u64,
network_requests_made: instance.network_requests_made(),
fuel_consumed,
};

info!(
module = module_path,
result,
execution_time_ms = metrics.execution_time_ms,
"WASM configure completed"
);

Ok((result, metrics))
}

fn load_module(&self, module_path: &str) -> Result<Arc<WasmModule>> {
{
let cache = self.module_cache.read();
Expand Down
Loading