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
208 changes: 190 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,202 @@
# PVQ
# PVQ: PolkaVM Query System for Polkadot

PolkaVM Query for Polkadot
<!-- generated by polka.codes -->

## Getting Started
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
[![Rust](https://github.com/open-web3-stack/PVQ/workflows/Rust/badge.svg)](https://github.com/open-web3-stack/PVQ/actions)

A powerful and secure query system for Polkadot parachains that enables developers to write expressive, sandboxed guest programs using PolkaVM. PVQ provides a safe and efficient alternative to custom RPC endpoints for runtime data queries.

## ✨ Features

- **🔒 Secure Execution**: Sandboxed PolkaVM environment for safe query execution
- **🧩 Modular Extensions**: Extensible system for exposing runtime functionalities
- **⚡ High Performance**: Efficient RISC-V execution with minimal overhead
- **🛠️ Developer Friendly**: Rust-first development experience with procedural macros
- **🌐 Runtime Integration**: Seamless integration with Substrate runtimes
- **🔍 Rich Querying**: Support for complex queries involving multiple runtime components

## 🏗️ Architecture

The PVQ system consists of several interconnected components:

```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ PVQ Program │───▶│ PVQ Executor │───▶│ Substrate │
│ (Guest Code) │ │ (Host Side) │ │ Runtime │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ ┌─────────────────┐ │
└─────────────▶│ PVQ Extensions │◀─────────────┘
│ (Modules) │
└─────────────────┘
```

### Core Components

| Component | Description |
|-----------|-------------|
| **[PVQ Program](pvq-program/)** | Guest programs written in Rust that compile to RISC-V |
| **[PVQ Executor](pvq-executor/)** | Host-side component managing PolkaVM instances and runtime interaction |
| **[PVQ Extensions](pvq-extension/)** | Modular system exposing runtime functionalities to guest programs |
| **[PVQ Runtime API](pvq-runtime-api/)** | Substrate runtime API for external query submission |
| **[PVQ Primitives](pvq-primitives/)** | Common types and utilities shared across components |

### Available Extensions

- **[Core Extension](pvq-extension-core/)**: Fundamental functionalities and extension discovery
- **[Fungibles Extension](pvq-extension-fungibles/)**: Asset querying, balances, and metadata
- **[Swap Extension](pvq-extension-swap/)**: DEX interactions, liquidity pools, and price quotes

## 🚀 Getting Started

### Prerequisites

- Pull vendored `polkavm`: `git submodule update --init --recursive`.
- Install `polkatool` (for relinking the standard RV32E ELF to a PolkaVM blob) and `chain-spec-builder` (for building chainspec from a wasm): `make tools`
Ensure you have the following installed:

- **Rust** (latest stable version)
- **Git** with submodule support

### Installation

1. **Clone the repository with submodules:**
```bash
git clone --recursive https://github.com/open-web3-stack/PVQ.git
cd PVQ
```

2. **Install required tools:**
```bash
make tools
```
This installs `polkatool` for ELF to PolkaVM blob conversion and `chain-spec-builder`.

3. **Build the project:**
```bash
cargo build --release
```

### Quick Start

#### Running Example Programs

1. **Build guest programs:**
```bash
make guests
```

2. **Run a test program:**
```bash
cargo run -p pvq-test-runner -- --program output/guest-sum-balance
```

#### Available Example Programs

| Program | Description |
|---------|-------------|
| `guest-sum-balance` | Sum balances of multiple accounts |
| `guest-total-supply` | Get total supply of an asset |
| `guest-sum-balance-percent` | Calculate percentage of total supply for account balances |
| `guest-swap-info` | Query DEX/swap information |

### Runtime Integration

#### Testing with PoC Runtime

1. **Start local test chain:**
```bash
make run
```

2. **Build and test programs:**
```bash
make guests
cargo run -p pvq-test-runner -- --program output/guest-total-supply
```

3. **Use with Polkadot JS UI:**
- Copy the hex-encoded `args` from test runner logs
- Upload program and arguments through the PJS UI

## 📖 Documentation

### Writing Your First PVQ Program

```rust
use pvq_program::program;
use pvq_extension_fungibles::ExtensionFungibles;

#[program]
fn query_balance(account: [u8; 32], asset_id: u32) -> u128 {
ExtensionFungibles::balance(asset_id, &account)
}
```

### Creating Custom Extensions

```rust
use pvq_extension::extension_decl;

#[extension_decl]
pub trait MyCustomExtension {
fn my_query() -> String;
}
```

### Component Documentation

- 📚 **[Development Guide](docs/development.md)** - Comprehensive development setup
- 🔧 **[Extension Development](docs/extensions.md)** - Creating custom extensions
- 🏗️ **[Architecture Deep Dive](docs/architecture.md)** - System design and internals
- 📋 **[API Reference](docs/api.md)** - Complete API documentation

## 🛠️ Development

### Project Structure

```
PVQ/
├── poc/runtime/ # PoC Substrate runtime
├── pvq-program/ # Program macro and utilities
├── pvq-executor/ # Core execution engine
├── pvq-extension*/ # Extension system and implementations
├── pvq-runtime-api/ # Runtime API definition
├── pvq-test-runner/ # Testing utilities
├── guest-examples/ # Example programs
└── vendor/polkavm/ # Vendored PolkaVM dependency
```

### Building from Source

```bash
# Development build
cargo build

# Release build with optimizations
cargo build --release

# Build guest examples
make guests

### Run Examples
# Run all tests
cargo test --all

`guest-examples` contains several guest programs to test the PVQ.
# Run clippy lints
cargo clippy --all --all-targets
```

1. Build guest program: `make guests`
2. Run test runner: `cargo run -p pvq-test-runner -- --program output/<guest-program>`
### Testing

Available PoC guest programs:
```bash
# Run unit tests
cargo test

- `guest-sum-balance`: sum the balances of multiple accounts
- `guest-total-supply`: get the total supply of an asset
- `guest-sum-balance-percent`: sum the balances of multiple accounts and calculate the percentage of the total supply
# Run integration tests
cargo test --test integration

### RuntimeAPI PoC
# Test specific component
cargo test -p pvq-executor

1. Use chopsticks to start a local chain with the RuntimeAPI enabled: `make run`
2. Build guest programs: `make guests`
3. Run test runner to display hex-encoded `args` in tracing logs: `cargo run -p pvq-test-runner -- --program output/<guest-program>`
4. Upload `program` and `args` in PJS UI.
# Run example programs
make test-guests
```
21 changes: 21 additions & 0 deletions guest-examples/sum-balance-percent/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
//! A guest program that sums the balances of a set of accounts for a given asset and returns the percentage of the total supply.
#![no_std]
#![no_main]

/// The primary module for the sum-balance-percent guest program.
///
/// This module defines the necessary types, extension functions, and the main entrypoint
/// for calculating the percentage of total supply represented by the sum of balances
/// for a given set of accounts.
#[pvq_program::program]
mod sum_balance_percent {
/// A type alias for the asset identifier.
type AssetId = u32;
/// A type alias for the account identifier.
type AccountId = [u8; 32];
/// A type alias for the balance of an account.
type Balance = u64;

/// Retrieves the balance of a specific account for a given asset.
///
/// This is an extension function that calls into the runtime.
#[program::extension_fn(extension_id = 1248491991627109725u64, fn_index = 6)]
fn balance(asset: AssetId, who: AccountId) -> Balance {}

/// Retrieves the total supply of a given asset.
///
/// This is an extension function that calls into the runtime.
#[program::extension_fn(extension_id = 1248491991627109725u64, fn_index = 5)]
fn total_supply(asset: AssetId) -> Balance {}

/// The entrypoint of the program.
///
/// It takes an asset ID and a vector of account IDs, calculates the sum of their balances,
/// and returns the percentage of this sum with respect to the total supply of the asset.
#[program::entrypoint]
fn sum_balance(asset: AssetId, accounts: alloc::vec::Vec<AccountId>) -> Balance {
let mut sum_balance = 0;
Expand Down
31 changes: 31 additions & 0 deletions guest-examples/sum-balance/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,59 @@
//! A guest program that sums the balance of a given asset for a list of accounts.
#![no_std]
#![no_main]

/// A guest program that sums the balance of a given asset for a list of accounts.
#[pvq_program::program]
mod sum_balance {

cfg_if::cfg_if! {
if #[cfg(feature = "option_version_1")] {
/// Represents a unique identifier for an account.
type AccountId = [u8; 64];
/// Represents a unique identifier for an asset.
type AssetId = u64;
/// Represents the balance of an asset.
type Balance = u128;
} else if #[cfg(feature = "option_version_2")] {
/// Represents a unique identifier for an account.
type AccountId = [u8; 32];
/// Represents a unique identifier for an asset.
type AssetId = u32;
/// Represents the balance of an asset.
type Balance = u64;
} else {
/// Represents a unique identifier for an account.
type AccountId = [u8; 32];
/// Represents a unique identifier for an asset.
type AssetId = u32;
/// Represents the balance of an asset.
type Balance = u64;
}
}

/// Get the balance of a given asset for a specific account.
///
/// # Arguments
///
/// * `asset`: The identifier of the asset.
/// * `who`: The account identifier.
///
/// # Returns
///
/// The balance of the asset for the specified account.
#[program::extension_fn(extension_id = 1248491991627109725u64, fn_index = 6)]
fn balance(asset: AssetId, who: AccountId) -> Balance {}

/// Sums the balance of a given asset for a list of accounts.
///
/// # Arguments
///
/// * `asset`: The identifier of the asset.
/// * `accounts`: A list of account identifiers.
///
/// # Returns
///
/// The total balance of the asset for all the specified accounts.
#[program::entrypoint]
fn sum_balance(asset: AssetId, accounts: alloc::vec::Vec<AccountId>) -> Balance {
let mut sum = 0;
Expand Down
Loading
Loading