Skip to content
Snippets Groups Projects
Andrew Jones's avatar
Andrew Jones authored
Closes #216.

This PR allows pallets to define a `view_functions` impl like so:

```rust
#[pallet::view_functions]
impl<T: Config> Pallet<T>
where
	T::AccountId: From<SomeType1> + SomeAssociation1,
{
	/// Query value no args.
	pub fn get_value() -> Option<u32> {
		SomeValue::<T>::get()
	}

	/// Query value with args.
	pub fn get_value_with_arg(key: u32) -> Option<u32> {
		SomeMap::<T>::get(key)
	}
}
```
### `QueryId`

Each view function is uniquely identified by a `QueryId`, which for this
implementation is generated by:

```twox_128(pallet_name) ++ twox_128("fn_name(fnarg_types) -> return_ty")```

The prefix `twox_128(pallet_name)` is the same as the storage prefix for pallets and take into account multiple instances of the same pallet.

The suffix is generated from the fn type signature so is guaranteed to be unique for that pallet impl. For one of the view fns in the example above it would be `twox_128("get_value_with_arg(u32) -> Option<u32>")`. It is a known limitation that only the type names themselves are taken into account: in the case of type aliases the signature may have the same underlying types but a different id; for generics the concrete types may be different but the signatures will remain the same.

The existing Runtime `Call` dispatchables are addressed by their concatenated indices `pallet_index ++ call_index`, and the dispatching is handled by the SCALE decoding of the `RuntimeCallEnum::PalletVariant(PalletCallEnum::dispatchable_variant(payload))`. For `view_functions` the runtime/pallet generated enum structure is replaced by implementing the `DispatchQuery` trait on the outer (runtime) scope, dispatching to a pallet based on the id prefix, and the inner (pallet) scope dispatching to the specific function based on the id suffix.

Future implementations could also modify/extend this scheme and routing to pallet agnostic queries.

### Executing externally

These view functions can be executed externally via the system runtime api:

