The core execution engine for PVQ (PolkaVM Query) programs. This crate provides a secure, sandboxed environment for running guest programs compiled to RISC-V, with controlled access to runtime functionality through extensions.
The PVQ Executor serves as the bridge between compiled guest programs and the Substrate runtime. It manages PolkaVM instances, handles program lifecycle, and provides secure execution context for queries.
- 🔒 Secure Execution: Sandboxed PolkaVM environment with resource limits
- ⚡ High Performance: Optimized for fast query execution
- 🛡️ Memory Safety: Controlled memory allocation and access
- 🔌 Extension Integration: Seamless interaction with PVQ extensions
- ⏱️ Timeout Protection: Configurable execution time limits
- 📊 Resource Monitoring: Track memory usage and execution metrics
┌─────────────────────────────────────────────────────────────┐
│ PVQ Executor │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │
│ │ PvqExecutor │ │ ExecutorContext │ │ PolkaVM │ │
│ │ │ │ │ │ Instance │ │
│ │ ├─ Lifecycle │ │ ├─ Extensions │ │ │ │
│ │ ├─ Resource │ │ ├─ Runtime │ │ ├─ Memory │ │
│ │ │ Management │ │ │ Access │ │ ├─ Execution │ │
│ │ └─ Error │ │ └─ Permissions │ │ └─ Sandbox │ │
│ │ Handling │ │ │ │ │ │
│ └─────────────────┘ └─────────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
The main executor that manages the complete lifecycle of PVQ program execution.
Responsibilities:
- Program loading and validation
- PolkaVM instance creation and management
- Execution context setup
- Resource limit enforcement
- Error handling and recovery
Provides the execution context and environment for guest programs.
Responsibilities:
- Extension registry management
- Runtime state access
- Permission control
- Inter-component communication
Comprehensive error types covering all execution scenarios.
Error Categories:
- Program loading errors
- Execution timeout/resource exhaustion
- Extension call failures
- Memory access violations
use pvq_executor::{PvqExecutor, PvqExecutorContext};
use pvq_extension::ExtensionsExecutor;
// Create executor context
let extensions = ExtensionsExecutor::new();
let context = PvqExecutorContext::new(extensions);
// Create and configure executor
let executor = PvqExecutor::new(context);
// Load and execute program
let program_blob = std::fs::read("program.polkavm")?;
let args = vec![1, 2, 3, 4]; // Serialized arguments
let result = executor.execute(&program_blob, &args)?;
println!("Execution result: {:?}", result);use pvq_executor::{PvqExecutor, PvqExecutorConfig, PvqExecutorContext};
use std::time::Duration;
// Configure execution parameters
let config = PvqExecutorConfig {
max_memory: 32 * 1024 * 1024, // 32MB
max_execution_time: Duration::from_secs(30),
enable_debugging: false,
stack_size: 1024 * 1024, // 1MB
};
// Create executor with custom config
let context = PvqExecutorContext::new(extensions);
let executor = PvqExecutor::with_config(context, config);use pvq_executor::{PvqExecutor, PvqExecutorError};
match executor.execute(&program_blob, &args) {
Ok(result) => {
println!("Success: {:?}", result);
}
Err(PvqExecutorError::Timeout) => {
eprintln!("Program execution timed out");
}
Err(PvqExecutorError::OutOfMemory) => {
eprintln!("Program exceeded memory limits");
}
Err(PvqExecutorError::ExtensionError(e)) => {
eprintln!("Extension call failed: {}", e);
}
Err(e) => {
eprintln!("Execution failed: {}", e);
}
}| Parameter | Default | Description |
|---|---|---|
max_memory |
16MB | Maximum memory allocation |
max_execution_time |
10s | Maximum execution duration |
stack_size |
512KB | Program stack size |
max_call_depth |
1024 | Maximum call stack depth |
| Setting | Default | Description |
|---|---|---|
enable_debugging |
false | Enable debug information |
strict_memory |
true | Strict memory access checking |
extension_whitelist |
None | Allowed extensions (None = all) |
let config = PvqExecutorConfig {
// Optimize for throughput
max_memory: 64 * 1024 * 1024,
precompile_programs: true,
enable_jit: true,
// Or optimize for safety
max_execution_time: Duration::from_secs(5),
strict_memory: true,
enable_debugging: false,
};use pvq_executor::{PvqExecutor, PvqExecutorContext};
use pvq_runtime_api::PvqApi;
use sp_api::ProvideRuntimeApi;
impl<Block: BlockT> PvqApi<Block> for Runtime {
fn execute_query(&self, program: Vec<u8>, args: Vec<u8>) -> Vec<u8> {
let extensions = self.create_extensions_executor();
let context = PvqExecutorContext::new(extensions);
let executor = PvqExecutor::new(context);
executor.execute(&program, &args)
.unwrap_or_else(|e| format!("Error: {}", e).into_bytes())
}
}use pvq_executor::PvqExecutorContext;
use pvq_extension::ExtensionsExecutor;
use pvq_extension_fungibles::ExtensionFungibles;
// Register extensions
let mut extensions = ExtensionsExecutor::new();
extensions.register::<ExtensionFungibles>();
// Create context with extensions
let context = PvqExecutorContext::new(extensions);# Build the executor
cargo build -p pvq-executor
# Build with all features
cargo build -p pvq-executor --all-features
# Run tests
cargo test -p pvq-executor# Run unit tests
cargo test -p pvq-executor
# Run integration tests
cargo test -p pvq-executor --test integration
# Test with different configurations
cargo test -p pvq-executor --test config_tests
# Benchmark performance
cargo bench -p pvq-executorEnable debug logging:
use log::debug;
// In executor implementation
debug!("Executing program with {} bytes", program.len());
debug!("Memory usage: {} bytes", memory_usage);Run with debug output:
RUST_LOG=pvq_executor=debug cargo test| Error Type | Description | Recovery |
|---|---|---|
InvalidProgram |
Malformed program blob | Validate program before execution |
Timeout |
Execution time limit exceeded | Increase timeout or optimize program |
OutOfMemory |
Memory limit exceeded | Increase memory limit or optimize program |
ExtensionError |
Extension call failed | Check extension implementation |
RuntimeError |
Runtime execution error | Debug program logic |
Program won't load:
# Validate program format
polkatool validate program.polkavm
# Check file size and format
file program.polkavmExecution timeouts:
// Increase timeout for complex queries
let config = PvqExecutorConfig {
max_execution_time: Duration::from_secs(60),
..Default::default()
};Memory errors:
// Monitor memory usage
let config = PvqExecutorConfig {
max_memory: 64 * 1024 * 1024, // Increase limit
enable_debugging: true, // Enable memory tracking
..Default::default()
};- Program Size: Keep programs small for faster loading
- Memory Access: Use sequential memory access patterns
- Extension Calls: Minimize expensive extension operations
- Data Structures: Use efficient serialization formats
# Run performance benchmarks
cargo bench -p pvq-executor
# Profile memory usage
cargo run --example memory_profile --release
# Measure execution time
cargo run --example timing_analysis --release- Programs execute in a sandboxed environment
- Memory access is controlled and limited
- Execution time is bounded to prevent DoS
- Extension calls go through permission checks
- No direct system call access from guest programs
- PVQ Extension System - Extension framework
- PVQ Runtime API - Runtime integration
- PVQ Program - Program development tools
- PVQ Test Runner - Testing utilities
The PVQ Executor provides the secure foundation for all PVQ program execution.