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
2 changes: 2 additions & 0 deletions bins/validator-node/src/wasm_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ impl WasmChallengeExecutor {
validator_id: "validator".to_string(),
restart_id: String::new(),
config_version: 0,
..Default::default()
};

let mut instance = self
Expand Down Expand Up @@ -181,6 +182,7 @@ impl WasmChallengeExecutor {
validator_id: "validator".to_string(),
restart_id: String::new(),
config_version: 0,
..Default::default()
};

let mut instance = self
Expand Down
8 changes: 5 additions & 3 deletions crates/wasm-runtime-interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ pub use exec::{
ExecError, ExecHostFunction, ExecHostFunctions, ExecPolicy, ExecRequest, ExecResponse,
ExecState,
};
pub use network::{NetworkHostFunctions, NetworkState, NetworkStateError};
pub use network::{
NetworkHostFunctions, NetworkState, NetworkStateError, HOST_GET_TIMESTAMP, HOST_LOG_MESSAGE,
};
pub use storage::{
InMemoryStorageBackend, NoopStorageBackend, StorageAuditEntry, StorageAuditLogger,
StorageBackend, StorageDeleteRequest, StorageGetRequest, StorageGetResponse, StorageHostConfig,
StorageHostError, StorageHostState, StorageHostStatus, StorageOperation,
StorageHostError, StorageHostFunctions, StorageHostState, StorageHostStatus, StorageOperation,
StorageProposeWriteRequest, StorageProposeWriteResponse,
};

Expand All @@ -43,7 +45,7 @@ pub use runtime::{
};
pub use storage::{
HOST_STORAGE_ALLOC, HOST_STORAGE_DELETE, HOST_STORAGE_GET, HOST_STORAGE_GET_RESULT,
HOST_STORAGE_NAMESPACE, HOST_STORAGE_PROPOSE_WRITE,
HOST_STORAGE_NAMESPACE, HOST_STORAGE_PROPOSE_WRITE, HOST_STORAGE_SET,
};
pub use time::{TimeError, TimeHostFunction, TimeHostFunctions, TimeMode, TimePolicy, TimeState};

Expand Down
76 changes: 61 additions & 15 deletions crates/wasm-runtime-interface/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,20 @@ use std::io::Read;
use std::net::IpAddr;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tracing::{info, warn};
use tracing::{error, info, warn};
use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};
use trust_dns_resolver::proto::rr::RecordType;
use trust_dns_resolver::Resolver;
use wasmtime::{Caller, Linker, Memory};

use crate::runtime::{HostFunctionRegistrar, RuntimeState, WasmRuntimeError};

pub const HOST_LOG_MESSAGE: &str = "log_message";
pub const HOST_GET_TIMESTAMP: &str = "get_timestamp";

const DEFAULT_RESPONSE_BUF_SIZE: i32 = 65536;
const DEFAULT_DNS_BUF_SIZE: i32 = 4096;

#[derive(Debug, thiserror::Error)]
pub enum NetworkStateError {
#[error("network policy invalid: {0}")]
Expand Down Expand Up @@ -86,10 +92,9 @@ impl HostFunctionRegistrar for NetworkHostFunctions {
|mut caller: Caller<RuntimeState>,
req_ptr: i32,
req_len: i32,
resp_ptr: i32,
resp_len: i32|
resp_ptr: i32|
-> i32 {
handle_http_get(&mut caller, req_ptr, req_len, resp_ptr, resp_len)
handle_http_get(&mut caller, req_ptr, req_len, resp_ptr)
},
)
.map_err(|err| WasmRuntimeError::HostFunction(err.to_string()))?;
Expand All @@ -104,7 +109,8 @@ impl HostFunctionRegistrar for NetworkHostFunctions {
req_ptr: i32,
req_len: i32,
resp_ptr: i32,
resp_len: i32|
resp_len: i32,
_extra: i32|
-> i32 {
handle_http_post(&mut caller, req_ptr, req_len, resp_ptr, resp_len)
},
Expand All @@ -120,15 +126,32 @@ impl HostFunctionRegistrar for NetworkHostFunctions {
|mut caller: Caller<RuntimeState>,
req_ptr: i32,
req_len: i32,
resp_ptr: i32,
resp_len: i32|
resp_ptr: i32|
-> i32 {
handle_dns_request(&mut caller, req_ptr, req_len, resp_ptr, resp_len)
handle_dns_request(&mut caller, req_ptr, req_len, resp_ptr)
},
)
.map_err(|err| WasmRuntimeError::HostFunction(err.to_string()))?;
}

linker
.func_wrap(
HOST_FUNCTION_NAMESPACE,
HOST_LOG_MESSAGE,
|mut caller: Caller<RuntimeState>, level: i32, msg_ptr: i32, msg_len: i32| {
handle_log_message(&mut caller, level, msg_ptr, msg_len);
},
)
.map_err(|err| WasmRuntimeError::HostFunction(err.to_string()))?;

linker
.func_wrap(
HOST_FUNCTION_NAMESPACE,
HOST_GET_TIMESTAMP,
|caller: Caller<RuntimeState>| -> i64 { handle_get_timestamp(&caller) },
)
.map_err(|err| WasmRuntimeError::HostFunction(err.to_string()))?;

Ok(())
}
}
Expand All @@ -147,11 +170,6 @@ pub struct NetworkState {
}