```rust
pub trait ViewFunctionsApi<QueryId, Query, QueryResult, Error> where
	QueryId: codec::Codec,
	Query: codec::Codec,
	QueryResult: codec::Codec,
	Error: codec::Codec,
{
	/// Execute a view function query.
fn execute_query(query_id: QueryId, query: Query) -> Result<QueryResult,
Error>;
}
```
### `XCQ`
Currently there is work going on by @xlc to implement [`XCQ`](https://github.com/open-web3-stack/XCQ/) which may eventually supersede this work.

It may be that we still need the fixed function local query dispatching in addition to XCQ, in the same way that we have chain specific runtime dispatchables and XCM.

I have kept this in mind and the high level query API is agnostic to the underlying query dispatch and execution. I am just providing the implementation for the `view_function` definition.

### Metadata
Currently I am utilizing the `custom` section of the frame metadata, to avoid modifying the official metadata format until this is standardized.

### vs `runtime_api`
There are similarities with `runtime_apis`, some differences being:
- queries can be defined directly on pallets, so no need for boilerplate declarations and implementations
- no versioning, the `QueryId` will change if the signature changes. 
- possibility for queries to be executed from smart contracts (see below)

### Calling from contracts
Future work would be to add `weight` annotations to the view function queries, and a host function to `pallet_contracts` to allow executing these queries from contracts.

### TODO

- [x] Consistent naming (view functions pallet impl, queries, high level api?)
- [ ] End to end tests via `runtime_api`
- [ ] UI tests
- [x] Mertadata tests
- [ ] Docs

---------

Co-authored-by: default avatarkianenigma <kian@parity.io>
Co-authored-by: default avatarJames Wilson <james@jsdw.me>
Co-authored-by: default avatarGiuseppe Re <giuseppe.re@parity.io>
Co-authored-by: default avatarGuillaume Thiolliere <guillaume.thiolliere@parity.io>
0b8d7441

Polkadot SDK's Minimal Template

Polkadot SDK Logo Polkadot SDK Logo

This is a minimal template for creating a blockchain based on Polkadot SDK.

This template is automatically updated after releases in the main Polkadot SDK monorepo.

Table of Contents

Intro

  • 🤏 This template is a minimal (in terms of complexity and the number of components) template for building a blockchain node.

  • 🔧 Its runtime is configured with a single custom pallet as a starting point, and a handful of ready-made pallets such as a Balances pallet.

  • 👤 The template has no consensus configured - it is best for experimenting with a single node network.

Template Structure

A Polkadot SDK based project such as this one consists of:

  • 🧮 the Runtime - the core logic of the blockchain.
  • 🎨 the Pallets - from which the runtime is constructed.
  • 💿 a Node - the binary application (which is not part of the cargo default-members list and is not compiled unless building the entire workspace).

Getting Started

  • 🦀 The template is using the Rust language.

  • 👉 Check the Rust installation instructions for your system.

  • 🛠️ Depending on your operating system and Rust version, there might be additional packages required to compile this template - please take note of the Rust compiler output.

Fetch minimal template code.

git clone https://github.com/paritytech/polkadot-sdk-minimal-template.git minimal-template

cd minimal-template

Starting a Minimal Template Chain

Omni Node

Omni Node can be used to run the minimal template's runtime. polkadot-omni-node binary crate usage is described at a high-level on crates.io.

Install polkadot-omni-node

Please see installation section on crates.io/omni-node.

Build minimal-template-runtime

cargo build -p minimal-template-runtime --release

Install staging-chain-spec-builder

Please see the installation section at crates.io/staging-chain-spec-builder.

Use chain-spec-builder to generate the chain_spec.json file

chain-spec-builder create --relay-chain "dev" --para-id 1000 --runtime \
    target/release/wbuild/minimal-template-runtime/minimal_template_runtime.wasm named-preset development

Note: the relay-chain and para-id flags are extra bits of information required to configure the node for the case of representing a parachain that is connected to a relay chain. They are not relevant to minimal template business logic, but they are mandatory information for Omni Node, nonetheless.

Run Omni Node

Start Omni Node in development mode (sets up block production and finalization based on manual seal, sealing a new block every 3 seconds), with a minimal template runtime chain spec.

polkadot-omni-node --chain <path/to/chain_spec.json> --dev

Minimal Template Node

Build both node & runtime

cargo build --workspace --release

🐳 Alternatively, build the docker image which builds all the workspace members, and has as entry point the node binary:

docker build . -t polkadot-sdk-minimal-template

Start the minimal-template-node

The minimal-template-node has dependency on the minimal-template-runtime. It will use the minimal_template_runtime::WASM_BINARY constant (which holds the WASM blob as a byte array) for chain spec building, while starting. This is in contrast to Omni Node which doesn't depend on a specific runtime, but asks for the chain spec at startup.

<target/release/path/to/minimal-template-node> --tmp --consensus manual-seal-3000
# or via docker
docker run --rm polkadot-sdk-minimal-template

Zombienet with Omni Node

Install zombienet

We can install zombienet as described here, and zombienet-omni-node.toml contains the network specification we want to start.

Update zombienet-omni-node.toml with a valid chain spec path

To simplify the process of starting the minimal template with ZombieNet and Omni Node, we've included a pre-configured development chain spec (dev_chain_spec.json) in the minimal template. The zombienet-omni-node.toml file in this template points to it, but you can update it to a new path for the chain spec generated on your machine. To generate a chain spec refer to staging-chain-spec-builder

Then make the changes in the network specification like so:

# ...
chain = "dev"
chain_spec_path = "<TO BE UPDATED WITH A VALID PATH>"
default_args = ["--dev"]
# ..

Start the network

zombienet --provider native spawn zombienet-omni-node.toml

Zombienet with minimal-template-node

For this one we just need to have zombienet installed and run:

zombienet --provider native spawn zombienet-multi-node.toml

Connect with the Polkadot-JS Apps Front-End

  • 🌐 You can interact with your local node using the hosted version of the Polkadot/Substrate Portal.

  • 🪐 A hosted version is also available on IPFS.

  • 🧑‍🔧 You can also find the source code and instructions for hosting your own instance in the polkadot-js/apps repository.

Takeaways

Previously minimal template's development chains:

  • ❌ Started in a multi-node setup will produce forks because minimal lacks consensus.
  • 🧹 Do not persist the state.
  • 💰 Are pre-configured with a genesis state that includes several pre-funded development accounts.
  • 🧑‍⚖️ One development account (ALICE) is used as sudo accounts.

Contributing

Getting Help