VM Integration: - Add compute module with offloadable operations support - Enable distributed execution for heavy VM operations - Support batch signature verification, merkle proofs, hashing - Add ComputeContext for managing compute cluster connections - Feature-gated behind 'compute' flag Hosting Integration: - Add edge compute module for serverless functions - Support edge functions (WASM, JS, Python runtimes) - Enable server-side rendering and image optimization - Add AI/ML inference at the edge - Feature-gated behind 'compute' flag Docker Deployment: - Add docker-compose.compute.yml for compute layer - Deploy orchestrator, CPU workers, WASM worker, spot market - Include Redis for task queue and Prometheus for metrics - Reserved ports: 17250-17290 for compute services
319 lines
9.6 KiB
Rust
319 lines
9.6 KiB
Rust
//! WASM Virtual Machine for Synor smart contracts.
|
|
//!
|
|
//! Synor uses Rust-only smart contracts compiled to WebAssembly. This provides:
|
|
//! - Memory safety and type safety
|
|
//! - Predictable gas metering
|
|
//! - Sandboxed execution
|
|
//! - Rich standard library
|
|
//!
|
|
//! # Architecture
|
|
//!
|
|
//! ```text
|
|
//! ┌─────────────────────────────────────────────────────────────────┐
|
|
//! │ SMART CONTRACT VM │
|
|
//! ├─────────────────────────────────────────────────────────────────┤
|
|
//! │ │
|
|
//! │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
|
|
//! │ │ WASM │ │ Host │ │ Contract │ │
|
|
//! │ │ Engine │ │ Functions │ │ Storage │ │
|
|
//! │ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
|
|
//! │ │ │ │ │
|
|
//! │ └─────────────────┼──────────────────────┘ │
|
|
//! │ │ │
|
|
//! │ ┌──────▼───────┐ │
|
|
//! │ │ Execution │ │
|
|
//! │ │ Context │ │
|
|
//! │ └──────────────┘ │
|
|
//! │ │
|
|
//! └─────────────────────────────────────────────────────────────────┘
|
|
//! ```
|
|
//!
|
|
//! # Contract Interface
|
|
//!
|
|
//! Contracts must export these functions:
|
|
//! - `init(params: &[u8]) -> Result<(), Error>` - Initialize contract
|
|
//! - `call(method: &str, params: &[u8]) -> Result<Vec<u8>, Error>` - Call method
|
|
|
|
#![allow(dead_code)]
|
|
|
|
pub mod compression;
|
|
pub mod compute;
|
|
pub mod context;
|
|
pub mod engine;
|
|
pub mod gas;
|
|
pub mod gas_estimator;
|
|
pub mod host;
|
|
pub mod scheduler;
|
|
pub mod speculation;
|
|
pub mod storage;
|
|
pub mod tiered;
|
|
|
|
pub use context::{CallContext, ExecutionContext};
|
|
pub use engine::{ContractModule, VmEngine, VmInstance};
|
|
pub use gas::{Gas, GasConfig, GasMeter};
|
|
pub use gas_estimator::{costs, GasEstimate, GasEstimator};
|
|
pub use host::HostFunctions;
|
|
pub use scheduler::{
|
|
AccessSet, ExecutionBatch, LockManager, ParallelExecutor, ParallelScheduler,
|
|
ScheduledResult, ScheduledTransaction, SchedulerConfig, SchedulerStats, TransactionBuilder,
|
|
};
|
|
pub use storage::{ContractStorage, MemoryStorage, StorageKey, StorageValue};
|
|
pub use tiered::{
|
|
CompilationTier, CompilerStatistics, TieredCompiler, TieredConfig, TieredModule,
|
|
};
|
|
pub use compression::{
|
|
BytecodeCompressor, CompressedBytecode, CompressedContractStore, CompressionConfig,
|
|
CompressionLevel, CompressionStats, DeltaPatch,
|
|
};
|
|
pub use speculation::{
|
|
CachedState, ContractCache, GlobalCacheStats, HotStateCache, PendingExecution,
|
|
SpeculativeConfig, SpeculativeContext, SpeculativeExecutor, SpeculativeResult,
|
|
SpeculativeStats, StateSnapshot, StateVersion,
|
|
};
|
|
|
|
use synor_types::{Address, Hash256};
|
|
|
|
/// Maximum contract code size (1 MB).
|
|
pub const MAX_CONTRACT_SIZE: usize = 1024 * 1024;
|
|
|
|
/// Maximum memory pages (64 KB each, max 16 MB).
|
|
pub const MAX_MEMORY_PAGES: u32 = 256;
|
|
|
|
/// Maximum call depth.
|
|
pub const MAX_CALL_DEPTH: u32 = 64;
|
|
|
|
/// Maximum execution time (milliseconds).
|
|
pub const MAX_EXECUTION_TIME_MS: u64 = 30_000;
|
|
|
|
/// Contract identifier.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
pub struct ContractId(pub Hash256);
|
|
|
|
impl ContractId {
|
|
/// Creates a new contract ID.
|
|
pub fn new(hash: Hash256) -> Self {
|
|
ContractId(hash)
|
|
}
|
|
|
|
/// Creates from bytes.
|
|
pub fn from_bytes(bytes: [u8; 32]) -> Self {
|
|
ContractId(Hash256::from_bytes(bytes))
|
|
}
|
|
|
|
/// Returns as bytes.
|
|
pub fn as_bytes(&self) -> &[u8; 32] {
|
|
self.0.as_bytes()
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for ContractId {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", hex::encode(&self.0.as_bytes()[..8]))
|
|
}
|
|
}
|
|
|
|
/// Contract execution result.
|
|
#[derive(Clone, Debug)]
|
|
pub struct ExecutionResult {
|
|
/// Return data.
|
|
pub return_data: Vec<u8>,
|
|
/// Gas used.
|
|
pub gas_used: u64,
|
|
/// Logs emitted.
|
|
pub logs: Vec<ContractLog>,
|
|
/// Storage changes.
|
|
pub storage_changes: Vec<StorageChange>,
|
|
/// Internal calls made.
|
|
pub internal_calls: Vec<InternalCall>,
|
|
}
|
|
|
|
impl ExecutionResult {
|
|
/// Creates a successful empty result.
|
|
pub fn success() -> Self {
|
|
ExecutionResult {
|
|
return_data: Vec::new(),
|
|
gas_used: 0,
|
|
logs: Vec::new(),
|
|
storage_changes: Vec::new(),
|
|
internal_calls: Vec::new(),
|
|
}
|
|
}
|
|
|
|
/// Creates a result with return data.
|
|
pub fn with_data(data: Vec<u8>) -> Self {
|
|
ExecutionResult {
|
|
return_data: data,
|
|
gas_used: 0,
|
|
logs: Vec::new(),
|
|
storage_changes: Vec::new(),
|
|
internal_calls: Vec::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Log emitted by a contract.
|
|
#[derive(Clone, Debug)]
|
|
pub struct ContractLog {
|
|
/// Contract that emitted the log.
|
|
pub contract: ContractId,
|
|
/// Log topics (indexed).
|
|
pub topics: Vec<Hash256>,
|
|
/// Log data.
|
|
pub data: Vec<u8>,
|
|
}
|
|
|
|
/// Storage change made by a contract.
|
|
#[derive(Clone, Debug)]
|
|
pub struct StorageChange {
|
|
/// Contract storage.
|
|
pub contract: ContractId,
|
|
/// Storage key.
|
|
pub key: StorageKey,
|
|
/// Old value (None if new).
|
|
pub old_value: Option<StorageValue>,
|
|
/// New value (None if deleted).
|
|
pub new_value: Option<StorageValue>,
|
|
}
|
|
|
|
/// Internal contract call.
|
|
#[derive(Clone, Debug)]
|
|
pub struct InternalCall {
|
|
/// Caller contract.
|
|
pub from: ContractId,
|
|
/// Callee contract.
|
|
pub to: ContractId,
|
|
/// Method called.
|
|
pub method: String,
|
|
/// Call value.
|
|
pub value: u64,
|
|
/// Gas limit.
|
|
pub gas_limit: u64,
|
|
/// Input data.
|
|
pub input: Vec<u8>,
|
|
/// Success status.
|
|
pub success: bool,
|
|
/// Return data.
|
|
pub return_data: Vec<u8>,
|
|
}
|
|
|
|
/// Contract deployment parameters.
|
|
#[derive(Clone, Debug)]
|
|
pub struct DeployParams {
|
|
/// Contract bytecode (WASM).
|
|
pub code: Vec<u8>,
|
|
/// Constructor arguments.
|
|
pub args: Vec<u8>,
|
|
/// Initial value to send.
|
|
pub value: u64,
|
|
/// Gas limit.
|
|
pub gas_limit: u64,
|
|
/// Deployer address.
|
|
pub deployer: Address,
|
|
/// Salt for CREATE2-style addresses.
|
|
pub salt: Option<Hash256>,
|
|
}
|
|
|
|
/// Contract call parameters.
|
|
#[derive(Clone, Debug)]
|
|
pub struct CallParams {
|
|
/// Target contract.
|
|
pub contract: ContractId,
|
|
/// Method to call.
|
|
pub method: String,
|
|
/// Call arguments.
|
|
pub args: Vec<u8>,
|
|
/// Value to send.
|
|
pub value: u64,
|
|
/// Gas limit.
|
|
pub gas_limit: u64,
|
|
/// Caller address.
|
|
pub caller: Address,
|
|
}
|
|
|
|
/// VM errors.
|
|
#[derive(Debug, Clone, thiserror::Error)]
|
|
pub enum VmError {
|
|
/// Contract not found.
|
|
#[error("Contract not found: {0}")]
|
|
ContractNotFound(ContractId),
|
|
|
|
/// Invalid bytecode.
|
|
#[error("Invalid bytecode: {0}")]
|
|
InvalidBytecode(String),
|
|
|
|
/// Bytecode too large.
|
|
#[error("Bytecode too large: {size} > {max}")]
|
|
BytecodeTooLarge { size: usize, max: usize },
|
|
|
|
/// Out of gas.
|
|
#[error("Out of gas: used {used}, limit {limit}")]
|
|
OutOfGas { used: u64, limit: u64 },
|
|
|
|
/// Execution error.
|
|
#[error("Execution error: {0}")]
|
|
ExecutionError(String),
|
|
|
|
/// Memory access violation.
|
|
#[error("Memory access violation: {0}")]
|
|
MemoryViolation(String),
|
|
|
|
/// Stack overflow.
|
|
#[error("Stack overflow")]
|
|
StackOverflow,
|
|
|
|
/// Call depth exceeded.
|
|
#[error("Call depth exceeded: {0} > {}", MAX_CALL_DEPTH)]
|
|
CallDepthExceeded(u32),
|
|
|
|
/// Execution timeout.
|
|
#[error("Execution timeout")]
|
|
Timeout,
|
|
|
|
/// Invalid method.
|
|
#[error("Invalid method: {0}")]
|
|
InvalidMethod(String),
|
|
|
|
/// Serialization error.
|
|
#[error("Serialization error: {0}")]
|
|
SerializationError(String),
|
|
|
|
/// Storage error.
|
|
#[error("Storage error: {0}")]
|
|
StorageError(String),
|
|
|
|
/// Host function error.
|
|
#[error("Host function error: {0}")]
|
|
HostError(String),
|
|
|
|
/// Contract panicked.
|
|
#[error("Contract panicked: {0}")]
|
|
Panic(String),
|
|
|
|
/// Reverted by contract.
|
|
#[error("Reverted: {0}")]
|
|
Revert(String),
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_contract_id() {
|
|
let hash = Hash256::from_bytes([0x42; 32]);
|
|
let id = ContractId::new(hash);
|
|
|
|
assert_eq!(id.as_bytes(), &[0x42; 32]);
|
|
assert!(!id.to_string().is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_execution_result() {
|
|
let result = ExecutionResult::success();
|
|
assert!(result.return_data.is_empty());
|
|
assert_eq!(result.gas_used, 0);
|
|
|
|
let result = ExecutionResult::with_data(vec![1, 2, 3]);
|
|
assert_eq!(result.return_data, vec![1, 2, 3]);
|
|
}
|
|
}
|