Skip to content

Commit ee2ae3f

Browse files
committed
fix: use execute_get_routes_from_bytes to avoid runtime nesting panic
1 parent c35ffd2 commit ee2ae3f

File tree

2 files changed

+118
-51
lines changed

2 files changed

+118
-51
lines changed

bins/validator-node/src/main.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -756,10 +756,18 @@ async fn main() -> Result<()> {
756756
cs.register_wasm_challenge(wasm_config);
757757
}
758758

759-
// Load and register routes
759+
// Load and register routes using the WASM bytes we already have
760760
if let Some(ref executor) = wasm_executor {
761-
match executor.execute_get_routes(
761+
// Get the WASM bytes from distributed storage (just stored)
762+
let wasm_bytes_result = storage.get(
763+
&StorageKey::new("wasm", &challenge_id_str),
764+
platform_distributed_storage::GetOptions::default()
765+
).await;
766+
767+
if let Ok(Some(stored)) = wasm_bytes_result {
768+
match executor.execute_get_routes_from_bytes(
762769
&challenge_id_str,
770+
&stored.data,
763771
&wasm_runtime_interface::NetworkPolicy::default(),
764772
&wasm_runtime_interface::SandboxPolicy::default(),
765773
) {
@@ -790,6 +798,7 @@ async fn main() -> Result<()> {
790798
);
791799
}
792800
}
801+
} // end if wasm_bytes_result
793802
}
794803
}
795804
Err(e) => {
@@ -1420,10 +1429,11 @@ async fn handle_network_event(
14201429
cs.register_wasm_challenge(wasm_config);
14211430
}
14221431

1423-
// Load and log WASM routes
1432+
// Load and log WASM routes using the bytes we already have
14241433
if let Some(ref executor) = wasm_executor_ref {
1425-
match executor.execute_get_routes(
1434+
match executor.execute_get_routes_from_bytes(
14261435
&challenge_id_str,
1436+
&update.data,
14271437
&wasm_runtime_interface::NetworkPolicy::default(),
14281438
&wasm_runtime_interface::SandboxPolicy::default(),
14291439
) {

bins/validator-node/src/wasm_executor.rs

Lines changed: 104 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,106 @@ impl WasmChallengeExecutor {
686686
Ok((result_data, metrics))
687687
}
688688

689+
/// Execute get_routes using provided WASM bytes directly (avoids reload)
690+
pub fn execute_get_routes_from_bytes(
691+
&self,
692+
module_id: &str,
693+
wasm_bytes: &[u8],
694+
network_policy: &NetworkPolicy,
695+
sandbox_policy: &SandboxPolicy,
696+
) -> Result<(Vec<u8>, ExecutionMetrics)> {
697+
let start = Instant::now();
698+
699+
info!(
700+
module = module_id,
701+
size_bytes = wasm_bytes.len(),
702+
"Compiling WASM module from bytes"
703+
);
704+
705+
let module = self
706+
.runtime
707+
.compile_module(wasm_bytes)
708+
.map_err(|e| anyhow::anyhow!("Failed to compile WASM module: {}", e))?;
709+
710+
let network_host_fns = Arc::new(NetworkHostFunctions::all());
711+
712+
let instance_config = InstanceConfig {
713+
network_policy: network_policy.clone(),
714+
sandbox_policy: sandbox_policy.clone(),
715+
exec_policy: ExecPolicy::default(),
716+
time_policy: TimePolicy::default(),
717+
audit_logger: None,
718+
memory_export: "memory".to_string(),
719+
challenge_id: module_id.to_string(),
720+
validator_id: "validator".to_string(),
721+
restart_id: String::new(),
722+
config_version: 0,
723+
storage_host_config: StorageHostConfig::default(),
724+
storage_backend: Arc::new(InMemoryStorageBackend::new()),
725+
fixed_timestamp_ms: None,
726+
consensus_policy: ConsensusPolicy::default(),
727+
terminal_policy: TerminalPolicy::default(),
728+
llm_policy: match &self.config.chutes_api_key {
729+
Some(key) => LlmPolicy::with_api_key(key.clone()),
730+
None => LlmPolicy::default(),
731+
},
732+
..Default::default()
733+
};
734+
735+
let mut instance = self
736+
.runtime
737+
.instantiate(&module, instance_config, Some(network_host_fns))
738+
.map_err(|e| anyhow::anyhow!("WASM instantiation failed: {}", e))?;
739+
740+
let initial_fuel = instance.fuel_remaining();
741+
742+
let result = instance
743+
.call_return_i64("get_routes")
744+
.map_err(|e| anyhow::anyhow!("WASM get_routes call failed: {}", e))?;
745+
746+
let out_len = (result >> 32) as i32;
747+
let out_ptr = (result & 0xFFFF_FFFF) as i32;
748+
749+
if out_len > 0 && out_len as u64 > MAX_ROUTE_OUTPUT_SIZE {
750+
return Err(anyhow::anyhow!(
751+
"WASM get_routes output size {} exceeds maximum allowed {}",
752+
out_len,
753+
MAX_ROUTE_OUTPUT_SIZE
754+
));
755+
}
756+
757+
let result_data = if out_ptr > 0 && out_len > 0 {
758+
instance
759+
.read_memory(out_ptr as usize, out_len as usize)
760+
.map_err(|e| {
761+
anyhow::anyhow!("failed to read WASM memory for get_routes output: {}", e)
762+
})?
763+
} else {
764+
Vec::new()
765+
};
766+
767+
let fuel_consumed = match (initial_fuel, instance.fuel_remaining()) {
768+
(Some(initial), Some(remaining)) => Some(initial.saturating_sub(remaining)),
769+
_ => None,
770+
};
771+
772+
let metrics = ExecutionMetrics {
773+
execution_time_ms: start.elapsed().as_millis(),
774+
memory_used_bytes: instance.memory().data_size(instance.store()) as u64,
775+
network_requests_made: instance.network_requests_made(),
776+
fuel_consumed,
777+
};
778+
779+
info!(
780+
module = module_id,
781+
result_bytes = result_data.len(),
782+
execution_time_ms = metrics.execution_time_ms,
783+
"WASM get_routes from bytes completed"
784+
);
785+
786+
Ok((result_data, metrics))
787+
}
788+
689789
#[allow(dead_code)]
690790
pub fn execute_handle_route(
691791
&self,
@@ -916,53 +1016,10 @@ impl WasmChallengeExecutor {
9161016
}
9171017
}
9181018

919-
// Try to load from distributed storage first
920-
let wasm_bytes = if let Some(ref storage) = self.config.distributed_storage {
921-
let storage = Arc::clone(storage);
922-
let key = platform_distributed_storage::StorageKey::new("wasm", module_path);
923-
924-
// Spawn a new thread with its own runtime to avoid nesting runtimes
925-
// Use join() to ensure thread completes before returning
926-
let handle = std::thread::spawn(move || {
927-
let rt = match tokio::runtime::Builder::new_current_thread()
928-
.enable_all()
929-
.build()
930-
{
931-
Ok(rt) => rt,
932-
Err(_) => return None,
933-
};
934-
let result = rt.block_on(async {
935-
storage
936-
.get(&key, platform_distributed_storage::GetOptions::default())
937-
.await
938-
.ok()
939-
.flatten()
940-
});
941-
// Explicitly shutdown runtime before thread exit
942-
drop(rt);
943-
result
944-
});
945-
946-
match handle.join() {
947-
Ok(Some(stored)) => {
948-
info!(
949-
module = module_path,
950-
size_bytes = stored.data.len(),
951-
"Loading WASM module from distributed storage"
952-
);
953-
Some(stored.data)
954-
}
955-
_ => {
956-
debug!(
957-
module = module_path,
958-
"WASM module not found in distributed storage"
959-
);
960-
None
961-
}
962-
}
963-
} else {
964-
None
965-
};
1019+
// Note: We don't try distributed storage here because this is a sync function
1020+
// that may be called from async context. The WASM should already be cached
1021+
// locally after upload. Use load_module_async for distributed storage access.
1022+
let wasm_bytes: Option<Vec<u8>> = None;
9661023

9671024
// Fallback to filesystem if not in distributed storage
9681025
let wasm_bytes = match wasm_bytes {

0 commit comments

Comments
 (0)