impl NetworkState {
/// Create network state for a single WASM execution.
///
/// The policy is validated and then enforced by every host call. All host
/// functions share the same counters and audit logger, providing a single
/// enforcement surface for HTTP and DNS access.
pub fn new(
policy: NetworkPolicy,
audit_logger: Option<Arc<dyn NetworkAuditLogger>>,
Expand Down Expand Up @@ -578,8 +596,8 @@ fn handle_http_get(
req_ptr: i32,
req_len: i32,
resp_ptr: i32,
resp_len: i32,
) -> i32 {
let resp_len = DEFAULT_RESPONSE_BUF_SIZE;
let enforcement = "http_get";
let request_bytes = match read_memory(caller, req_ptr, req_len) {
Ok(bytes) => bytes,
Expand Down Expand Up @@ -678,8 +696,8 @@ fn handle_dns_request(
req_ptr: i32,
req_len: i32,
resp_ptr: i32,
resp_len: i32,
) -> i32 {
let resp_len = DEFAULT_DNS_BUF_SIZE;
let enforcement = "dns_resolve";
let request_bytes = match read_memory(caller, req_ptr, req_len) {
Ok(bytes) => bytes,
Expand Down Expand Up @@ -716,6 +734,34 @@ fn handle_dns_request(
write_result(caller, resp_ptr, resp_len, result)
}

fn handle_log_message(caller: &mut Caller<RuntimeState>, level: i32, msg_ptr: i32, msg_len: i32) {
let msg = match read_memory(caller, msg_ptr, msg_len) {
Ok(bytes) => String::from_utf8_lossy(&bytes).into_owned(),
Err(err) => {
warn!(
challenge_id = %caller.data().challenge_id,
error = %err,
"log_message: failed to read message from wasm memory"
);
return;
}
};

let challenge_id = caller.data().challenge_id.clone();
match level {
0 => info!(challenge_id = %challenge_id, "[wasm] {}", msg),
1 => warn!(challenge_id = %challenge_id, "[wasm] {}", msg),
_ => error!(challenge_id = %challenge_id, "[wasm] {}", msg),
}
}

fn handle_get_timestamp(caller: &Caller<RuntimeState>) -> i64 {
if let Some(ts) = caller.data().fixed_timestamp_ms {
return ts;
}
chrono::Utc::now().timestamp_millis()
}

fn resolve_dns(
resolver: &Resolver,
request: &DnsRequest,
Expand Down
57 changes: 56 additions & 1 deletion crates/wasm-runtime-interface/src/runtime.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use crate::bridge::{self, BridgeError, EvalRequest, EvalResponse};
use crate::exec::{ExecPolicy, ExecState};
use crate::storage::{
InMemoryStorageBackend, StorageBackend, StorageHostConfig, StorageHostFunctions,
StorageHostState,
};
use crate::time::{TimePolicy, TimeState};
use crate::{NetworkAuditLogger, NetworkPolicy, NetworkState};
use crate::{NetworkAuditLogger, NetworkHostFunctions, NetworkPolicy, NetworkState};
use std::sync::Arc;
use std::time::Instant;
use thiserror::Error;
Expand Down Expand Up @@ -103,6 +107,12 @@ pub struct InstanceConfig {
pub restart_id: String,
/// Configuration version for hot-restarts.
pub config_version: u64,
/// Storage host function configuration.
pub storage_host_config: StorageHostConfig,
/// Storage backend implementation.
pub storage_backend: Arc<dyn StorageBackend>,
/// Fixed timestamp for deterministic consensus execution.
pub fixed_timestamp_ms: Option<i64>,
}

impl Default for InstanceConfig {
Expand All @@ -117,6 +127,9 @@ impl Default for InstanceConfig {
validator_id: "unknown".to_string(),
restart_id: String::new(),
config_version: 0,
storage_host_config: StorageHostConfig::default(),
storage_backend: Arc::new(InMemoryStorageBackend::new()),
fixed_timestamp_ms: None,
}
}
}
Expand All @@ -140,6 +153,10 @@ pub struct RuntimeState {
pub restart_id: String,
/// Configuration version for hot-restarts.
pub config_version: u64,
/// Storage host state for key-value operations.
pub storage_state: StorageHostState,
/// Fixed timestamp in milliseconds for deterministic consensus execution.
pub fixed_timestamp_ms: Option<i64>,
limits: StoreLimits,
}

Expand All @@ -155,6 +172,8 @@ impl RuntimeState {
validator_id: String,
restart_id: String,
config_version: u64,
storage_state: StorageHostState,
fixed_timestamp_ms: Option<i64>,
limits: StoreLimits,
) -> Self {
Self {
Expand All @@ -167,6 +186,8 @@ impl RuntimeState {
validator_id,
restart_id,
config_version,
storage_state,
fixed_timestamp_ms,
limits,
}
}
Expand All @@ -175,6 +196,10 @@ impl RuntimeState {
self.network_state.reset_counters();
}

pub fn reset_storage_counters(&mut self) {
self.storage_state.reset_counters();
}

pub fn reset_exec_counters(&mut self) {
self.exec_state.reset_counters();
}
Expand Down Expand Up @@ -242,6 +267,11 @@ impl WasmRuntime {
instance_config.validator_id.clone(),
)
.map_err(|err| WasmRuntimeError::HostFunction(err.to_string()))?;
let storage_state = StorageHostState::new(
instance_config.challenge_id.clone(),
instance_config.storage_host_config.clone(),
Arc::clone(&instance_config.storage_backend),
);
let exec_state = ExecState::new(
instance_config.exec_policy.clone(),
instance_config.challenge_id.clone(),
Expand All @@ -262,6 +292,8 @@ impl WasmRuntime {
instance_config.validator_id.clone(),
instance_config.restart_id.clone(),
instance_config.config_version,
storage_state,
instance_config.fixed_timestamp_ms,
limits.build(),
);
let mut store = Store::new(&self.engine, runtime_state);
Expand All @@ -277,6 +309,13 @@ impl WasmRuntime {
store.limiter(|state| &mut state.limits);

let mut linker = Linker::new(&self.engine);

let network_host_fns = NetworkHostFunctions::all();
network_host_fns.register(&mut linker)?;

let storage_host_fns = StorageHostFunctions::new();
storage_host_fns.register(&mut linker)?;

if let Some(registrar) = registrar {
registrar.register(&mut linker)?;
}
Expand Down Expand Up @@ -436,6 +475,22 @@ impl ChallengeInstance {
self.store.data_mut().reset_network_counters();
}

pub fn reset_storage_state(&mut self) {
self.store.data_mut().reset_storage_counters();
}

pub fn storage_bytes_read(&self) -> u64 {
self.store.data().storage_state.bytes_read
}

pub fn storage_bytes_written(&self) -> u64 {
self.store.data().storage_state.bytes_written
}

pub fn storage_operations_count(&self) -> u32 {
self.store.data().storage_state.operations_count
}

pub fn challenge_id(&self) -> &str {
&self.store.data().challenge_id
}
Expand Down
Loading