Skip to content
Open
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
26 changes: 26 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,9 @@ members = [
"substrate/primitives/version/proc-macro",
"substrate/primitives/wasm-interface",
"substrate/primitives/weights",
"substrate/pvq/pvq-executor",
"substrate/pvq/pvq-primitives",
"substrate/pvq/pvq-runtime-api",
"substrate/scripts/ci/node-template-release",
"substrate/test-utils",
"substrate/test-utils/cli",
Expand Down Expand Up @@ -1177,6 +1180,9 @@ prometheus-parse = { version = "0.2.2" }
proptest = { version = "1" }
prost = { version = "0.12.4" }
prost-build = { version = "0.13.2" }
pvq-executor = { path = "substrate/pvq/pvq-executor", default-features = false }
pvq-primitives = { path = "substrate/pvq/pvq-primitives", default-features = false }
pvq-runtime-api = { path = "substrate/pvq/pvq-runtime-api", default-features = false }
pyroscope = { version = "0.5.8" }
pyroscope_pprofrs = { version = "0.2.8" }
quick_cache = { version = "0.3" }
Expand Down
37 changes: 37 additions & 0 deletions prdoc/pr_10671.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
title: "Add PVQ foundation layer (primitives, runtime API, and executor)"

doc:
- audience: Runtime Dev
description: |
## Summary

This PR introduces the foundation layer for PVQ (PolkaVM Query), a system that enables
runtime developers to expose chain-specific functionality through standardized interfaces
while allowing client-side developers to perform custom computations through PolkaVM programs.

## What's included

This PR adds three new crates that form the base infrastructure for PVQ:

- **pvq-primitives** (~50 lines): Core types including `PvqResult`, `PvqResponse`, and `PvqError`
with both `std` and `no_std` support
- **pvq-runtime-api** (~35 lines): Substrate runtime API definition with `PvqApi` trait
providing `execute_query` and `metadata` methods
- **pvq-executor** (~400-500 lines): PolkaVM execution wrapper including `PvqExecutor`,
`PvqExecutorContext` trait for registering host functions, gas limits, and error handling

## For runtime developers

These crates provide the foundation that will be extended in subsequent PRs with:
- Extension framework for registering custom host functions
- Concrete extensions for fungibles, swaps, etc.
- Guest-side SDK for writing PVQ programs

crates:
- name: pvq-primitives
bump: minor
- name: pvq-runtime-api
bump: minor
- name: pvq-executor
bump: minor

21 changes: 21 additions & 0 deletions substrate/pvq/pvq-executor/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "pvq-executor"
description = "PVQ program executor"
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
version = "0.1.0"

[dependencies]
tracing = { workspace = true }

polkavm = { workspace = true }

pvq-primitives = { workspace = true }

thiserror = { workspace = true, optional = true }

[features]
default = ["std"]
std = ["polkavm/std", "pvq-primitives/std", "thiserror", "tracing/std"]
47 changes: 47 additions & 0 deletions substrate/pvq/pvq-executor/src/context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! Defines context trait for PVQ executor.

use polkavm::Linker;

/// Provides host integration for [`crate::PvqExecutor`].
///
/// A context is responsible for registering host functions into the [`Linker`] and for providing
/// the mutable user data value passed to guest calls.
pub trait PvqExecutorContext {
/// The user data passed to host functions.
///
/// This is the `T` parameter of [`Linker<T, E>`].
type UserData;
/// The user-defined error type returned by host functions.
///
/// This is the `E` parameter of [`Linker<T, E>`] and becomes [`crate::PvqExecutorError::User`].
type UserError;

/// Registers host functions with the given [`Linker`].
///
/// This is called by [`crate::PvqExecutor::new`] exactly once during construction.
fn register_host_functions(&mut self, linker: &mut Linker<Self::UserData, Self::UserError>);

/// Returns a mutable reference to the user data.
///
/// The executor calls this right before invoking the guest entrypoint, and passes the returned
/// reference to PolkaVM so it is accessible to host functions.
fn data(&mut self) -> &mut Self::UserData;
}
100 changes: 100 additions & 0 deletions substrate/pvq/pvq-executor/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! Defines error types for PVQ executor.
use pvq_primitives::PvqError;
/// Errors that can occur while executing a PVQ program.
#[derive(Debug)]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
pub enum PvqExecutorError<UserError> {
/// The program format is invalid.
#[cfg_attr(feature = "std", error("Invalid PVQ program format"))]
InvalidProgramFormat,
/// A memory access error occurred.
#[cfg_attr(feature = "std", error("Memory access error: {0}"))]
MemoryAccessError(polkavm::MemoryAccessError),
/// A trap occurred during execution.
#[cfg_attr(feature = "std", error("Trap"))]
Trap,
/// Not enough gas to execute the program.
#[cfg_attr(feature = "std", error("Not enough gas"))]
NotEnoughGas,
/// A user-defined error occurred.
#[cfg_attr(feature = "std", error("User error: {0}"))]
User(UserError),
/// A step error occurred.
#[cfg_attr(feature = "std", error("Execution stepped"))]
Step,
/// Another error from PolkaVM occurred.
#[cfg_attr(feature = "std", error("Other PVM error: {0}"))]
OtherPvmError(polkavm::Error),
}

impl<UserError> From<polkavm::CallError<UserError>> for PvqExecutorError<UserError> {
fn from(err: polkavm::CallError<UserError>) -> Self {
match err {
polkavm::CallError::Trap => Self::Trap,
polkavm::CallError::NotEnoughGas => Self::NotEnoughGas,
polkavm::CallError::Error(e) => Self::OtherPvmError(e),
polkavm::CallError::Step => Self::Step,
polkavm::CallError::User(e) => Self::User(e),
}
}
}

impl<UserError> From<polkavm::Error> for PvqExecutorError<UserError> {
fn from(e: polkavm::Error) -> Self {
Self::OtherPvmError(e)
}
}

impl<UserError> From<polkavm::MemoryAccessError> for PvqExecutorError<UserError> {
fn from(e: polkavm::MemoryAccessError) -> Self {
Self::MemoryAccessError(e)
}
}

#[cfg(feature = "std")]
impl<UserError: core::fmt::Debug> From<PvqExecutorError<UserError>> for PvqError {
fn from(e: PvqExecutorError<UserError>) -> PvqError {
match e {
PvqExecutorError::InvalidProgramFormat => "Invalid PVQ program format".to_string(),
PvqExecutorError::MemoryAccessError(_) => "Memory access error".to_string(),
PvqExecutorError::Trap => "Trap".to_string(),
PvqExecutorError::NotEnoughGas => "Not enough gas".to_string(),
PvqExecutorError::User(user_error) => format!("Host call error: {user_error:?}"),
PvqExecutorError::OtherPvmError(pvm_error) => format!("Other error: {pvm_error:?}"),
PvqExecutorError::Step => "Execution stepped".to_string(),
}
}
}

#[cfg(not(feature = "std"))]
impl<UserError> From<PvqExecutorError<UserError>> for PvqError {
fn from(e: PvqExecutorError<UserError>) -> PvqError {
match e {
PvqExecutorError::InvalidProgramFormat => PvqError::InvalidPvqProgramFormat,
PvqExecutorError::MemoryAccessError(_) => PvqError::MemoryAccessError,
PvqExecutorError::Trap => PvqError::Trap,
PvqExecutorError::NotEnoughGas => PvqError::QueryExceedsWeightLimit,
PvqExecutorError::User(_) => PvqError::HostCallError,
PvqExecutorError::Step => PvqError::Step,
PvqExecutorError::OtherPvmError(_) => PvqError::Other,
}
}
}
Loading
Loading