# Technical Documentation: QuipNetwork/quip-protocol-rs

> ℹ️ **Provenance:** Hybrid Fusion: `QuipNetwork/quip-protocol-rs` (README + 9 In-Tree Chapters) · [CodeWiki Reference](https://codewiki.google/github.com/QuipNetwork/quip-protocol-rs) · Recency: Active (< 180 days)

## 1. Project Overview & Quickstart (QuipNetwork/quip-protocol-rs)

# Quip Network node

Quip Network solochain node (`quip-network-node`).

## Chain specs

Omit `--chain` to join the live public Quip Testnet (EIP-155 `20033`).
`--chain=local` is a disposable Alice-and-Bob chain. It is not the public testnet.

| Flag | Preset | Network | EIP-155 |
|------|--------|---------|--------:|
| (omit `--chain`) | `quip-testnet` | live public testnet | 20033 |
| `--chain=quip-testnet` | `quip-testnet` | live public testnet | 20033 |
| `--dev` or `--chain=dev` | Development | Alice-only local | 1337 |
| `--chain=local` | Local Testnet | Alice and Bob local | 1337 |
| `--chain=local3` | Local Testnet (3 Validators) | Alice, Bob, and Charlie local | 1337 |

Aliases for the public testnet: `quip_testnet`, `testnet`. A path argument loads that JSON file and uses the chain ID in its genesis.

## Getting Started

Depending on your operating system and Rust version, there might be additional
packages required to compile this template. Check the
[Install](https://docs.substrate.io/install/) instructions for your platform for
the most common dependencies. Alternatively, you can use one of the [alternative
installation](#alternatives-installations) options.

Fetch solochain template code:

```sh
git clone https://github.com/paritytech/polkadot-sdk-solochain-template.git solochain-template

cd solochain-template
```

### Build

🔨 Use the following command to build the node without launching it:

```sh
cargo build --release
```

### Join the live public testnet

Omit `--chain` to select `quip-testnet` (EIP-155 `20033`).

```sh
# Live public testnet (EIP-155 20033). Default if you omit --chain.
./target/release/quip-network-node
```

### Embedded Docs

After you build the project, you can use the following command to explore its
parameters and subcommands:

```sh
./target/release/solochain-template-node -h
```

You can generate and view the [Rust
Docs](https://doc.rust-lang.org/cargo/commands/cargo-doc.html) for this template
with this command:

```sh
cargo +nightly doc --open
```

### Single-Node Development Chain

The following command starts a single-node development chain that does not
persist state. `--dev` is the Alice-only local preset (EIP-155 `1337`), not
the public testnet.

```sh
# Alice-only local chain (EIP-155 1337)
./target/release/solochain-template-node --dev
```

To purge the development chain's state, run the following command:

```sh
# Alice-only local chain (EIP-155 1337)
./target/release/solochain-template-node purge-chain --dev
```

To start the development chain with detailed logging, run the following command:

```sh
# Alice-only local chain (EIP-155 1337)
RUST_BACKTRACE=1 ./target/release/solochain-template-node -ldebug --dev
```

Development chains (`--dev` / `--chain=dev`):

- Maintain state in a `tmp` folder while the node is running.
- Use the **Alice** account as the sole validator authority.
- Use the **Alice** account as the default `sudo` account.
- Are preconfigured with a genesis state (`/node/src/chain_spec.rs`) that
  includes several pre-funded development accounts.
- Use EIP-155 chain ID `1337`.


To persist chain state between runs, specify a base path by running a command
similar to the following:

```sh
// Create a folder to use as the db base path
$ mkdir my-chain-state

// Alice-only local chain (EIP-155 1337)
$ ./target/release/solochain-template-node --dev --base-path ./my-chain-state/

// Check the folder structure created inside the base path after running the chain
$ ls ./my-chain-state
chains
$ ls ./my-chain-state/chains/
dev
$ ls ./my-chain-state/chains/dev
db keystore network
```

### Connect with Polkadot-JS Apps Front-End

After you start the node template locally, you can interact with it using the
hosted version of the [Polkadot/Substrate
Portal](https://polkadot.js.org/apps/#/explorer?rpc=ws://localhost:9944)
front-end by connecting to the local node endpoint. A hosted version is also
available on [IPFS](https://dotapps.io/). You can
also find the source code and instructions for hosting your own instance in the
[`polkadot-js/apps`](https://github.com/polkadot-js/apps) repository.

Quip uses hybrid BABE and GRANDPA consensus keys. Polkadot.js Apps does not
require custom types for Quip anymore; for usage notes, see
[docs/polkadotjs/README.md](/Users/romanuseinov/projects/quip/quip-protocol-rs/docs/polkadotjs/README.md).

### Multi-Node Local Testnet

A scripted three-validator local network is available two ways.
Both use `--chain=local3` (EIP-155 `1337`). This is not the public testnet.

- **Native build:** `scripts/start-local3.sh` builds the debug binary and starts
  three validators (Alice/Bob/Charlie) against the embedded `local3` chain spec.
- **Docker:** `docker compose up --build` starts the same three-validator
  topology in containers. See the [Docker](#docker) section below.

Both paths use the same hardcoded libp2p node-keys and bootnode peer ID, so
they're interchangeable for development.

For background on multi-node consensus, see [Simulate a
network](https://docs.substrate.io/tutorials/build-a-blockchain/simulate-network/).

## Template Structure

A Substrate project such as this consists of a number of components that are
spread across a few directories.

### Node

A blockchain node is an application that allows users to participate in a
blockchain network. Substrate-based blockchain nodes expose a number of
capabilities:

- Networking: Substrate nodes use the [`libp2p`](https://libp2p.io/) networking
  stack to allow the nodes in the network to communicate with one another.
- Consensus: Blockchains must have a way to come to
  [consensus](https://docs.substrate.io/fundamentals/consensus/) on the state of
  the network. Substrate makes it possible to supply custom consensus engines
  and also ships with several consensus mechanisms that have been built on top
  of [Web3 Foundation
  research](https://research.web3.foundation/Polkadot/protocols/NPoS).
- RPC Server: A remote procedure call (RPC) server is used to interact with
  Substrate nodes.

There are several files in the `node` directory. Take special note of the
following:

- [`chain_spec.rs`](./node/src/chain_spec.rs): A [chain
  specification](https://docs.substrate.io/build/chain-spec/) is a source code
  file that defines a Substrate chain's initial (genesis) state. Chain
  specifications are useful for development and testing, and critical when
  architecting the launch of a production chain. Take note of the
  `development_config` and `testnet_genesis` functions. These functions are
  used to define the genesis state for the local development chain
  configuration. These functions identify some [well-known
  accounts](https://docs.substrate.io/reference/command-line-tools/subkey/) and
  use them to configure the blockchain's initial state.
- [`service.rs`](./node/src/service.rs): This file defines the node
  implementation. Take note of the libraries that this file imports and the
  names of the functions it invokes. In particular, there are references to
  consensus-related topics, such as the [block finalization and
  forks](https://docs.substrate.io/fundamentals/consensus/#finalization-and-forks)
  and other [consensus
  mechanisms](https://docs.substrate.io/fundamentals/consensus/#default-consensus-models)
  such as BABE for block authoring and GRANDPA for finality.


### Runtime

In Substrate, the terms "runtime" and "state transition function" are analogous.
Both terms refer to the core logic of the blockchain that is responsible for
validating blocks and executing the state changes they define. The Substrate
project in this repository uses
[FRAME](https://docs.substrate.io/learn/runtime-development/#frame) to construct
a blockchain runtime. FRAME allows runtime developers to declare domain-specific
logic in modules called "pallets". At the heart of FRAME is a helpful [macro
language](https://docs.substrate.io/reference/frame-macros/) that makes it easy
to create pallets and flexibly compose them to create blockchains that can
address [a variety of needs](https://substrate.io/ecosystem/projects/).

Review the [FRAME runtime implementation](./runtime/src/lib.rs) included in this
template and note the following:

- This file configures several pallets to include in the runtime. Each pallet
  configuration is defined by a code block that begins with `impl
  $PALLET_NAME::Config for Runtime`.
- The pallets are composed into a single runtime by way of the
  [#[runtime]](https://paritytech.github.io/polkadot-sdk/master/frame_support/attr.runtime.html)
  macro, which is part of the [core FRAME pallet
  library](https://docs.substrate.io/reference/frame-pallets/#system-pallets).

### Pallets

The runtime in this project is constructed using many FRAME pallets that ship
with [the Substrate
repository](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame) and a
template pallet that is [defined in the
`pallets`](./pallets/template/src/lib.rs) directory.

A FRAME pallet is comprised of a number of blockchain primitives, including:

- Storage: FRAME defines a rich set of powerful [storage
  abstractions](https://docs.substrate.io/build/runtime-storage/) that makes it
  easy to use Substrate's efficient key-value database to manage the evolving
  state of a blockchain.
- Dispatchables: FRAME pallets define special types of functions that can be
  invoked (dispatched) from outside of the runtime in order to update its state.
- Events: Substrate uses
  [events](https://docs.substrate.io/build/events-and-errors/) to notify users
  of significant state changes.
- Errors: When a dispatchable fails, it returns an error.

Each pallet has its own `Config` trait which serves as a configuration interface
to generically define the types and parameters it depends on.

## Alternatives Installations

Instead of installing dependencies and building this source directly, consider
the following alternatives.

### Nix

Install [nix](https://nixos.org/) and
[nix-direnv](https://github.com/nix-community/nix-direnv) for a fully
plug-and-play experience for setting up the development environment. To get all
the correct dependencies, activate direnv `direnv allow`.

### Docker

A multi-stage `Dockerfile` builds the `quip-network-node` binary on top of
`debian:bookworm-slim` (~80 MB runtime image). The image exposes the binary
directly as ENTRYPOINT, so any Substrate CLI flag works at `docker run` time.

#### Build

```sh
docker build -t quip-network-node:local .
```

The first build compiles the full workspace and takes a while. BuildKit cache
mounts (declared in the Dockerfile) keep the cargo registry and target
directory between local rebuilds.

#### Pre-built images

Every push to `main` and every git tag publishes an image to the project's
GitLab Container Registry, so you don't have to build locally:

```sh
docker pull registry.gitlab.com/quip.network/quip-protocol-rs/quip-network-node:latest
```

Tag scheme:

- `:latest` — tip of `main`. Floating, advances on every merge.
- `:sha-<short>` — pinned to a specific commit on `main` or to a tagged release.
- `:<git-tag>` — pinned to a release tag (e.g. `:v0.1.0`).

#### Run as a validator

```sh
# Three-validator local chain (EIP-155 1337). Not the public testnet.
docker run --rm -v quip-data:/data -p 9944:9944 -p 30333:30333 \
  quip-network-node:local \
  --chain=local3 --base-path=/data \
  --validator --alice \
  --unsafe-rpc-external --rpc-cors=all
```

`--unsafe-rpc-external` is required because Substrate refuses to combine
`--rpc-external` with `--validator` by default (a safety guard against
exposing a validator's RPC to the public internet). For local development
the unsafe flag is fine; for production validators you almost certainly do
not want any external RPC at all.

#### Run as a full node

Same command, omit `--validator` (and the `--alice/--bob/--charlie` shortcut):

```sh
# Three-validator local chain (EIP-155 1337). Not the public testnet.
docker run --rm -v quip-data:/data -p 9944:9944 -p 30333:30333 \
  quip-network-node:local \
  --chain=local3 --base-path=/data \
  --bootnodes=/dns//tcp/30333/p2p/ \
  --rpc-external --rpc-cors=all
```

#### Local 3-node network via docker-compose

`docker-compose.yml` reproduces `scripts/start-local3.sh` in containers.
Each service passes `--chain=local3` (EIP-155 `1337`). This is not the public
testnet.

```sh
docker compose up --build           # start local3 (EIP-155 1337)
docker compose down                 # stop, keep chain state
docker compose down -v              # stop and wipe state
```

Then connect Polkadot.js Apps to `ws://localhost:9944` (node1),
`ws://localhost:9945` (node2), or `ws://localhost:9946` (node3).

## Public testnet

`quip-testnet` is the live public testnet ("AGLS" tokens, 12 decimals,
EIP-155 `20033`). Omitting `--chain` selects this preset. The canonical
genesis is baked into the `v0.2.0+` binary and also published as a raw JSON
file at `nodes.quip.network/chain-specs/quip-testnet.json`.

### Quickstart (Docker)

```sh
# Pull the matching release image
docker pull registry.gitlab.com/quip.network/quip-protocol-rs/quip-network-node:v0.2.0

# Live public testnet (EIP-155 20033). Omit --chain for the same preset.
docker run --rm -v quip-data:/data -p 9944:9944 -p 30333:30333 \
  registry.gitlab.com/quip.network/quip-protocol-rs/quip-network-node:v0.2.0 \
  --chain=quip-testnet --base-path=/data \
  --name="my-quip-node"
```

The three canonical bootnodes (`bootnode-{1,2,3}.testnet.quip.network`) are
embedded in the chain spec, so peer discovery happens automatically.

### Using the hosted raw chain spec

Alternatively, fetch the published JSON spec from `nodes.quip.network` and
pass its path to `--chain`. That file is the live public testnet
(EIP-155 `20033`).

```sh
curl -fsSL https://gitlab.com/quip.network/nodes.quip.network/-/raw/main/chain-specs/quip-testnet.json \
    -o quip-testnet.json

# Live public testnet JSON (EIP-155 20033)
docker run --rm -v "$PWD:/spec" -v quip-data:/data -p 9944:9944 -p 30333:30333 \
  registry.gitlab.com/quip.network/quip-protocol-rs/quip-network-node:v0.2.0 \
  --chain=/spec/quip-testnet.json --base-path=/data
```

### Running a validator

Operator validator slots are committed at genesis (see
[`docs/genesis-quip-testnet.md`](docs/genesis-quip-testnet.md)). To rotate or
add a slot, follow [`docs/testnet-keys.md`](docs/testnet-keys.md) and the
`scripts/derive-operator-keys.sh` helper.

## 2. In-Tree Documentation Chapters (QuipNetwork/quip-protocol-rs)

## File: README.md

# Quip Network node

Quip Network solochain node (`quip-network-node`).

## Chain specs

Omit `--chain` to join the live public Quip Testnet (EIP-155 `20033`).
`--chain=local` is a disposable Alice-and-Bob chain. It is not the public testnet.

| Flag | Preset | Network | EIP-155 |
|------|--------|---------|--------:|
| (omit `--chain`) | `quip-testnet` | live public testnet | 20033 |
| `--chain=quip-testnet` | `quip-testnet` | live public testnet | 20033 |
| `--dev` or `--chain=dev` | Development | Alice-only local | 1337 |
| `--chain=local` | Local Testnet | Alice and Bob local | 1337 |
| `--chain=local3` | Local Testnet (3 Validators) | Alice, Bob, and Charlie local | 1337 |

Aliases for the public testnet: `quip_testnet`, `testnet`. A path argument loads that JSON file and uses the chain ID in its genesis.

## Getting Started

Depending on your operating system and Rust version, there might be additional
packages required to compile this template. Check the
[Install](https://docs.substrate.io/install/) instructions for your platform for
the most common dependencies. Alternatively, you can use one of the [alternative
installation](#alternatives-installations) options.

Fetch solochain template code:

```sh
git clone https://github.com/paritytech/polkadot-sdk-solochain-template.git solochain-template

cd solochain-template
```

### Build

🔨 Use the following command to build the node without launching it:

```sh
cargo build --release
```

### Join the live public testnet

Omit `--chain` to select `quip-testnet` (EIP-155 `20033`).

```sh
# Live public testnet (EIP-155 20033). Default if you omit --chain.
./target/release/quip-network-node
```

### Embedded Docs

After you build the project, you can use the following command to explore its
parameters and subcommands:

```sh
./target/release/solochain-template-node -h
```

You can generate and view the [Rust
Docs](https://doc.rust-lang.org/cargo/commands/cargo-doc.html) for this template
with this command:

```sh
cargo +nightly doc --open
```

### Single-Node Development Chain

The following command starts a single-node development chain that does not
persist state. `--dev` is the Alice-only local preset (EIP-155 `1337`), not
the public testnet.

```sh
# Alice-only local chain (EIP-155 1337)
./target/release/solochain-template-node --dev
```

To purge the development chain's state, run the following command:

```sh
# Alice-only local chain (EIP-155 1337)
./target/release/solochain-template-node purge-chain --dev
```

To start the development chain with detailed logging, run the following command:

```sh
# Alice-only local chain (EIP-155 1337)
RUST_BACKTRACE=1 ./target/release/solochain-template-node -ldebug --dev
```

Development chains (`--dev` / `--chain=dev`):

- Maintain state in a `tmp` folder while the node is running.
- Use the **Alice** account as the sole validator authority.
- Use the **Alice** account as the default `sudo` account.
- Are preconfigured with a genesis state (`/node/src/chain_spec.rs`) that
  includes several pre-funded development accounts.
- Use EIP-155 chain ID `1337`.


To persist chain state between runs, specify a base path by running a command
similar to the following:

```sh
// Create a folder to use as the db base path
$ mkdir my-chain-state

// Alice-only local chain (EIP-155 1337)
$ ./target/release/solochain-template-node --dev --base-path ./my-chain-state/

// Check the folder structure created inside the base path after running the chain
$ ls ./my-chain-state
chains
$ ls ./my-chain-state/chains/
dev
$ ls ./my-chain-state/chains/dev
db keystore network
```

### Connect with Polkadot-JS Apps Front-End

After you start the node template locally, you can interact with it using the
hosted version of the [Polkadot/Substrate
Portal](https://polkadot.js.org/apps/#/explorer?rpc=ws://localhost:9944)
front-end by connecting to the local node endpoint. A hosted version is also
available on [IPFS](https://dotapps.io/). You can
also find the source code and instructions for hosting your own instance in the
[`polkadot-js/apps`](https://github.com/polkadot-js/apps) repository.

Quip uses hybrid BABE and GRANDPA consensus keys. Polkadot.js Apps does not
require custom types for Quip anymore; for usage notes, see
[docs/polkadotjs/README.md](/Users/romanuseinov/projects/quip/quip-protocol-rs/docs/polkadotjs/README.md).

### Multi-Node Local Testnet

A scripted three-validator local network is available two ways.
Both use `--chain=local3` (EIP-155 `1337`). This is not the public testnet.

- **Native build:** `scripts/start-local3.sh` builds the debug binary and starts
  three validators (Alice/Bob/Charlie) against the embedded `local3` chain spec.
- **Docker:** `docker compose up --build` starts the same three-validator
  topology in containers. See the [Docker](#docker) section below.

Both paths use the same hardcoded libp2p node-keys and bootnode peer ID, so
they're interchangeable for development.

For background on multi-node consensus, see [Simulate a
network](https://docs.substrate.io/tutorials/build-a-blockchain/simulate-network/).

## Template Structure

A Substrate project such as this consists of a number of components that are
spread across a few directories.

### Node

A blockchain node is an application that allows users to participate in a
blockchain network. Substrate-based blockchain nodes expose a number of
capabilities:

- Networking: Substrate nodes use the [`libp2p`](https://libp2p.io/) networking
  stack to allow the nodes in the network to communicate with one another.
- Consensus: Blockchains must have a way to come to
  [consensus](https://docs.substrate.io/fundamentals/consensus/) on the state of
  the network. Substrate makes it possible to supply custom consensus engines
  and also ships with several consensus mechanisms that have been built on top
  of [Web3 Foundation
  research](https://research.web3.foundation/Polkadot/protocols/NPoS).
- RPC Server: A remote procedure call (RPC) server is used to interact with
  Substrate nodes.

There are several files in the `node` directory. Take special note of the
following:

- [`chain_spec.rs`](./node/src/chain_spec.rs): A [chain
  specification](https://docs.substrate.io/build/chain-spec/) is a source code
  file that defines a Substrate chain's initial (genesis) state. Chain
  specifications are useful for development and testing, and critical when
  architecting the launch of a production chain. Take note of the
  `development_config` and `testnet_genesis` functions. These functions are
  used to define the genesis state for the local development chain
  configuration. These functions identify some [well-known
  accounts](https://docs.substrate.io/reference/command-line-tools/subkey/) and
  use them to configure the blockchain's initial state.
- [`service.rs`](./node/src/service.rs): This file defines the node
  implementation. Take note of the libraries that this file imports and the
  names of the functions it invokes. In particular, there are references to
  consensus-related topics, such as the [block finalization and
  forks](https://docs.substrate.io/fundamentals/consensus/#finalization-and-forks)
  and other [consensus
  mechanisms](https://docs.substrate.io/fundamentals/consensus/#default-consensus-models)
  such as BABE for block authoring and GRANDPA for finality.


### Runtime

In Substrate, the terms "runtime" and "state transition function" are analogous.
Both terms refer to the core logic of the blockchain that is responsible for
validating blocks and executing the state changes they define. The Substrate
project in this repository uses
[FRAME](https://docs.substrate.io/learn/runtime-development/#frame) to construct
a blockchain runtime. FRAME allows runtime developers to declare domain-specific
logic in modules called "pallets". At the heart of FRAME is a helpful [macro
language](https://docs.substrate.io/reference/frame-macros/) that makes it easy
to create pallets and flexibly compose them to create blockchains that can
address [a variety of needs](https://substrate.io/ecosystem/projects/).

Review the [FRAME runtime implementation](./runtime/src/lib.rs) included in this
template and note the following:

- This file configures several pallets to include in the runtime. Each pallet
  configuration is defined by a code block that begins with `impl
  $PALLET_NAME::Config for Runtime`.
- The pallets are composed into a single runtime by way of the
  [#[runtime]](https://paritytech.github.io/polkadot-sdk/master/frame_support/attr.runtime.html)
  macro, which is part of the [core FRAME pallet
  library](https://docs.substrate.io/reference/frame-pallets/#system-pallets).

### Pallets

The runtime in this project is constructed using many FRAME pallets that ship
with [the Substrate
repository](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame) and a
template pallet that is [defined in the
`pallets`](./pallets/template/src/lib.rs) directory.

A FRAME pallet is comprised of a number of blockchain primitives, including:

- Storage: FRAME defines a rich set of powerful [storage
  abstractions](https://docs.substrate.io/build/runtime-storage/) that makes it
  easy to use Substrate's efficient key-value database to manage the evolving
  state of a blockchain.
- Dispatchables: FRAME pallets define special types of functions that can be
  invoked (dispatched) from outside of the runtime in order to update its state.
- Events: Substrate uses
  [events](https://docs.substrate.io/build/events-and-errors/) to notify users
  of significant state changes.
- Errors: When a dispatchable fails, it returns an error.

Each pallet has its own `Config` trait which serves as a configuration interface
to generically define the types and parameters it depends on.

## Alternatives Installations

Instead of installing dependencies and building this source directly, consider
the following alternatives.

### Nix

Install [nix](https://nixos.org/) and
[nix-direnv](https://github.com/nix-community/nix-direnv) for a fully
plug-and-play experience for setting up the development environment. To get all
the correct dependencies, activate direnv `direnv allow`.

### Docker

A multi-stage `Dockerfile` builds the `quip-network-node` binary on top of
`debian:bookworm-slim` (~80 MB runtime image). The image exposes the binary
directly as ENTRYPOINT, so any Substrate CLI flag works at `docker run` time.

#### Build

```sh
docker build -t quip-network-node:local .
```

The first build compiles the full workspace and takes a while. BuildKit cache
mounts (declared in the Dockerfile) keep the cargo registry and target
directory between local rebuilds.

#### Pre-built images

Every push to `main` and every git tag publishes an image to the project's
GitLab Container Registry, so you don't have to build locally:

```sh
docker pull registry.gitlab.com/quip.network/quip-protocol-rs/quip-network-node:latest
```

Tag scheme:

- `:latest` — tip of `main`. Floating, advances on every merge.
- `:sha-<short>` — pinned to a specific commit on `main` or to a tagged release.
- `:<git-tag>` — pinned to a release tag (e.g. `:v0.1.0`).

#### Run as a validator

```sh
# Three-validator local chain (EIP-155 1337). Not the public testnet.
docker run --rm -v quip-data:/data -p 9944:9944 -p 30333:30333 \
  quip-network-node:local \
  --chain=local3 --base-path=/data \
  --validator --alice \
  --unsafe-rpc-external --rpc-cors=all
```

`--unsafe-rpc-external` is required because Substrate refuses to combine
`--rpc-external` with `--validator` by default (a safety guard against
exposing a validator's RPC to the public internet). For local development
the unsafe flag is fine; for production validators you almost certainly do
not want any external RPC at all.

#### Run as a full node

Same command, omit `--validator` (and the `--alice/--bob/--charlie` shortcut):

```sh
# Three-validator local chain (EIP-155 1337). Not the public testnet.
docker run --rm -v quip-data:/data -p 9944:9944 -p 30333:30333 \
  quip-network-node:local \
  --chain=local3 --base-path=/data \
  --bootnodes=/dns//tcp/30333/p2p/ \
  --rpc-external --rpc-cors=all
```

#### Local 3-node network via docker-compose

`docker-compose.yml` reproduces `scripts/start-local3.sh` in containers.
Each service passes `--chain=local3` (EIP-155 `1337`). This is not the public
testnet.

```sh
docker compose up --build           # start local3 (EIP-155 1337)
docker compose down                 # stop, keep chain state
docker compose down -v              # stop and wipe state
```

Then connect Polkadot.js Apps to `ws://localhost:9944` (node1),
`ws://localhost:9945` (node2), or `ws://localhost:9946` (node3).

## Public testnet

`quip-testnet` is the live public testnet ("AGLS" tokens, 12 decimals,
EIP-155 `20033`). Omitting `--chain` selects this preset. The canonical
genesis is baked into the `v0.2.0+` binary and also published as a raw JSON
file at `nodes.quip.network/chain-specs/quip-testnet.json`.

### Quickstart (Docker)

```sh
# Pull the matching release image
docker pull registry.gitlab.com/quip.network/quip-protocol-rs/quip-network-node:v0.2.0

# Live public testnet (EIP-155 20033). Omit --chain for the same preset.
docker run --rm -v quip-data:/data -p 9944:9944 -p 30333:30333 \
  registry.gitlab.com/quip.network/quip-protocol-rs/quip-network-node:v0.2.0 \
  --chain=quip-testnet --base-path=/data \
  --name="my-quip-node"
```

The three canonical bootnodes (`bootnode-{1,2,3}.testnet.quip.network`) are
embedded in the chain spec, so peer discovery happens automatically.

### Using the hosted raw chain spec

Alternatively, fetch the published JSON spec from `nodes.quip.network` and
pass its path to `--chain`. That file is the live public testnet
(EIP-155 `20033`).

```sh
curl -fsSL https://gitlab.com/quip.network/nodes.quip.network/-/raw/main/chain-specs/quip-testnet.json \
    -o quip-testnet.json

# Live public testnet JSON (EIP-155 20033)
docker run --rm -v "$PWD:/spec" -v quip-data:/data -p 9944:9944 -p 30333:30333 \
  registry.gitlab.com/quip.network/quip-protocol-rs/quip-network-node:v0.2.0 \
  --chain=/spec/quip-testnet.json --base-path=/data
```

### Running a validator

Operator validator slots are committed at genesis (see
[`docs/genesis-quip-testnet.md`](docs/genesis-quip-testnet.md)). To rotate or
add a slot, follow [`docs/testnet-keys.md`](docs/testnet-keys.md) and the
`scripts/derive-operator-keys.sh` helper.

---

## File: docs/polkadotjs/README.md

# Polkadot.js Apps Support

Quip uses hybrid consensus keys:

- BABE authority key: `sr25519 + ML-DSA-44` (`H3`)
- GRANDPA authority key: `ed25519 + ML-DSA-44` (`H1`)

These consensus keys are not browser-managed account keys. Polkadot.js Apps can
connect to Quip without custom type overrides, but validator/session key
management still goes through the node keystore rather than browser-generated
consensus keys.

## Current Support Model

Polkadot.js Apps works normally for:

- connect to the Quip node over WebSocket
- inspect storage, events, and extrinsics
- call `author_rotateKeys`
- submit `session.setKeys`

The browser does **not** need to generate hybrid BABE or GRANDPA keys directly.
Session keys should continue to be generated by the node keystore through
`author_rotateKeys`, then registered on-chain with `session.setKeys`.

Browser-side transaction signing now has an initial WASM/signature wrapper in
this repository and is tracked in
[`wasm-signing-plan.md`](./wasm-signing-plan.md).

## Regenerating the signing fixture

`fixtures/hybrid-signing.json` embeds drift-prone runtime values (spec version,
transaction version, extension set), so `cargo test -p quip-protocol-runtime
--test signing_fixture` fails whenever those change. Regenerate it with:

```bash
cargo run -p quip-protocol-runtime --example generate_polkadotjs_signing_fixture -- --write
```

Then re-run the signing-fixture tests and `npm test --prefix js/quip-signer`,
which both consume the fixture.

## Metadata Compatibility

Quip exposes the hybrid BABE and GRANDPA types through metadata-safe wrapper
shapes, so current Polkadot.js Apps builds should not require any manual custom
types just to connect and decode the runtime metadata.

This matters because the hybrid BABE and GRANDPA signatures are larger than the
classical Substrate defaults, and older metadata representations using a single
large `[u8; N]` array can exceed client-side parser limits.

If Apps has cached older metadata from before this fix, reconnecting may still
show stale decode errors. In that case, hard-refresh the page or remove the
saved endpoint and reconnect.

## What This Does Not Add

This does not add browser-side generation or signing support for hybrid BABE or
GRANDPA authority keys. For validator/session key management, the intended flow
remains:

1. `author_rotateKeys`
2. `session.setKeys`

That is the supported path for Quip's hybrid consensus keys in Polkadot.js
Apps.

---

## File: docs/genesis-quip-testnet.md

# quip-testnet Genesis Manifest

Public material for the three bootnode operators pinned into the
`quip-testnet` genesis preset (introduced in v0.2.0). The full BABE / GRANDPA
public bytes live as `include_str!`-loaded hex blobs under
`runtime/src/genesis_quip_testnet/`. This file is the human-readable index.

## Operators

### Operator 1

- **multiaddr**: `/dns4/bootnode-1.testnet.quip.network/tcp/30333/p2p/12D3KooWBdhB4xGX6hfFsNufqQsG99kekiH9kJhLSiui3RgatnpE`
- **peer-id**: `12D3KooWBdhB4xGX6hfFsNufqQsG99kekiH9kJhLSiui3RgatnpE`
- **tx_account_ss58**: `5GZMoWFMoNGLZKT1tduLMQQQC7dBQo4MHkYqriCdDATXqaYi`
- **tx_account_hex**: `0xc6cb8a79a71b11347a7ce0d983104278c0682dc70b7f90be9afd92ab54f1404b`
- **roles (v0.2.0)**: validator session keys, sudo, and faucet dispense
  source — the public testnet faucet in `nodes.quip.network` is configured
  with operator-1's mnemonic. Operator-1's host must therefore safeguard the
  mnemonic with care: any leak compromises validator authority, sudo, and
  faucet funds simultaneously. A future release should split sudo into a
  multisig and the faucet into its own genesis-endowed account.

### Operator 2

- **multiaddr**: `/dns4/bootnode-2.testnet.quip.network/tcp/30333/p2p/12D3KooWPJAHo45AA94u3fYS3tXvyKouZnWihQnXWPHAzikXLfPW`
- **peer-id**: `12D3KooWPJAHo45AA94u3fYS3tXvyKouZnWihQnXWPHAzikXLfPW`
- **tx_account_ss58**: `5FUFx3HLMXCAes5RrGDV2KEPxHwPyDu8LpmarD2iwcqQm48c`
- **tx_account_hex**: `0x96ab60c5a90f6b18566155d2187fae8f52e3cd43627fb4a40d5c89f3a512bb5b`

### Operator 3

- **multiaddr**: `/dns4/bootnode-3.testnet.quip.network/tcp/30333/p2p/12D3KooWM6n7wYvett975UnLYXrvnBGqLk2DLJoCRoFxgXTkptWe`
- **peer-id**: `12D3KooWM6n7wYvett975UnLYXrvnBGqLk2DLJoCRoFxgXTkptWe`
- **tx_account_ss58**: `5HgizfVW1rciPqPafkipfytynovFC3d8N1WFr8ffVF9Gjtte`
- **tx_account_hex**: `0xf8a5d50a6b32c3784b1e9fd9811e57b63524e5ec0defaafc289304bf99061db7`

## Verifying

To independently confirm the genesis preset matches this manifest:

```bash
cargo build --release -p quip-network-node
./target/release/quip-network-node export-chain-spec --chain quip-testnet \
    | jq '.bootNodes, .properties'
```

Expected output: three `/dns4/bootnode-N.testnet.quip.network/.../p2p/12D3KooW…`
multiaddrs (matching the peer-ids above) and `tokenSymbol=AGLS`,
`tokenDecimals=12`, `ss58Format=42`.

To compare the runtime-derived authority public bytes against the hex blobs:

```bash
cargo test -p quip-protocol-runtime --lib genesis_config_presets::tests
```

The `quip_testnet_operator_1_account_is_pinned` test asserts that
`tx_account_from_hex("c6cb8a79…")` round-trips to the same bytes, catching
silent regressions in either the hex parsing or the account-id derivation.

## Updating

To add or rotate an operator, follow [`docs/testnet-keys.md`](testnet-keys.md):
the helper script produces the `public-bundle.txt` and the coordinator
commits the matching `*.hex` files plus a manifest entry here. Bumping the
`quip_testnet` genesis requires re-exporting `quip-testnet.json` and
republishing it in `nodes.quip.network`.

---

## File: docs/quantum-compute-mempool.md

# Quantum Compute Mempool

## Canonical Plain Ising Job Spec

`QuantumComputeMempool` seeds a default plain Ising job spec at genesis so SDKs
can call `propose_job` without first registering a spec.

Canonical tuple:

```text
name = "plain-ising-v1"
formulation = Ising
validation_program = None
transform_program = None
```

The `spec_id` is the runtime hash of the SCALE-encoded tuple:

```text
(name, formulation, validation_program, transform_program)
```

Pinned `spec_id`:  
NOTE: This value is verified by CI (default_ising_spec_id_is_pinned and default_ising_spec_id_matches_pinned_hash);
if any tuple field changes, those tests will fail.

```text
0x8f46f3a31321d1d093314fc769c42cbe7a83d71a0b69e6571a0f68e2a04067f0
```

SDKs may hardcode this value or read the `DefaultIsingSpecId` pallet constant
from metadata. Additional job specs are team-controlled: `register_job_spec`
requires root origin and records an explicit builder account supplied by root.

## Coefficient and Solution Domains

`QuantumComputeMempool` stores plain Ising problems. The on-chain solution
domain is spin space:

```text
s_i in {-1, +1}
```

The pallet does not accept binary solution values such as `0` and `1` for
plain Ising jobs. If an SDK accepts a QUBO or binary-domain model from a user,
the SDK should transform it off-chain before calling `propose_job`, and should
map returned spin solutions back to the user's binary domain if needed:

```text
x_i = (s_i + 1) / 2
s_i = 2x_i - 1
```

Coefficients are fixed-point milli values. `h_values` and `j_values` are
`i32` values where `1000` represents `1.0`. For example:

```text
5.0   -> 5000
0.25  -> 250
-1.5  -> -1500
```

The same milli convention is used for `best_energy_milli`,
`min_energy_milli`, `diversity_milli`, and `min_diversity_milli`. Energy
values are signed (`i64`); diversity values are unsigned (`u32`). Keep any
constant offset introduced by a QUBO-to-Ising transform in the adapter, or
adjust user-facing energy thresholds before submitting the job.

## Accepted Solution Storage

Accepted solver submissions are stored in `OrderSolutions`, a `DoubleMap`
keyed first by `order_id` and second by solver account:

```rust
pub type OrderSolutions<T: Config> = StorageDoubleMap<
    _,
    Blake2_128Concat,
    u64,
    Blake2_128Concat,
    T::AccountId,
    JobSolutionOf<T>,
>;
```

Each stored `JobSolution` contains:

```text
solver
solver_type
solutions
best_energy_milli
diversity_milli
num_valid
submitted_at
```

Because this storage item is exposed through runtime metadata, SDKs can query
it through the standard Substrate storage RPC. No custom RPC is needed unless a
client cannot perform or decode metadata-backed `DoubleMap` prefix queries.

### Query with substrate-interface

Install the Python client:

```bash
pip install substrate-interface
```

Connect to a node over websocket:

```python
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(url="ws://127.0.0.1:9944")
```

Read one solver's accepted submission for an order:

```python
order_id = 0
solver_ss58 = "5..."

solution = substrate.query(
    module="QuantumComputeMempool",
    storage_function="OrderSolutions",
    params=[order_id, solver_ss58],
)

if solution.value is not None:
    print(solution.value["solver"])
    print(solution.value["solutions"])
    print(solution.value["best_energy_milli"])
    print(solution.value["diversity_milli"])
```

Read all accepted submissions for one order by querying the `DoubleMap` with
only the first key:

```python
order_id = 0

rows = substrate.query_map(
    module="QuantumComputeMempool",
    storage_function="OrderSolutions",
    params=[order_id],
    page_size=100,
)

for solver_key, solution in rows:
    print("solver key:", solver_key.value)
    print("stored solver:", solution.value["solver"])
    print("solutions:", solution.value["solutions"])
    print("best energy:", solution.value["best_energy_milli"])
    print("diversity:", solution.value["diversity_milli"])
```

Use `block_hash=` on `query` or `query_map` when an adapter needs a historical
or finalized view instead of the current best block.

## Job Lifecycle Events

`QuantumComputeMempool` emits lifecycle events that SDKs can monitor through
`System.Events`. The most useful events for result retrieval are:

```text
JobProposed
SolutionAccepted
FirstSolutionReceived
BlockWaitStarted
FrontRunnerChanged
OrderExpired
OrderClosed
ResultReady
```

`SolutionAccepted` is emitted when a solver submission is accepted and written
to `OrderSolutions`. `ResultReady` is emitted when settlement produces a final
winner payload for callback delivery modes. Settlement also emits
`RewardClaimed` for each winner payout, and `ResultPurged` is emitted when a
stored poll payload is deleted after its TTL — `CallbackWithPoll` consumers
must fetch results before the purge.

`OrderExpired` is emitted lazily when an extrinsic touches an expired open
order and the pallet updates the status. It is not emitted automatically at the
exact expiry block, so SDKs that need exact deadline handling should also read
`JobOrders` and compute the effective expiry as:

```text
min(created_at + timing.deadline_blocks, first_solution_at + timing.block_wait)
```

Before the first solution arrives, `first_solution_at` is null and the expiry
is just `created_at + timing.deadline_blocks`. Note that `created_at` and
`first_solution_at` are top-level `JobOrder` fields, while `deadline_blocks`
and `block_wait` are nested inside the order's `timing` struct.

### Read events from a block with substrate-interface

```python
EVENTS = {
    "JobProposed",
    "SolutionAccepted",
    "FirstSolutionReceived",
    "BlockWaitStarted",
    "FrontRunnerChanged",
    "OrderExpired",
    "OrderClosed",
    "ResultReady",
}


def mempool_events_at(block_hash=None):
    for event in substrate.get_events(block_hash=block_hash):
        if (
            event.value["module_id"] == "QuantumComputeMempool"
            and event.value["event_id"] in EVENTS
        ):
            yield event


for event in mempool_events_at():
    print(event.value["event_id"], event.value["attributes"])
```

### Subscribe to event storage with substrate-interface

For live best-block monitoring, subscribe to `System.Events` and filter the
decoded event records:

```python
def event_name_from_storage_record(record):
    event = record.get("event", {})
    module = event.get("module_id") or event.get("module")
    name = event.get("event_id") or event.get("name")
    return module, name


def handle_events(events_obj, update_nr, subscription_id):
    if update_nr == 0:
        return None

    for record in events_obj.value:
        module, name = event_name_from_storage_record(record)
        if module == "QuantumComputeMempool" and name in EVENTS:
            print(name, record)

    return None


substrate.query(
    module="System",
    storage_function="Events",
    subscription_handler=handle_events,
)
```

For finalized-only processing, subscribe to finalized or imported block
headers in the adapter process, then call `get_events(block_hash=...)` for each
block hash before acting on the events.

---

## File: docs/quantum-pow-difficulty-convergence-plan.md

# Quantum PoW Difficulty Convergence Plan

## Terminology

A **qblock** (also `qpow_block`) is a chain block won by a quantum PoW
proof — what we previously referred to as "solution #N" or "problem #N".
The timing thresholds in this document are measured in elapsed *chain
blocks* (6-second Substrate blocks) between consecutive qblocks: a "fast
qblock" is one mined fewer than 60 chain blocks after the previous qblock.

## Problem

Current PoW difficulty adjustment can converge into energy ranges that are
effectively unsolvable for the current design. A fast qblock can push
`max_energy_milli` too hard, and recovery then depends on decay sweeps that can
take hours before the threshold becomes mineable again.

The main causes are:

- The runtime energy curve uses `c = 0.700 / 0.750 / 0.800`, making the hard end
  too negative.
- The hardening cutoff is effectively 100 chain blocks, or roughly 600
  seconds at six-second blocks.
- The prior miner-type/QPU dominance easing behavior is no longer represented.
  We cannot reliably use miner type today, but we can detect repeated qblock
  wins by the same account.

## Goals

- Keep difficulty in a theoretically solvable range.
- Restore a roughly 10-minute convergence target after qblocks.
- Reduce difficulty when the same miner account dominates consecutive
  qblocks.
- Avoid changing extrinsic arguments or signed transaction encoding.
- Keep decay interval and proof hardening cutoff separate.

## Proposed Changes

### 1. Recalibrate the Energy Curve

Change runtime PoW curve constants in `runtime/src/configs/mod.rs`:

```rust
pub const QuantumPowCurveCEasyMilli: u32 = 700;
pub const QuantumPowCurveCKneeMilli: u32 = 725;
pub const QuantumPowCurveCHardMilli: u32 = 750;
```

This replaces the current `0.700 / 0.750 / 0.800` curve with
`0.700 / 0.725 / 0.750`.

Expected effect:

- The knee moves back into the intended range.
- The hard edge is still difficult, but no longer pushed into the known
  impossible range.
- Future tuning can move the middle value slightly upward, for example
  `0.728` or `0.730`, if observed chain data supports it.

Update mock/test curve helpers to use the configured `700 / 725 / 750`
constants and the spec-aware `EnergyCurve::new(..., CurveC, allowed_h,
allowed_j)` constructor.

### 2. Separate Hardening Cutoff from Decay Interval

Keep `QuantumPowEpochLength = 100` as the decay interval.

In `pallets/quantum-pow/src/difficulty.rs`, keep the v0.1 block-native
thresholds:

```rust
const FAST_PROOF_BLOCKS: u64 = 60;
const TARGET_PROOF_BLOCKS: u64 = 100;
const SLOW_PROOF_BLOCKS: u64 = 200;
```

Direction follows the v0.1 `compute_next_block_requirements` policy:

- A qblock mined before 60 chain blocks (360s) always hardens, even for a
  dominant winner.
- A qblock at or after 60 chain blocks hardens gently (graduated 35%→5%
  band) unless the winner is dominant (see Section 3), in which case it
  eases.

`TARGET_PROOF_BLOCKS = 100` no longer decides direction — only the rate
bands: hardening interpolates 35%±30% → 5%±4% across 60–100 chain blocks,
easing interpolates 2.5%±2% → 15%±14% across 100–200 chain blocks, exactly
as v0.1 did across 360–600s and 600–1200s. The decay interval
(`EpochLength = 100`) remains a separate concept.

The 100-block value of `TARGET_PROOF_BLOCKS` is deliberately co-located
with `QuantumPowEpochLength = 100`: the first decay step, the hardening
band's gentle plateau, and the easing rate ramp all begin at the same
100-chain-block (600s) boundary. This yields three clean qblock cases:

1. **Fast qblock (< 60 chain blocks):** always hardens at 35%±30%,
   dominant or not.
2. **Regular qblock (60–99 chain blocks):** sub-epoch, so no decay has
   occurred; the adjustment starts from the stored difficulty. Streaks
   decide direction — a dominant winner eases, anyone else hardens on the
   graduated 35%→5% band.
3. **Slow qblock (≥ 100 chain blocks):** decay has already eased the
   stored difficulty by `elapsed / EpochLength` steps; the adjustment
   starts from that decay-eased base. Streaks decide direction, and
   hardening sits on the gentle 5%±4% plateau — so a long round can end
   easier overall even though the qblock itself hardened.

`TARGET_PROOF_BLOCKS` and `QuantumPowEpochLength` stay separate constants
(rate-band anchor vs decay cadence). Retuning the epoch length does not
move the rate bands — change both together or the three-case model above
stops holding.

### 3. Add Dominant-Winner Easing

Add storage to track consecutive qblocks won by the same miner account. A
simple shape is enough:

```rust
pub type WinnerStreak<T: Config> = StorageValue<
    _,
    types::WinnerStreak<T::AccountId>,
    OptionQuery,
>;
```

Add a type in `pallets/quantum-pow/src/types.rs`:

```rust
pub struct WinnerStreak<AccountId> {
    pub miner: AccountId,
    pub count: u32,
}
```

Add a runtime config constant:

```rust
type ConsecutiveWinnerEasingThreshold: Get;
```

Recommended runtime value:

```rust
pub const QuantumPowConsecutiveWinnerEasingThreshold: u32 = 3;
```

Policy:

- If the current qblock winner is the same account as the stored streak
  miner, increment the streak count.
- If the current winner differs, reset the streak to `{ miner, count: 1 }`.
- A winner with streak count at or above the threshold is *dominant*: its
  qblocks at or past 60 chain blocks ease difficulty instead of hardening.
- Fast qblocks (under 60 chain blocks) always harden, dominant or not —
  matching v0.1, where the under-360s harden rule took precedence over
  repeat-winner easing.
- Non-dominant slow qblocks harden gently. v0.1 eased any repeat winner (a
  streak of 2, keyed on miner type); we instead require the configured
  threshold (3) by account, so a winner must demonstrate sustained dominance
  before difficulty pressure reverses.
- A threshold of `0` disables dominant-winner easing.

This restores the spirit of miner-type/QPU awareness without adding node
descriptors or changing miner registration.

### 4. Clamp Existing Impossible Difficulty on Runtime Upgrade

`pallet_quantum_pow` does not currently have a storage version. Add one and
wire an `on_runtime_upgrade`.

Migration behavior:

- Read `DefaultTopology`.
- Read the matching registered topology.
- Build the new curve from the recalibrated constants.
- If `Difficulty.max_energy_milli < curve.min_milli`, clamp it to
  `curve.knee_milli`.
- Preserve `min_solutions` and `min_diversity_milli`.
- If no default topology is registered, no-op.

Rationale:

- Changing constants fixes future adjustment, but existing chain state may
  already hold an impossible threshold.
- Clamping only out-of-range values avoids disturbing healthy deployments.

### 5. Runtime Versioning

Bump `spec_version` in `runtime/src/lib.rs`.

Do not bump `transaction_version` unless extrinsic arguments change. This plan
does not require any extrinsic encoding changes.

## Tests

Add or update tests for:

- New curve constants: `700 / 725 / 750`.
- Curve ordering remains `min_milli < knee_milli < max_milli`.
- Fast qblock before 60 chain blocks hardens, including for a dominant
  winner.
- Slow qblock at or after 60 chain blocks hardens gently for a non-dominant
  winner (including a different winner — the restored v0.1 rule).
- Consecutive same-miner slow qblocks at the threshold ease instead of
  harden.
- Winner streak resets when a different miner wins a qblock.
- Migration clamps an out-of-range `Difficulty.max_energy_milli` to the new
  knee.
- Migration leaves in-range difficulty unchanged.
- Existing decay and mining snapshot tests use the recalibrated curve.

## Non-Goals

- Do not add miner type or node descriptor metadata in this changeset.
- Do not change proof submission extrinsic arguments.
- Do not change `QuantumPowEpochLength` unless separate chain data shows decay
  cadence itself needs tuning.
- Do not change `min_solutions` or `min_diversity_milli` as part of automatic
  adjustment.

---

## File: docs/release.md

# Release Checklist

## Pre-tag verification

- [ ] All target-version commits merged to `main` via MR
- [ ] `cargo check --workspace --all-targets` clean
- [ ] `cargo clippy --workspace --all-targets -- -D warnings` clean
- [ ] `cargo test --workspace` passes
- [ ] Node image builds locally: `docker build -t quip-network-node:rc .`
- [ ] Sidecar Dockerfile passes its static build check:
      `docker build --check -f docker/revive-eth-rpc.Dockerfile .`
- [ ] `./target/release/quip-network-node --version` reports the target version
- [ ] `./target/release/quip-network-node export-chain-spec --chain quip-testnet --raw > /tmp/quip-testnet.raw.json` succeeds
- [ ] Companion `nodes.quip.network` MR with the matching
      `chain-specs/quip-testnet.json` (sha256 from the above raw export) is
      merged

## Tag

```bash
git checkout <release-branch>   # main or the active series branch (e.g. v0.2) —
git pull                        # this choice decides the floating tag (see below)
git tag -a v<MAJOR>.<MINOR>. -m "v<MAJOR>.<MINOR>.: <one-line summary>"
git push origin v<MAJOR>.<MINOR>.
```

The GitLab CI pipeline at `.gitlab-ci.yml` picks up the tag via the
`$CI_COMMIT_TAG` rule and publishes
`registry.gitlab.com/quip.network/quip-protocol-rs/quip-network-node:v<MAJOR>.<MINOR>.`
and
`registry.gitlab.com/quip.network/quip-protocol-rs/quip-network-evm-sidecar:v<MAJOR>.<MINOR>.`.

### Version-tag format (shared standard)

Pre-release tags use **SemVer hyphenated** pre-releases —
`v<MAJOR>.<MINOR>.-rcN` (e.g. `v0.2.1-rc18`), **never** the PEP 440
no-hyphen form `v0.2.1rc18`. This is the cross-repo standard so `quip-node-manager`
(and any SemVer consumer) can order release candidates correctly; see
`quip-protocol/docs/VERSIONING.md` for the full rationale.

Both container images publish on release tags **only** — branch pushes never
build or push images. Their floating tags follow the branch the tag was cut from,
resolved by the `resolve-floating-tag` CI job via commit ancestry (tag
pipelines carry no branch variable): a tag on `v0.2` publishes `:<tag>` +
`:v0.2`, a tag on `main` publishes `:<tag>` + `:latest`, both plus
`:sha-<short-sha>`. A tag reachable from neither branch gets only `:<tag>` +
`:sha-<short-sha>`.

## Post-tag verification

- [ ] CI pipeline on the tag completes green (`glab ci status --live`)
- [ ] Both images are present:

  ```bash
  docker pull registry.gitlab.com/quip.network/quip-protocol-rs/quip-network-node:v<MAJOR>.<MINOR>.
  docker pull registry.gitlab.com/quip.network/quip-protocol-rs/quip-network-evm-sidecar:v<MAJOR>.<MINOR>.
  ```

- [ ] Sidecar image starts and reports its CLI help:

  ```bash
  docker run --rm \
    registry.gitlab.com/quip.network/quip-protocol-rs/quip-network-evm-sidecar:v<MAJOR>.<MINOR>. \
    --help
  ```

- [ ] Smoke test against the published spec:

  ```bash
  curl -fsSL https://gitlab.com/quip.network/nodes.quip.network/-/raw/main/chain-specs/quip-testnet.json \
      -o /tmp/quip-testnet.json
  docker run --rm -v /tmp:/spec \
      registry.gitlab.com/quip.network/quip-protocol-rs/quip-network-node:v<MAJOR>.<MINOR>. \
      --chain=/spec/quip-testnet.json --tmp --name v-smoke --no-mdns
  ```

  Expect peer discovery against at least one of the three canonical bootnodes
  within 60 seconds.

## What v0.2.0 ships

- First semver tag for the validator image; previously only `:latest` and
  `:sha-<short>` were published.
- Built-in `quip-testnet` chain spec preset with three operator-controlled
  bootnodes and a `ChainType::Live` genesis.
- Helper script (`scripts/derive-operator-keys.sh`) and example
  (`crates/transaction-crypto/examples/derive_genesis_keys.rs`) for
  reproducing operator key generation end-to-end.
- macOS-only `.cargo/config.toml` rpath fix so `cargo build` works on a
  fresh Xcode install without `LIBCLANG_PATH` exports.

Runtime `spec_version` remains at `101`; v0.2.0 is packaging plus the named
testnet identity, not a runtime upgrade.

---

## File: docs/rust-setup.md

# Installation

This guide is for reference only, please check the latest information on getting started with Substrate [here](https://docs.substrate.io/main-docs/install/).

This page will guide you through the **2 steps** needed to prepare a computer for **Substrate** development. Since
Substrate is built with [the Rust programming language](https://www.rust-lang.org/), the first thing you will need to do
is prepare the computer for Rust development - these steps will vary based on the computer's operating system. Once Rust
is configured, you will use its toolchains to interact with Rust projects; the commands for Rust's toolchains will be
the same for all supported, Unix-based operating systems.

## Build dependencies

Substrate development is easiest on Unix-based operating systems like macOS or Linux. The examples in the [Substrate
Docs](https://docs.substrate.io) use Unix-style terminals to demonstrate how to interact with Substrate from the command
line.

### Ubuntu/Debian

Use a terminal shell to execute the following commands:

```bash
sudo apt update
# May prompt for location information
sudo apt install -y git clang curl libssl-dev llvm libudev-dev
```

### Arch Linux

Run these commands from a terminal:

```bash
pacman -Syu --needed --noconfirm curl git clang
```

### Fedora

Run these commands from a terminal:

```bash
sudo dnf update
sudo dnf install clang curl git openssl-devel
```

### OpenSUSE

Run these commands from a terminal:

```bash
sudo zypper install clang curl git openssl-devel llvm-devel libudev-devel
```

### macOS

> **Apple M1 ARM** If you have an Apple M1 ARM system on a chip, make sure that you have Apple Rosetta 2 installed
> through `softwareupdate --install-rosetta`. This is only needed to run the `protoc` tool during the build. The build
> itself and the target binaries would remain native.

Open the Terminal application and execute the following commands:

```bash
# Install Homebrew if necessary https://brew.sh/
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

# Make sure Homebrew is up-to-date, install openssl
brew update
brew install openssl
```

### Windows

**_PLEASE NOTE:_** Native Windows development of Substrate is _not_ very well supported! It is _highly_
recommended to use [Windows Subsystem Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10)
(WSL) and follow the instructions for [Ubuntu/Debian](#ubuntudebian).
Please refer to the separate
[guide for native Windows development](https://docs.substrate.io/main-docs/install/windows/).

## Rust developer environment

This guide uses <https://rustup.rs> installer and the `rustup` tool to manage the Rust toolchain. First install and
configure `rustup`:

```bash
# Install
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Configure
source ~/.cargo/env
```

Configure the Rust toolchain to default to the latest stable version, add nightly and the nightly wasm target:

```bash
rustup default stable
rustup update
rustup update nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
```

## Test your set-up

Now the best way to ensure that you have successfully prepared a computer for Substrate development is to follow the
steps in [our first Substrate tutorial](https://docs.substrate.io/tutorials/v3/create-your-first-substrate-chain/).

## Troubleshooting Substrate builds

Sometimes you can't get the Substrate node template to compile out of the box. Here are some tips to help you work
through that.

### Rust configuration check

To see what Rust toolchain you are presently using, run:

```bash
rustup show
```

This will show something like this (Ubuntu example) output:

```text
Default host: x86_64-unknown-linux-gnu
rustup home:  /home/user/.rustup

installed toolchains
--------------------

stable-x86_64-unknown-linux-gnu (default)
nightly-2020-10-06-x86_64-unknown-linux-gnu
nightly-x86_64-unknown-linux-gnu

installed targets for active toolchain
--------------------------------------

wasm32-unknown-unknown
x86_64-unknown-linux-gnu

active toolchain
----------------

stable-x86_64-unknown-linux-gnu (default)
rustc 1.50.0 (cb75ad5db 2021-02-10)
```

As you can see above, the default toolchain is stable, and the `nightly-x86_64-unknown-linux-gnu` toolchain as well as
its `wasm32-unknown-unknown` target is installed. You also see that `nightly-2020-10-06-x86_64-unknown-linux-gnu` is
installed, but is not used unless explicitly defined as illustrated in the [specify your nightly
version](#specifying-nightly-version) section.

### WebAssembly compilation

Substrate uses [WebAssembly](https://webassembly.org) (Wasm) to produce portable blockchain runtimes. You will need to
configure your Rust compiler to use [`nightly` builds](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html) to
allow you to compile Substrate runtime code to the Wasm target.

> There are upstream issues in Rust that need to be resolved before all of Substrate can use the stable Rust toolchain.
> [This is our tracking issue](https://github.com/paritytech/substrate/issues/1252) if you're curious as to why and how
> this will be resolved.

#### Latest nightly for Substrate `master`

Developers who are building Substrate _itself_ should always use the latest bug-free versions of Rust stable and
nightly. This is because the Substrate codebase follows the tip of Rust nightly, which means that changes in Substrate
often depend on upstream changes in the Rust nightly compiler. To ensure your Rust compiler is always up to date, you
should run:

```bash
rustup update
rustup update nightly
rustup target add wasm32-unknown-unknown --toolchain nightly
```

> NOTE: It may be necessary to occasionally rerun `rustup update` if a change in the upstream Substrate codebase depends
> on a new feature of the Rust compiler. When you do this, both your nightly and stable toolchains will be pulled to the
> most recent release, and for nightly, it is generally _not_ expected to compile WASM without error (although it very
> often does). Be sure to [specify your nightly version](#specifying-nightly-version) if you get WASM build errors from
> `rustup` and [downgrade nightly as needed](#downgrading-rust-nightly).

#### Rust nightly toolchain

If you want to guarantee that your build works on your computer as you update Rust and other dependencies, you should
use a specific Rust nightly version that is known to be compatible with the version of Substrate they are using; this
version will vary from project to project and different projects may use different mechanisms to communicate this
version to developers. For instance, the Polkadot client specifies this information in its [release
notes](https://github.com/paritytech/polkadot-sdk/releases).

```bash
# Specify the specific nightly toolchain in the date below:
rustup install nightly-<yyyy-MM-dd>
```

#### Wasm toolchain

Now, configure the nightly version to work with the Wasm compilation target:

```bash
rustup target add wasm32-unknown-unknown --toolchain nightly-<yyyy-MM-dd>
```

### Specifying nightly version

Use the `WASM_BUILD_TOOLCHAIN` environment variable to specify the Rust nightly version a Substrate project should use
for Wasm compilation:

```bash
WASM_BUILD_TOOLCHAIN=nightly-<yyyy-MM-dd> cargo build --release
```

> Note that this only builds _the runtime_ with the specified nightly. The rest of project will be compiled with **your
> default toolchain**, i.e. the latest installed stable toolchain.

### Downgrading Rust nightly

If your computer is configured to use the latest Rust nightly and you would like to downgrade to a specific nightly
version, follow these steps:

```bash
rustup uninstall nightly
rustup install nightly-<yyyy-MM-dd>
rustup target add wasm32-unknown-unknown --toolchain nightly-<yyyy-MM-dd>
```

---

## File: docs/testnet-keys.md

# Testnet Key Generation (Operator Procedure)

This document is the canonical procedure for generating the keys that pin a
new `quip-testnet` bootnode operator into the genesis preset. Use it when
rotating an operator or onboarding additional slots beyond the initial three
shipped in v0.2.0.

> **Secret material never leaves the operator host.** The mnemonic and the
> libp2p `nodekey` file are the only secrets in the process. Only the
> `public-bundle.txt` block is submitted back to the release coordinator.

## Prerequisites

1. Clone `quip-protocol-rs` at the release-candidate tag.
2. Build the node binary:

   ```bash
   cargo build --release -p quip-network-node
   ```

   On macOS, `.cargo/config.toml` already wires the rpath needed to resolve
   `libclang.dylib` (required by the bindgen-driven `librocksdb-sys` build
   script). No manual `LIBCLANG_PATH` export is needed.

## Per-operator key generation

For each operator slot, run the helper script with the slot index (and
optionally the bootnode hostname). The script wraps the full procedure:

```bash
scripts/derive-operator-keys.sh 1
scripts/derive-operator-keys.sh 2 bootnode-2.testnet.quip.network
scripts/derive-operator-keys.sh 3
```

Each run produces, under `quip-testnet-keys/operator-<N>/`:

| File | Permissions | Purpose |
| --- | --- | --- |
| `mnemonic` | `0600` | BIP39 master secret (back up, never share) |
| `nodekey` | `0600` | libp2p secret bytes; mount via `--node-key-file` on the bootnode host |
| `public-bundle.txt` | `0644` | Public peer-id, multiaddr, BABE/GRANDPA/TX public bytes |

The script auto-creates a `.gitignore` in `quip-testnet-keys/` containing `*`
so secrets cannot accidentally be `git add`'d. The repo's `.gitignore` also
excludes the entire directory.

## Submission

Send the contents of `public-bundle.txt` for each operator to the release
coordinator over a secure channel (signed message in the ops chat, encrypted
mail, etc.). The bundle is:

```
operator: <N>
hostname: <dns>
multiaddr: /dns4/<dns>/tcp/30333/p2p/
peer_id: 12D3KooW…
babe_pub: 0x…             # 1344 bytes hex (sr25519 + ML-DSA-44)
grandpa_pub: 0x…          # 1344 bytes hex (ed25519 + ML-DSA-44)
tx_account_ss58: 5…
tx_account_hex: 0x…       # 32 bytes
```

The coordinator commits the public bytes into
`runtime/src/genesis_quip_testnet/` (BABE/GRANDPA, one hex file per pubkey)
and into the inline `tx_account_from_hex(...)` calls in
`runtime/src/genesis_config_presets.rs::quip_testnet_config_genesis`.

## Manual derivation

Should the helper script be unavailable, the equivalent commands are:

```bash
# 1. Mnemonic
./target/release/quip-network-node key generate
#    → copy the "Secret phrase:" line

# 2. libp2p node-key file
./target/release/quip-network-node key generate-node-key --file ./nodekey
chmod 600 ./nodekey
./target/release/quip-network-node key inspect-node-key --file ./nodekey
#    → prints the peer-id

# 3. Hybrid BABE/GRANDPA/TX public material
cd crates/transaction-crypto
cargo run --release --example derive_genesis_keys --features std -- "<mnemonic>"
```

The example binary lives at
`crates/transaction-crypto/examples/derive_genesis_keys.rs` and produces the
same `babe_pub` / `grandpa_pub` / `tx_account_*` values regardless of whether
it is invoked directly or via the script.

---

## File: env-setup/README.md

# Env setup

Special files for setting up an environment to work with the template:

- `rust-toolchain.toml` when working with `rustup`.
- `flake.nix` when working with `nix`.

These files will be copied by the installer script to the main directory. They are
put into this special directory to not interfere with the normal CI.

--- METRICS ---
- Files Extracted: 10
- Estimated Token Budget: ~16901 tokens
- Recency Window: Active (< 180 days)
- Canonical Reference: https://codewiki.google/github.com/QuipNetwork/quip-protocol-rs
