# Start Here

A reference document for the Joystream Network.

## Introduction

This is the Joystream Handbook, it is intended as a reference document which describes the way the system functions from a technical, economic and social perspective, and also links to various community resources. For detailed information or tutorials on how specific software and applications work, please consult the relevant [source repository](http://github.com/joystream/) for the project:&#x20;

## Lightpaper

This handbook is quite technical and contains a substantial level of detail, hence for anyone attempting to gain a more integrated high level overview of the full project and system, then the [lightpaper](http://www.joystream.org/lightpaper.pdf) is more appropriate.

<figure><img src="/files/eSd6ceaIStovH7BxTwNG" alt=""><figcaption><p>Joystream Lightpaper</p></figcaption></figure>

## Opportunities

There are a variety of ways to earn $JOY, summarized in the table below:

| Activity              | Reference                                                           |
| --------------------- | ------------------------------------------------------------------- |
| Council Member        | [Council](/system/council#council-member)                           |
| Content Curator       | [Content Directory](/system/content-directory#content-curator)      |
| Content Curator Lead  | [Content Directory](/system/content-directory#content-curator-lead) |
| Builder               | [Builders](/system/builders#builder)                                |
| Builders Lead         | [Builders](/system/builders#builder-lead)                           |
| Human Resources       | [Human Resources](/system/human-resources#hr-representative)        |
| Human Resources Lead  | [Human Resources](/system/human-resources#hr-lead)                  |
| Marketer              | [Marketers](/system/marketers#marketer)                             |
| Marketer Lead         | [Marketers](/system/marketers#marketer-lead)                        |
| Storage Provider      | [Storage & Bandwidth](/system/storage#storage-provider)             |
| Storage Provider Lead | [Storage & Bandwidth](/system/storage#storage-provider-leader)      |
| Distributor           | [Storage & Bandwidth](/system/storage#distributor)                  |
| Distributor Lead      | [Storage & Bandwidth](/system/storage#distributor-lead)             |
| Validator             | [Validation](/system/validation#validator)                          |
| Bounty                | [Bounties](/system/bounties)                                        |

## Community Notion

The community maintains a distinct Notion workspace for ongoing operational and coordination activities, it is a great complement to this more static handbook.

{% embed url="<https://joystream.notion.site/Joystream-Workspace-1175fcb1cc644fdb874558181fd2dbee>" %}

## Feeling Lost?

Are you still totally lost, not sure where to go or whom to speak to about getting started? Go [speak to our integrators](https://discord.com/channels/811216481340751934/813361923172335648), these are the friendliest folks you can imagine, standing by 24/7 - rewarded by the DAO, to help you find your place and become valuable and effective contributor to the system.


# $JOY

Our mainnet asset.

## Introduction

This is the native asset of the Joystream mainnet blockchain.

## Ticker

$JOY

## Supply

The initial total supply is 1,000,000,000 $JOY, however there is no supply cap.

## Hapi Units

The base unit is called *Hapi*, equivalent to Satoshis in Bitcoin. There are ten billion (10,000,000,000) Hapi in each $JOY, and hence at genesis there will be 1,000,000,000x10,000,000,000=10^19 Hapi.

## Denominations&#x20;

| Unit |     $JOY     | Hapi           |
| ---- | :----------: | -------------- |
| Hapi | 0.0000000001 | 1              |
| $JOY |       1      | 10,000,000,000 |

## Decimals

$JOY has 10 decimals for display and input purposes in UIs.

## Inflation

There are two sources of inflation, staking rewards for validators and nominators, and the council minting new tokens out of it's [Council](/system/council#budget), however both are dynamic and constrained.

## Genesis Block

The Joystream blockchain launched December 9th 2022, so all vesting durations start from this time.

| Purpose                    |  Genesis %  | Genesis Liquidity | Vesting Duration |
| -------------------------- | :---------: | :---------------: | :--------------: |
| Community Founding Members | 21.2189609% |         8%        |     24 months    |
| Jsgenesis Founding Members |   31.435%   |         8%        |     24 months    |
| Investors                  | 32.3285352% |        79%        |     12 months    |
| Membership Airdrop         |   0.21735%  |         8%        |     24 months    |
| Strategic Partners         |  3.0013001% |        100%       |     0 months     |
| Reserved 1                 | 11.7988418% |         0         |     12 months    |
| Reserved 2                 |  0.000012%  |         8         |     24 months    |

## Release Schedule

Many of the tokens in the genesis block were subject to limits on transferability, and the schedule below shows how the share of tokens in the genesis block that can be transferred increases over time. This schedule can easily be derived from the prior section on the genesis block, but it is explicitly included here for convenience.

<figure><img src="/files/8aF8R2B7YHEMXuj0CAHB" alt=""><figcaption></figcaption></figure>

Here is an embed of the source calculation.

{% embed url="<https://docs.google.com/spreadsheets/d/1i3ZZydgSuyGVVYmYGWikjzNd5_VtSIhOYgkVQxBhdJ4/edit#gid=1643597195>" fullWidth="false" %}


# Founding Members

The people who were here before the show got started.

## Introduction

![Handcrafted Founding Member avatars](/files/uBpKInyxykPYnkQ6KunW)

Joystream was under development for a very long time, with over 13 incentivsed testnets from 2019 to 2022. A key goal of that process was not only to mature the technology, tools and products, but also to attract, train and incentivse the community members who would direct and operate the DAO post-launch. The founding member program was a community development initiative devised to reach this goal, and through it we were able to build a highly aligne, well trained and passionate community of people who both could work together, had a shared mission and had the knowledge to be effective. You can read the history of the program below.

The basic idea of the program was to allow non-US community members to earn $JOY allocations through participating in the testnet across a variety of roles and activities. After reaching a certain level of involvement, they would be granted the status of a *founding member*, which with it also entailed receiving a hand crafted on-brand avatar and immutable recognition in the blockchain as being among an elite initial set of participants. **Importantly, the founding member status was later taken to include anyone who has worked directly in association with Jsgenesis, and depending on the context the term&#x20;*****Founding Member*****&#x20;may or may not include such Jsgenesis persons, which may be a bit confusing.**

## Founding Members

<https://github.com/Joystream/founding-members/blob/main/inducted/README.md>

## History

* **`11/12/2018`**
  * The Joystream project was launched by Jsgenesis ([Blog post](https://blog.joystream.org/jsgenesis/)), JSG was created from inception to focus on the coding and system elements of the platform, while the running of the platform was entrusted to users under the motto [*"We are building Joystream to set it free"*](https://www.jsgenesis.com)
* **`21/12/2018-19/05/2020`**
  * Various testnets were launched over this time period (`Mesopotamia`, `Sparta`, `Athens`, `Acropolis`, `Rome`).
  * All incentives were directly paid in USD (XMR) for work performed on the network. This meant that none of the balances on the testnet were of much consequence.
* **`20/05/2020`**
  * `Constantinople` testnet was launched ([Blog post](https://blog.joystream.org/constantinople-released/)). This for the first time created the concept of `tJOY` and a `fiat pool` allowing for testnet participants to be directly incentivized by tokens on the testnet and have their incentives be impacted by the DAO and community's decisions. The network was initially backed by a pool of `$2500 USD` that was managed by Jsgenesis ([Blog Post](https://blog.joystream.org/constantinople-incentives/)).
    * As of early 2022, the `fiat pool` had increased from the intial `$2500 USD` to over `$65,000 USD`, paying out more than `$93,000 USD` in incentives to users.
  * The `tJOY` balances were exchangeable into `USD` by exchanging them for `Monero` and eventually `Bitcoin Cash`.
  * This release also introduced `KPIs` which were a set of goals for each council to work toward to gain more incentives ([First KPI blog post](https://blog.joystream.org/constantinople-kpis/)).
    * As of early 2022, the potential weekly `KPI` rewards had increased from `$200 USD` to as much as `$4800 USD`.
* **`14/02/2021`**
  * [The `Founding Members` program was launched](https://blog.joystream.org/founding-member-program/). This program offered mainnet tokens to platform participants for the first time, in addition to the more immediate financial incentives available via the incentivized testnet. It was intended for this program to run up until mainnet launch of the project, however in early 2022 it was discontinued in favor of the new system.
    * Participating users were required to produce a document outlining their activity on the testnet which would be graded and given points, and unlike many programs within cryptocurrency projects, only gave actual allocations to those inducted as `Founding Members` (selected by JSG based upon the quality of their participation and contributions over time). You can access [notes from each scoring period](https://github.com/Joystream/founding-members/tree/main/scoring-periods) as well as a list of [`Founding Members`](https://github.com/Joystream/founding-members/tree/main/inducted).
    * Up until this point, no mainnet tokens were offered to users. Regardless of this, a few users had participated heavily beyond the immediate `USD` incentives by producing bots, scripts, documents, driving activity and growth as well contributing feedback, ideas and improvements to the platform. Jsgenesis selected 5 of the most valuable contributors as [`Initial Founding Members`](https://github.com/Joystream/founding-members/blob/main/scoring-periods/1.md) and gave them a generous allocation of mainnet tokens. This was explained further [here](https://github.com/Joystream/founding-members/tree/main/inducted#note-on-initial-founding-members).
    * None of the tokens given to community participants had any value attached. Jsgenesis had recieved investments from various firms however did not disclose any information regarding any valuation of the project.
* **`13/01/2022`**
  * Jsgenesis [announced](https://blog.joystream.org/important-updates-to-incentives-scheme/) the discontinuation of the the `points` system for the Founding Members program.
    * All points were converted into token allocations and given a potential USD value based on the valuation of the project. A deadline of `26/01/2022` was also given for any final reports to be submitted. The `KPI` system was also going to be discontinued upon launch of the new incentives system.
    * The valuation of the project was made public for the first time giving users an opportunity to see for the first time the potential financial value of their contributions so far as well as the potential future rewards available by participating further.
    * Users who were not inducted as `Founding Members` also had all of their points converted into potential token allocations, however they would still be required to contribute more to obtain `FM` status from Jsgenesis.
* **`26/01/2022`**
  * The old `Founding Member` program officially came to an end.
  * With `Olympia` (the largest testnet release to date) and a new `Founding Member` program launching, it would also involve wiping some amount of content from the testnet.
    * Prior to this upgrade, the testnet, incentives, `FM` program, `KPI` system had come together around a nascent, dedicated community and managed to produce a truly staggering amount of activity--including more than 1,400+ on-chain proposals, 3,400+ on-chain memberships, 11,000+ on-chain forum posts, 90+ on-chain forum threads, on-chain elections involving more than `$16,000 USD` worth of stake, more than 85 workers concurrently working on-chain and being paid automatically by a DAO maintained by more than 50 validators. While also sustaining a storage and distribution network serving more than 5,500 video uploads managed by the DAO.


# JIPs

A Social Process for Improving the Joystream Network

## Introduction

The Joystream Improvement Proposal (JIP) process concerns it self with how to document, propose and update standards in the Joystream Network. By standards one intends to refer very broadly to all protocols, formats, processes or policies involved in coordinating activity among participants in the Joystream project.&#x20;

The JIP process is heavily inspired by the Bitcoin Improvement Proposal ([BIP](https://github.com/bitcoin/bips)) and Ethereum Improvement Proposal ([EIP](https://eips.ethereum.org/)) processes. The JIP process attempts to improve upon these earlier approaches by taking advantage of the inherent governance, [binding upgrades](https://docs.substrate.io/build/upgrade-the-runtime/), accountability and publishing capabilities of the Joystream blockchain and DAO. The aspiration is that this will help generated greater transparency and legitimacy for how changes are made, contributing to the pace of innovation and preservation of network effect by avoiding some of the prior challenges around [highly subjective rules](https://github.com/bitcoin/bips/blob/master/bip-0002.mediawiki#rationale), [arbitrary](https://eips.ethereum.org/EIPS/eip-1#eip-editors) [authorities](https://github.com/bitcoin/bips/blob/master/bip-0001.mediawiki#bip-editors) and [arbitrary venues](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1.md#core-eips).&#x20;

## Proposals

A Joystream Improvement Proposal (JIP) is a well specified and scope constrained initiative, or proposal, for a new standard that in some way improves the Joystream Network. Such proposals are managed through a process described herein, called the *JIP process*, and one of the important activities in this process is the preparation and maintenance of a *JIP document* which encompasses the substance and status of the proposal.

## Repository

The public workspace for JIPs is the canonical Git forge, identified by the most recent JIP parameter signal to that effect. The JIP editors will have write access to this repository, and they are also identified by the most recent JIP parameter signal to that effect. It can be found here

{% embed url="<https://github.com/Joystream/jip>" %}

## Hosted Document Portal

The easiest way to review and consume current JIPs is to either build your own document portfal from the canonical repository, or to visit one of the hosted versions, such as this one:

`` `TODO add link later` ``

## Proposal Process

The life-cycle of an individual proposal is shown in the state machine diagram below.

<figure><img src="/files/uOtcAv3kjcIjQrCtoWHh" alt=""><figcaption><p>JIP proposal life-cycle</p></figcaption></figure>

##


# Wallets

This page explains the various wallet options available for Joystream and how to use them.

Joystream is an independent Layer 1 blockchain and although it uses the same framework as Polkadot called Substrate, it is not a Polkadot Parachain and you cannot send Joystream tokens to Polkadot addresses--please be careful to not send Joystream's token to exchanges or wallets that do not support Joystream

You can use any of the below wallets to connect to Polkadot Vault which will allow you to use a dedicated smartphone as an airgapped signer. It is highly recommended to store your assets using this as it is the most secure option available. Nova

<table><thead><tr><th>Wallet</th><th width="199.33333333333331">Description</th><th>Notes</th></tr></thead><tbody><tr><td><a href="https://www.talisman.xyz">Talisman</a></td><td><em>Keep your assets safe, manage your portfolio and explore Polkadot and Ethereum apps with Talisman</em></td><td><ul><li>Browser Extension available</li><li>Supports Polkadot Vault</li><li>Dashboard where you can see your balances and other information</li></ul></td></tr><tr><td><a href="https://novawallet.io">Nova</a></td><td>Next gen wallet for Dotsama ecosystem.</td><td><ul><li>Supports hardware wallets</li><li>65+ networks supported</li></ul></td></tr><tr><td><a href="https://www.subwallet.app">Subwallet</a></td><td><em>Comprehensive Polkadot,Substrate &#x26; Ethereum wallet</em></td><td><ul><li>Browser &#x26; Mobile wallets available</li><li>Supports Polkadot Vault</li><li>Dashboard where you can see your balances and other information</li></ul></td></tr><tr><td><a href="https://onekey.so">OneKey</a></td><td>Open source crypto wallet. Trusted by millions.</td><td><ul><li>Hardware and software wallet</li></ul></td></tr><tr><td><a href="https://signer.parity.io">Polkadot Vault</a></td><td><em>Turn your extra phone, tablet, or any other iOS or Android device into a hardware wallet.</em></td><td></td></tr></tbody></table>

## Polkadot Vault Tutorial

Joystream supports Polkadot Vault which enables users to utilize almost any smartphone or table to store their accounts in a secure and convenient way. The smartphone used should, once setup, never connect to the internet again and be dedicated for this purpose. QR codes are used to ensure the device remains airgapped.

**Requirements:**

* [A wallet that supports Joystream](/wallets) (such as Subwallet, Talisman or Polkadot-js Extension)
* A dedicated smartphone (which can be an older model) to store the accounts. Note that the camera quality of the device needs to be capable of scanning a quite complex QR code, so the device should preferably not be too old.
* A webcam on your computer

#### Basic setup

1. Set up Polkadot Vault
   1. Install the [Polkadot Vault](https://signer.parity.io/) app on your phone.
   2. Disconnect your phone from the internet forever. Best to remove any SIM cards, forget any WiFi network, enable airplane mode, etc.
2. Add Joystream network to Polkadot Vault
   1. On your computer, open the [Metadata Portal](https://metadata.novasama.io/?tab=1#/joystream-node) that will allow you to add Joystream as a network in Vault.
   2. Select Joystream network on the left menu
   3. Now select “Chain Specs” tab.\
      ![](/files/oCVqdJjHr5m34CWYMKBb)
   4. In the Polkadot Vault smartphone app, press `scanner` at the bottom of the screen and scan the chain spec QR from Metadata Portal.\
      ![](/files/kztwrgTiDOmyNwueW6d8)
   5. Now approve the Joystream network\
      ![](/files/PKFEPq0lL78roq4vAOoo)
   6. Now press `Add Network Metadata`\
      ![](/files/fKi2CjrBDNZUFHWPHJpi)
   7. Once done, click `Metadata` on the Metadata portal do the same for chain metadata (“Metadata” tab on the right). This is multi-part QR so you need to keep your camera on the animated QR until the scan is completed. On devices with low camera quality, it can even take few minutes.\
      ![](/files/QdKpw8x9yhyO8DAhurV4)
   8. Now click `Approve` to approve the network metadata\
      ![](/files/KQZksp3c3c1rcRpuRm73)
3. Generate/import your keys into Vault. This is done in “Key Sets” tab in Vault. If you are generating new keys, make sure to securely back up your seed phrase. To keep the air-gap, you should never keep your seed phrase on an online device. Old pen and paper are your best friends. When adding a key, make sure to select Joystream as network for it.
4. Set up your desktop extension. You can use both SubWallet and Talisman extensions. For this example, we’ll use SubWallet.
   1. Install SubWallet browser extension
   2. During setup, select “Attach an account” and click “Connect a Polkadot Vault account".\
      ![](/files/yTCfEkYbFHdSA7rmonXH)\\
   3. Click “Scan QR code”. This will most likely fail because of no camera access. Click the “Go to Settings” button and toggle “Camera access for QR” at the bottom. At this point you will most likely also need to allow camera access in browser/system popup.
   4. Once done, click back button in top left corner. You should now see preview from your camera.
   5. In the Polkadot Vault app, on “Key Sets” tab, select your keypair. Then, select the account from your keypair you want to use (you can derive multiple accounts from a single keypair). You should now see QR code with your public key. Place your phone in front of your desktop camera to finish SubWallet import
   6. At this point your account is imported into SubWallet. To finish the setup, you may want to enable Joystream network balance in SubWallet. To do so, click settings icon in top right corner and enable Joystream in the list of networks.
5. Now that your account is imported, you can use it as you would any other.
   1. Connect your SubWallet extension to Pioneer/Atlas/Gleev
   2. When you want to send a transaction, you will be presented with a QR code. Scan it with your Vault.
   3. Vault will produce another QR that represents this signed transaction. Scan it back on your desktop.
6. All done!

*(A special thanks to Klaudiusz for writing this tutorial and also setting up the metadata portal)*


# Blockchain

The centerpiece of the Joystream Network

## Introduction

At the heart of the Joystream Network sits a content, social, governance and asset ledger that coordinates the activity of all consumers, service providers, creators and applications.

## Overview

The blockchain is a standalone L1 blockchain with it's own independent validator set. Currently, there is only a single reference implementation, which is written in Rust, and is based on the [Substrate](https://docs.substrate.io/) blockchain development framework. This means that the consensus and networking logic have already been implemented, and the community can focus on the domain specific state machine which is core to Joystream. This is also written in Rust, and this state machine - called the *Runtime*, runs in a [WebAssembly](https://en.wikipedia.org/wiki/WebAssembly) execution environment. *While it is possible to incorporate a smart contract environment, like the* [*EVM*](https://substrate-developer-hub.github.io/docs/en/knowledgebase/smart-contracts/evm-pallet)*, that is currently not part of the Joystream runtime.* The architecture of a Joystream validator node is shown below.

<figure><img src="/files/QFZp6rqu5x6f9xIkXgMw" alt=""><figcaption><p>Overview of Joystream node architecture, built on Substrate.</p></figcaption></figure>

## Consensus

Validators are selected based on [Nominated PoS](https://arxiv.org/abs/2004.12990), and block production occurs in a slot-based stochastic block authoring schedule called [BABE](https://research.web3.foundation/en/latest/polkadot/block-production/Babe.html), or Blind Assignment for Blockchain Extension. Deterministic finality is produced through a classical BFT agreement protocol called [GRANDPA](https://arxiv.org/abs/2007.01560), or HOST-based Recursive Ancestor Deriving Prefix Agreement), which depends on ⅓ honest validator assumption.

## **Accounts and Digital Signature Schemes**

Accounts are identifiers under which transactions, for example for disposing of owned assets, can be issued. There are two kinds of accounts in the system. There are the *normal accounts* for which some known digital signature key pair is the credential used to authenticate transactions. The signature scheme used in Joystream is Sr25519, based on the Schnorrkel variant that uses Schnorr signatures with Ristretto point compression. Then there are *keyless accounts*, which are only meant to hold assets, in particular the native asset $JOY, but which are not owned by any key pair. They are also sometimes referred to as *module accounts*. They are loosely analogous to smart contract accounts in the EVM context, and some examples are.

## Genesis Block

The genesis block was the first block, which contained no user transactions, and had a block hash of `6b5e488e0fa8f9821110d5c13f4c468abcd43ce5e297e62b34c53c3346465956`. It contained the initial state of the blockchain, which primarily concerned

* the initial distribution of [account balances](/usdjoy#genesis-block) and initial state variables for various subsystems, please consult description of each subsystem to find each such initial value, but be aware that subsequent transactions may have altered them.
* the initial WebAssembly runtime.

## Boot Nodes

When starting a new full node, it needs to fetch all the data in the canonical chain, and this requires an initial list of nodes, called the *boot notes*, from which to download this data.. After initially connecting to the peer-to-peer network, a node will learn about additional nodes which can be used on next boot process after a possible session interruption.

## Execution Strategy

Using the WebAssembly runtime is important because the WebAssembly and native runtimes can diverge. For example, if you make changes to the runtime, you must generate a new WebAssembly blob and update the chain to use the new version of the WebAssembly runtime. After the update, the WebAssembly runtime differs from the native runtime. To account for this difference, all of the execution strategies treat the WebAssembly representation of the runtime as the canonical runtime. If the native runtime and the WebAssembly runtime versions are different, the WebAssembly runtime is always selected. A full-node will not attempt to use its native runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`, `spec_version` and `authoring_version` are the same between `RuntimeVersion` in Wasm and native. `authoring_version` is the version of the authorship interface. An authoring node will not attempt to author blocks unless this is equal to its native runtime.

## WebAssembly Execution Environment <a href="#webassembly-execution-environment" id="webassembly-execution-environment"></a>

The WebAssembly execution environment can be more restrictive than the Rust execution environment. For example, the WebAssembly execution environment is a 32-bit architecture with a maximum 4GB of memory.

## Forkless Upgrades

The Joystream blockchain takes advantage of a core feature of Substrate, which is that runtime itself - as an executable WebAssembly state machine, is held in the state of the chain. This awareness of the underlying state transition function allows for the blockchain to update it's own runtime on the fly, based on it's on domain specific rules. In Joystream this can be triggered by a [runtime upgrade proposal](/system/proposal-system#runtime-upgrade) being passed by the council. This has the benefit of providing a binding and transparent mechanism by which the rules of the protocol change, reducing the risk of permanent forks either due to contention or simple human coordination failures in the upgrade deployment itself.

Here is a running list of upgrades that have taken place.

<table><thead><tr><th width="143.33333333333331">Network</th><th>Deployed</th><th>Runtime*</th><th>spec_version**</th></tr></thead><tbody><tr><td>Mainnet</td><td>Friday at 9:07:42 PM CET December the 9th, 2022</td><td><code>1a1d11d2cc214edb180fd861826a9450df1acc650226db604d96f489f0a36f8f</code></td><td>1000</td></tr><tr><td>Ephesus</td><td>Wednesday at 9:47:12 AM CET April 12th, 2023</td><td><code>b0b35055b27a00c6a6be9c287049c79a9060e923c268de4ba148badcd435c184</code></td><td>1001</td></tr><tr><td>Nara</td><td>Wednesday at 14:06:54 AM CET March 13th, 2024</td><td><code>c3dda36a68353b12c57263ee101964b6ea80dadefa4a3d1e67903df7cae064c0</code></td><td>2002</td></tr></tbody></table>

*\* blake2-256 hash of runtime WASM runtime.*\
*\*\* Version of the runtime specification, must be distinct for each runtime for a chain. Is involved in execution strategy and also transactions commit to this version to avoid unintended semantic changes from time of signing to time of block inclusion.*

## Resource Accounting & Fees

Resource accounting is the activity of anticipating the computational resources consumed by an impending transaction or other execution trigger, such as a runtime upgrade or a block pre- or post-processing hook. The purpose of such accounting is to constrain overall consumption within blocks, so as to preserve decentralization by bounding node operational cost, and also to use as input for setting fees for pricing user transactions, which itself both funds overall consensus security budget and deters denial-of-service attacks.

### Resources

There are three scarce resources which are accounted for when pricing transactional use of the blockchain

* **Block space:** The size of the transaction in a block.
* **Weight:** Unit of compute time, specifically 1 picosecond of compute time on a reference machine. The weight of a transaction is the worst case total weight of executing it, and the process of determining how to efficiently forecast the *weight function*, i.e. the weight estimator for a concrete invocation of a given transaction, is called *benchmarking*, and is done by blockchain engineers when writing runtime and node code.
* **State:** The added size to the state of the blockchain after the transaction.

### Reference Hardware

The estimation of the weights, which is the time needed to perform various computations, is done on some reference hardware specification. Having such a specification also provides constraints for what validators should adhere to in order to process the chain in a timely manner, otherwise one can earn less era points, and potentially even get slashed.

The benchmarking is currently done on VM instances of two major cloud providers: Google Cloud Platform (GCP) and Amazon Web Services (AWS). To be specific, we used `c2d-highcpu-8` VM instance on GCP and `c6id.2xlarge` on AWS. A detailed specification is provided below.

<table><thead><tr><th width="135">Resource</th><th>Requirements</th></tr></thead><tbody><tr><td>CPU</td><td><ul><li>Prefer single-threaded performance over higher cores count.</li><li>Simultaneous multithreading disabled (Hyper-Threading on Intel, SMT on AMD)</li><li>4 physical cores @ 3.4GHz</li><li>Intel Ice Lake, or newer (Xeon or Core series); AMD Zen3, or newer (EPYC or Ryzen)</li><li>x86-64 compatible</li></ul></td></tr><tr><td>Storage</td><td>An NVMe SSD of 1 TB (As it should be reasonably sized to deal with blockchain growth). In general, the latency is more important than the throughput.</td></tr><tr><td>Memory</td><td>16GB DDR4 ECC.</td></tr><tr><td>System</td><td>Linux Kernel 5.16 or newer.</td></tr><tr><td>Network</td><td>The minimum symmetric networking speed is set to 500 Mbit/s (= 62.5 MB/s).</td></tr></tbody></table>

### Fees

In a distributed system, a user can require nodes to perform costly computation. Depending on the design of the system, the compute may be a one time event, or it may need to be replicated by multiple parties when they attempt to validate the integrity of the system. Regardless, this ability to command the compute resources of system participants means that some mechanism is needed to restrict and ration access to such resources. In blockchain systems specifically, access is intended to be open and permissionless, hence paying for access is has been considered the most neutral solution to this problem. Fees associated with inclusion of transactions in the blockchain plays the role of such payments in blockchains.

In Joystream, fees are burned, except an extra user specifiable tip, which goes to the block producer, so as to facilitate a fee market. For a detailed explanation of how fees are derived, please consult the link below.

{% embed url="<https://docs.substrate.io/build/tx-weights-fees/#how-fees-are-calculated>" %}
Fee calculation in Substrate chains.
{% endembed %}

## Parameters

Here are the most important blockchain level parameters.

<table><thead><tr><th width="286.3333333333333">Concept</th><th>Reference Codebase Token</th><th>Value</th></tr></thead><tbody><tr><td>Target block time</td><td><code>ExpectedBlockTime</code></td><td>6 seconds</td></tr><tr><td>Max block space</td><td><code>MaximumBlockLength</code></td><td>5 MB</td></tr><tr><td>Max block weight (normal)</td><td><code>RuntimeBlockWeights</code></td><td>75% of 2s of weight</td></tr><tr><td>Max block weight (operational)</td><td><code>RuntimeBlockWeights</code></td><td>25% of 2s of weight</td></tr><tr><td>Weight to fee function</td><td><code>WeightToFee</code></td><td><a href="https://github.com/Joystream/joystream/blob/master/runtime/src/constants.rs#L106">Link</a></td></tr></tbody></table>

*Warning: There is always a risk of such a table of values going stale, so only use these as baseline references, and consult reference implementation for the relevant runtime.*


# Account Generation

A guide for securely generating keys for the Joystream network.

### Glossary

* **Account** means a destination of funds, which can be credited or debited, and is under the control of a unique cryptographic keypair, where only the holder of the secret key can debit funds.
* **Account ID** means the public key of an Account compatible with substrate based chains. Equivalent to "**Public key (hex)**"
* **SS58 Address** means any valid SS58 encoding (Base58) of the Account ID. Equivalent to "Public key (SS58)". Note that unlike bitcoin, there is no one way hashing required to get the SS58 Address from the Account ID, so there is no information leaked by sharing the latter.
* **Joystream Network Address** means an SS58 Address encoded with the Joystream network prefix 126

## Introduction

This guide is primarily targeting current Stakeholders, meaning anyone with a claim on some allocation of Joystream mainnet tokens ($JOY) in the genesis block. The concepts of securely generating keys offline however will apply for others as well.

## Key Generation

Although there are other ways to generate keys we recommend `subkey`, a key generation and management utility developed and maintained by Parity Technologies. It's simple to use and allows keys to be generated offline. Both installation and usage is explained in the official [docs](https://docs.substrate.io/reference/command-line-tools/subkey/), that also contains plenty of examples. The downside of using the official release is that it doesn't support encoding the SS58 Address as Joystream Network Addresses out of the box.

Note that sharing your keys as Joystream Network Addresses is not required, as any Account ID and SS58 Address is sufficient. However, instructions for generating Joystream Network Addresses can be found [here](#joystream-network-address-generation).

A more user friendly alternative to the recommended key generation exists in the form of the [polkadot-js browser extension](https://polkadot.js.org/extension/). This is the default way of interacting with the Joystream chain, both for governance participants and content creators. Although there are some risks associated with generating keys in the browser generally, it may be a worthwhile tradeoff against the complexity of subkey, as a key generated here can be used without any further action.

Instructions for Account generation with polkadot-js can be found [here](#joystream-network-address-generation-1).

### Distribution

Stakeholders are free to distribute your allocation across as many Accounts as they wish. There are some pros and cons associated with any distribution, so we encourage Stakeholders, regardless of how actively they intend to participate on mainnet, to consider this when choosing the amount of Accounts, and the balance distribution.

Once a reasonable number of Accounts has been shared with us (at least half of the genesis tokens has been accounted for), we will create and maintain a table in the handbook allowing you to look up your Account(s) to confirm it's balance and vesting schedule.

### Deliverable

Please prepare a .csv file with the following information:

* Identifier (for reference only)
* Account ID
* Joystream Network Address (or any SS58 Address)
* Balance
  * Note that it may be beneficial

Example using the official subkey release, eg. generating keys as "generic" SS58 Addresses:

```
Identifier,Account ID,SS58 Address,Balance
0,0x82813aa36e07a42750852f6d47f8356577946f9bc0c617a1e543449ff80dbc4f,5F1pUHSPi46J3afWv3o4CPAu2wZtMX2r92XWW2MsSXefTTrB,5000
1,0xc8ce749ca7682a93459f8fe821b30027559d1329663c76e037160e60c32f347b,5GbzmH2Dc4uiA2jTA1RLJ1JdxQoTYecAX8QhKpmDjc5kg4Pr,2000
...
```

Same example, but with Joystream Network Addresses:

```
Identifier,Account ID,Joystream Network Address,Balance
0,0x82813aa36e07a42750852f6d47f8356577946f9bc0c617a1e543449ff80dbc4f,j4UGj3KgMambKtC7GC2Ek3XstZKeTrQc1ry9wsaQBsboJ6yh4,5000
1,0xc8ce749ca7682a93459f8fe821b30027559d1329663c76e037160e60c32f347b,j4VruLKGBUnQjzeBCRys29A1dUnt33YBLF538hNoYAgEPKejc,2000
...
```

How to transfer this information to the Jsgenesis team will be shared directly with the relevant Stakeholders.

### Testing

Before the mainnet launch, Jsgenesis will deploy a testing network, providing everyone an opportunity to test their keys.

## Joystream Network Address Generation

Although you may use download the temporary [binary](#binary) we have published, we recommend instead compiling your own version of `subkey` as outlined below.

### Fork and Compile Subkey

If you want to compile a version of `subkey` that supports Joystream Network Address, (fork and) clone the [substrate repo.](https://github.com/paritytech/substrate) Replace the default `ss58-registry` package in `primitives/core/Cargo.toml`:&#x20;

```
# From
ss58-registry = { version = "1.18.0", default-features = false }

# To
ss58-registry = { package = "ss58-registry", git = "https://github.com/Joystream/ss58-registry", branch = "joystream" }
```

Then follow the official [installation docs](https://docs.substrate.io/reference/command-line-tools/subkey/#installation).

You may now use the flag `-n joystream` to generate or inspect Joystream Network Addresses. Example:

```
$ ./subkey generate -n joystream

Secret phrase:       twice better blouse hire circle alpha mix outside climb employ slam bamboo
  Network ID:        joystream
  Secret seed:       0xe12f4b779e0944d85d6a5f47513df6a4088567df94181c74c50f5fc659592a62
  Public key (hex):  0xcc1ef0f6a5c0e2c82169656f0bd5b4b657cc7397bae107a148fda50279ba5b5c
  Account ID:        0xcc1ef0f6a5c0e2c82169656f0bd5b4b657cc7397bae107a148fda50279ba5b5c
  Public key (SS58): j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA
  SS58 Address:      j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA

$ ./subkey inspect -n joystream j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA

Public Key URI `j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA` is account:
  Network ID/Version: joystream
  Public key (hex):   0xcc1ef0f6a5c0e2c82169656f0bd5b4b657cc7397bae107a148fda50279ba5b5c
  Account ID:         0xcc1ef0f6a5c0e2c82169656f0bd5b4b657cc7397bae107a148fda50279ba5b5c
  Public key (SS58):  j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA
  SS58 Address:       j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA
```

### Binary

We have published a binary of a node that supports Joystream Network Address that can be downloaded [here](https://github.com/Joystream/joystream/releases). Note that the node is built for this purpose alone, and should (may) not be used to connect with any current (testnet) or future mainnet.

This will allow you to:

* generate a Joystream Network Address with the command `./joystream-node key generate`
* inspect a private key with the command `./joystream-node key inspect SECRET SEED`
* inspect a public key with the command `./joystream-node key inspect --public SECRET SEED`
* inspect a Joystream Network Address with the command `./joystream-node key inspect`

A keypair generated with this version can be inspected with the official subkey, and will then confirm the Network ID to be 126.

Example:

```
# With joystream-node:
$ ./joystream-node key generate

Secret phrase:       twice better blouse hire circle alpha mix outside climb employ slam bamboo
  Network ID:        joystream
  Secret seed:       0xe12f4b779e0944d85d6a5f47513df6a4088567df94181c74c50f5fc659592a62
  Public key (hex):  0xcc1ef0f6a5c0e2c82169656f0bd5b4b657cc7397bae107a148fda50279ba5b5c
  Account ID:        0xcc1ef0f6a5c0e2c82169656f0bd5b4b657cc7397bae107a148fda50279ba5b5c
  Public key (SS58): j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA
  SS58 Address:      j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA

$ ./joystream-node key inspect j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA

Public Key URI `j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA` is account:
  Network ID/Version: joystream
  Public key (hex):   0xcc1ef0f6a5c0e2c82169656f0bd5b4b657cc7397bae107a148fda50279ba5b5c
  Account ID:         0xcc1ef0f6a5c0e2c82169656f0bd5b4b657cc7397bae107a148fda50279ba5b5c
  Public key (SS58):  j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA
  SS58 Address:       j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA

# With official subkey

$ subkey inspect j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA

Public Key URI `j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA` is account:
  Network ID/Version: 126
  Public key (hex):   0xcc1ef0f6a5c0e2c82169656f0bd5b4b657cc7397bae107a148fda50279ba5b5c
  Account ID:         0xcc1ef0f6a5c0e2c82169656f0bd5b4b657cc7397bae107a148fda50279ba5b5c
  Public key (SS58):  j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA
  SS58 Address:       j4VwFPPGcpP77MuQbWdn7EbpBbjM4Siq5AGGuraF8ktr8vqjA
```

This means regardless if you choose to generate your keypairs with the official `subkey` or `joystream-node key`, you can verify the results using the other in an online setting without exposing your private keys on an online computer.

## Polkadot{.js} Account Generation

The Polkadot wiki have created their own guide, which can be found [here](https://wiki.polkadot.network/docs/learn-account-generation#polkadotjs-browser-extension). However, as with subkey, displaying the Joystream Network Address format as default is not possible without some extra steps.

Below, you will find a slightly modified version of the same guide, with references to the polkadot network are removed and some joystream specific information is added.

### Polkadot{.js} Browser Extension

The [Polkadot{.js} Extension](https://polkadot.js.org/extension/) provides a reasonable balance of security and usability. It provides a separate local mechanism to generate your address and interact with Polkadot.

This method involves installing the Polkadot{.js} plugin and using it as a “virtual vault," separate from your browser, to store your private keys. It also allows the signing of transactions and similar functionality.

#### Create Account

Open the Polkadot{.js} browser extension by clicking the logo on the top bar of your browser. You will see a browser popup, not unlike the one below.

![](/files/hVZtk8aFYL5bKsUC5VbK)

Click the big plus button or select "Create new account" from the small plus icon in the top right. The Polkadot{.js} plugin will then use system randomness to make a new seed for you and display it to you in the form of twelve words.

![](/files/iVxdJHwr9fNl9pXHPUgk)

Back up these words, as it's the only way to recover your Account if your computer is lost or wiped. It is imperative to store the seed somewhere safe, secret, and secure. If you cannot access your account via Polkadot{.js} for some reason, you can re-enter your seed through the "Add account menu" by selecting "Import account from pre-existing seed".

![](/files/FEVt1uxJWz0GLTOvpGwU)

#### Name Account

The account name is arbitrary and for your use only. It is not stored on the blockchain and will not be visible to other users who look at your address via a block explorer. If you're juggling multiple accounts, it helps to make this as descriptive and detailed as needed.

#### Enter Password

The password will be used to encrypt this account's information. You will need to re-enter it when using the account for any kind of outgoing transaction or when using it to cryptographically sign a message.

Note that this password does NOT protect your seed phrase. If someone knows the twelve words in your mnemonic seed, they still have control over your account even if they do not know the password.

### Address Format

Your address' format is only visual - the data used to derive this representation of your address are the same, so you can use the same address on multiple chains. However, for privacy reasons, we recommend creating a new address for each chain you're using.

You can copy your address by clicking on the account's icon while the desired chain format is active. E.g. selecting "Substrate" as the format will change your address to start with the number 5, and clicking the colorful icon of your account will copy it in that format. While in Polkadot mode (starts with 1), that format will be copied, and so on.

#### Verify Joystream Address Format

You can display your new Account in the Joystream Network Address format, and even make test transaction on a staging network, without installing any additional software.

Suppose I create an Account and name it "test" using the extension. This will display the address in the default SS58 Address format.

![5FnKASiFwEnKPszomKKgRtQF552mMNA3fT978QxD2pkNPMFL](/files/Cv0NFUimCLPWHhLwReQD)

Open the [polkadot{.js} app,](https://polkadot.js.org/apps) with [this link](https://polkadot.js.org/apps/?rpc=wss://172.104.229.249.nip.io/ws-rpc#/accounts), as it also configures your endpoint to `wss://172.104.229.249.nip.io/`, a development network we have deployed for this purpose.

Allow your extension to connect to the website, and make sure any Accounts you want to use for the Joystream network is configured to "Allow use on any chain". The `accounts` tab of the app should now display your own Account(s), together with the default development chain Accounts (which you should not use to receive any JOY with).

![](/files/A0c3kueDxR13a0LpQgLJ)

Click on the Account of "interest", and you will be shown the full Joystream Network Address.

![j4V3DjUxDoxHMEVSZ3HmNH37EbT7LrFjDPPmYVxzXTtu12c29](/files/lyRivAnSzydZZquAkBzr)

Feel free to send a test transaction to your Account and back.


# Metaprotocols

We are all just here for transaction ordering, the rest is gravy.

## Introduction

The fundamental service of a any consensus system is to maintain and build a transaction ordering, whether it is a blockchain or anything else. Once transactions are ordered, one can trivially establish consensus about any higher level abstraction, such as a token, exchange, registry or anything else. In all blockchains, the native L1 transactions carry some minimal amount of semantics that are also validated by the validator enforced consensus rules, for example rules for creation, transfer, locking and destruction of the native asset (BTC, ETH, .etc), as this is needed to generate the game theoretic incentives for validators/miners to participate at all. Beyond this, some systems have very rich validator in state machines, like the EVM, while others do nothing else, like Bitcoin (more or less). In both systems however, one can build arbitrarily complex consensus state machines on top by simply overloading raw data fields in the native transactions, such as `calldata` in the EVM, or `OP_RETURN` in Bitcoin. There are many examples of this, such as

* **Counterparty:** an asset protocol on top of Bitcoin which actually implemented the [EVM on top of Bitcoin before Ethereum was aout](https://counterparty.io/news/counterpartys-evm-port-moves-forward/).
* **Omni:** an asset protocol on top of Bitcoin which was the foundation of the Tether system for a long time.
* **Blockstack:** a namespace protocol on top of Bitcoin.
* **Colored Coin:** another very old asset protocol on top of Bitcoin.

These systems all work by having a separate group of indexing nodes that can identify the embedded transactions in the blockchain, and understand their semantics, so as to allow these nodes to build and maintain a separate state machine.

These embedded consensus systems are often referred to as *metaprotocols*.

The computational load of the metaprotocol computation load on those nodes can even be charged using the normal transactional fee system of the blockchain, by having the metaprotocol require extra tip to be added above and beyond the base fee of the on-chain transaction itself in order to process the transaction and incurr the computational cost.&#x20;

## Benefits and Limits

There are a number of benefits to such metaprotocols

* Third party developers can innovate in a permissionless way, requiring buyin from community to upgrade consensus rules.
* Additional state can be introduced which can be ignored by most, or al, of the other system participants. This allows for introduction of transaction and migration computations that violate upper compute capability bounds on validators, which typically exist to ensure decentralization.
* Failures, or bugs, are less costly to remedy.
* Separate, or no, resource accounting. There is substantial complexity assocaited with managing both state bloat and transaction processing time natively.

## Usage

There are a range of different metaprotocols in use in Joystream, documentation will need to be enhanced on this in the future, hence for now the most useful reference will just be the message definitions in the monorepo.

{% embed url="<https://github.com/Joystream/joystream/tree/master/metadata-protobuf>" %}


# Staking

Aligning incentives for the long run

## Introduction

Staking, or bonding, is the act of locking up funds under some terms so that they are not transferable and otherwise not entirely usable as they otherwise would be. The terms, referred to as *unstaking terms* describe the circumstances under which the funds may begin to cease being staked. This may involve who is able to initiate this, at what time and whether there is a possible delay from initiation to completion. This time lag is referred to as the *unstaking period.* Another critical term will also be whether and some part, possibly all of, the funds may be burned. This is referred to as *slashing.*

Staking is used for two purposes to serve the system as a whole by providing more robust incentives for socially optimal conduct in some role that impacts the overall success of the system.

1. **Exposure:** By requiring that someone who occupies a role that impacts the value of the system has exposure to that value in their portfolio. For this requirement to be effective, this exposure should not be hedgeable, and it is generally assumed that markets for this are missing. It is also assumed that any harm or benefit that results from the actions of the actor will capitalize in the value of the platform, and thus be partially reflected in the value of the stake. This should in total discourage harmful conduct and encourage beneficial conduct.
2. **Punishment:** In cases where it is possible to, if only imperfectly, have the system adjudicate whether an actor has acted harmfully, the ability to slash funds as a result of such detection can generate very strong incentives for pro-social behavior. The adjudication may be purely cryptographic, or it may require some level of social consensus. In either case, to the extent that it reliably can detect failure - that is avoiding false positives and negatives, it is a very cost-effective means of generating incentives compared to the first approach. It's cheaper because it allows for less capital to be locked for a given level of deterrence effect.

## Locks

A *lock* is limitation applied to how funds can be used in an account, primarily to enable staking, and it is defined by&#x20;

* a *lock ID, unique across all locks on that account*
* *an amount , which is a quantity of tokens, and*
* *a type, of which there are a finite number*

These are the different types of locks that currently exist, each has a distinct ID, which means it is very easy to work at what sort of staking is going on funded by a given account.

## Vesting

Vesting only occurs on accounts which existed in the genesis block, and it occurs using a linear curve on the amount of funds encumbered by a vesting lock. Is implemented using the `pallet_vesting` pallet described below.

{% embed url="<https://paritytech.github.io/substrate/master/pallet_vesting/index.html>" %}
Vesting pallet
{% endembed %}

## Binding

When initiating staking of some kind, it is very often - although not always, in the context of inhabiting some other kind of role, like being a member. This means that the initiating the staking effectively requires proving two things at the same time

1. You occupy the role you claim, by virtue of controlling the role account.
2. You control the funds living on the staking account in question.

Both are achieved by signing with for some account and in general they will not be the same. As a result, it is required that a user connects - or *binds*, a given account which holds funds for the purposes of staking, to their membership, in advance of being able to use that account for staking as that member. This binding is a two step process, per account, where the first step is to turn the account into a *staking candidate* by issuing a request to bind to a given member by signing via this account, and the second step is for the member to accept this candidate, using the controller account for that membership.

## Rivalry

In what follows we attempt to briefly summarizes the what locks exist, their purposes and in what combinations are allowed on the same account. The reason some combinations of locks are acceptable, while others are not, stems from whether reusing capital across the two purposes of the locks is compatible with these purposes. Allowing capital to be reused has the benefit of more efficient use of capital for a single actor, reducing the barrier to getting involved in multiple activities simultaneously. At the same time, it may in some cases not be acceptable, if it ends up reducing the effective bond needed to generate good incentives for some form of participation.\
\
The model for reuse of accounts is quite simple. There is a finite set of lock types in the system, and they are partitioned into two subsets: rivalrous and non-rivalrous locks.

* No lock of a given type can be applied more than once to a given account.
* Non-rivalrous locks can be combined with any other lock of any kind.
* Rivalrous locks can only be combined with other non-rivalrous locks.

| Lock                            | Binding |       ID      | Rivalrous |
| ------------------------------- | :-----: | :-----------: | --------- |
| Voting                          |    No   |  \*b"voting " | No        |
| Vesting                         |  No\*\* | \*b"vesting " | No        |
| Invitation                      |   No\*  | \*b"invitemb" | No        |
| Bound Staking Account           |   Yes   | \*b"stakcand" | No        |
| Council Candidate Staking       |   Yes   | \*b"candidac" | Yes       |
| Council Member Staking          |   Yes   | \*b"councilo" | Yes       |
| Validation & Nomination Staking |    No   | \*b"staking " | Yes       |
| Proposals Staking               |   Yes   | \*b"proposal" | Yes       |
| Storage WG Staking              |   Yes   | \*b"wg-storg" | Yes       |
| Content Directory WG Staking    |   Yes   | \*b"wg-contt" | Yes       |
| Forum WG Staking                |   Yes   | \*b"wg-forum" | Yes       |
| Membership WG Staking           |   Yes   | \*b"wg-membr" | Yes       |
| Distributor WG Staking          |   Yes   | \*b"wg-distr" | Yes       |
| Builders WG Staking             |   Yes   | \*b"wg-opera" | Yes       |
| Gateway WG Staking              |   Yes   | \*b"wg-gatew" | Yes       |
| HR WG Staking                   |   Yes   | \*b"wg-operb" | Yes       |
| Marketing WG Staking            |   Yes   | \*b"wg-operg" | Yes       |
| Bounty Entry Staking            |   Yes   |  \*b"bounty " | Yes       |

\* It is not possible to initiation the invitation lock, it is automatically applied when a new member is invited on, hence the question of whether binding is required for applying the lock does not even apply.\
\*\* Vesting is only going to be setup for accounts originating from mainnet genesis block, and so by definition no binding would be needed for that.

## Balances

The *total balance* of an account is the total number of tokens in the account, and it can be split into two distinct parts the *free balance* and the *reserved balance*, where the latter refers to reservations in the sense described in [#reservation](#reservation "mention"). The naming of the former is quite misleading, it is inherited from Substrate terminology, as it does not refer to funds that freely can be used for any purpose. The second constraint which ultimately still may encumber the free balance are locks, as described in [#locks](#locks "mention"). The key concept to understand is that locks "stack", hence the net effect of all locks on an account is simply equivalent to the lock for the largest amount. Hence for example, if you have a free balance of 10, and locks of size 7, 6 and 2, then they net out to a locking effect of 7, which means that only 10 - 7 = 3 of the tokens actually are entirely unencumbered, also called *usable*. We refer to this balance as the *usable balance.* We can thus summarise as follows\
&#x20;\
`total_balance = free_balance + reserved_balance`

`usable_balance = max(free_balance - max{lock_1, ..., lock_N, 0}, 0)`

where `lock_i` is the lock amount of the `i'th` lock.\
\
`<add image here of how locks and reservations interact to influence the free, reserved, total and usable balance>`

## The Invitation Lock Exception&#x20;

As you can read in the [Memberships](/system/memberships#invitations) section of the membership subsystem article, the purpose of the invitation lock is to onboard a new member, for example a creator, onto the system. This means they have to be granted some minimal quantity of initial funds that allow them to engage with the system in a very basic way. Typically, this will be done by applications that apply their own Sybill resistance check. Since all such checks are imperfect, the invitation lock is applied with an amount matching the initial quantity of funds credited, and this lock prevents the user from simply cashing out by exchanging the tokens to some other account for some sidepayment. At the same time, the lock also allows consuming the funds for a wide range of transactions, for example such as creating a channel or uploading a video. Some transactions are explicitly excluded because they inherently involve transferring funds to some new stakeholder, for example when bidding on an NFT or funding a bounty.

The precise constraint for what invitation locks allow is as follows: if the transaction otherwise would have succeeded if the invitation lock was not present, and it is not among one of the cases below, then the transaction will still succeed regardless of the lock amount:

1. Any transaction with non-zero tip.
2. Balance transfers.
3. Bidding on an NFT.
4. Buying an NFT *now.*
5. Accepting an offer for an NFT which requires payment.
6. Purchase creator tokens in a sale or from AMM.
7. Contribute funds to a bounty.
8. Paying for a channel swap.
9. Sending funds to the council or working group budget.
10. Gifting a membership.
11. Buying a membership.

This upside of this exception to how locks are normally influencing funds available to fund transactions is that despite the membership controller account possibly having a usable balance which is too low, funds encumbered by this lock can be deployed for the relevant purposes of a transaction, which on many occasions may make the difference, certainly for a totally new user.

## Slashing

Slashing is the act of reducing the balance of an account by some amount, and also reducing the size of the lock which represents the use case under which the slashing occurs.

## State bloat

Some modules such as forum and working group allows user to call extrinsics that allows them to occupy storage. This can be a problem since some malicious users could use this to fill up the storage, either taking all the allotted space for that given storage map or claim storage space until no single node has enough storage. Furthermore, there is no incentive even for regular users to cleanup their storage use.

That's why when a user can use up storage on top of the transaction fee we require either a deposit or additional stake.

When no longer using up the storage the stake is released or the deposit is paid off.

While staking is easier to implement when we are already requiring an stake account, a deposit allows to incentivize other users cleaning up by paying the deposit to them instead of the original person making the deposit.

As it stands now the modules require a deposit to prevent the state bloat are:

* Forum pallet
* Pallet Discussion
* Blog

The pallets requiring a stake to prevent state bloat are:

* Working Group
* Membership


# Validation

Maintaining agreement over the growing history of the system.

## Intro

The Nominated Proof of Stake (NPoS) employed in the Joystream ecosystem is an advanced consensus mechanism that relies on the collaborative efforts of validators and nominators to ensure network security. Validators, pivotal in this framework, are tasked with block production and transaction validation. To qualify as validators, participants must *bond* a specified quantity of tokens, a process which symbolizes their commitment and aligns their interests with the network's stability. Once tokens are bonded, they can then be *staked* for engaging in block validation and acquiring rewards. Meanwhile, nominators, after initially bonding their tokens, play a crucial role by selecting reliable validators and allocating their bonded tokens to them, thus bolstering the network's overall security and efficiency. In Joystream, an 'era' denotes a set period during which staking rewards are determined and allocated in accordance with the contributions of validators and nominators. These rewards serve dual purposes: they incentivize participation and offset the inflationary impact of new token generation. To safeguard network integrity, Joystream incorporates a 'slashing' mechanism. This punitive measure targets validators engaging in harmful activities or exhibiting incompetence, penalizing them by destroying a portion of their staked tokens.

## Reward and Inflation

Tokens are minted as part of the inflationary mechanism to compensate validators and nominators for their participation and contribution to network security. This minting process is automated and governed by the network's consensus rules. The newly minted tokens are distributed as rewards at the end of each era. The mathematical expression used in reward computation is referred as *reward curve* and it's graph is as follows

![Reward Curve](/files/yl9aL63733qqsP3h5Seh)

The minimum and maximum inflation for stakers reward values of `0.705%` and `3%` are highlighted in the y axis and the ideal staking percentage of `50%` is highlighted in the x axis. The reward curve is such that stakers are incentivised if the the current staking percentage is below the ideal rate and disincentivized if it's above the ideal rate.

The current percentage of supply staked can be inspected in Joystream the [subscan dashboard](https://joystream.subscan.io/)

## Staking reward computation

Below is an illustrative example on how validator can determined their profit margin given their initial operational cost of setting up a server. Let's start with the following assumptions:

### Network Parameters:

1. **Total Supply of Tokens:** 1,000,000,000 tokens.
2. **Annual Inflation Rate for NPoS Rewards:** 1.3%.
3. **Percentage of Tokens Staked:** 12%.
4. **Era Length:** 6 hours.

### Validators and Nominators Configuration:

1. Validator 1 (v1): 40M JOY, 20% commission (20M validators, 20M nominators).
2. Validator 2 (v2): 40M JOY, 40% commission (20M validators, 20M nominators).
3. Validator 3 (v3): 30,000,000 JOY, 10% commission (15M validators, 15M nominators).
4. Validator 4 (v4): 10M JOY, 0% commission (2M validator, 8M nominators).

### Total Staked and Era Reward Pool:

1. **Total Staked Tokens:** 12% of 1,000,000,000 = 120,000,000 JOY.
2. **Annual Reward Pool:** 1,000,000,000 \* 1.3% = 13,000,000 JOY.
3. **Reward Pool per Era:** 13,000,000 JOY / (24 \* 365 / 6) = 8910.96 JOY (approx).

### Era Points and Reward Distribution:

Era points are a measure of a validator's contribution to the network during an era. These points are awarded for various actions like validating blocks, producing blocks, and other network-supportive actions. The specific allocation of era points can vary based on the network's rules and validators' performance. They are not predictable and they add a probabilistic component to the validator payout scheme.

Validator era point are used to computed the share of each validator for the total reward pool, using the formula `validatorPoints / totalPoints`: where `totalPoints` is the total number of points earned by all validators in the era, and `validatorPoints` is the number of points awarded to each unique validator during the same era. Suppose for the sake of simplicity that the share is proportional to the validator stake percentage over the total staked amount in the network:

1. Validator 1's share `v1s`: 40,000,000 / 120,000,000.
2. Validator 2's share `v2s`: 40,000,000 / 120,000,000.
3. Validator 3's share `v3s`: 30,000,000 / 120,000,000.
4. Validator 4's share `v4s`: 10,000,000 / 120,000,000.

However, in practice, era points are not strictly proportional to stake but depend on actual performance and contributions, which can vary.

### Calculating Rewards:

1. **Validator 1's Total Reward:**
   * Era Points Share: `v1s` \* 8910.96 JOY = 2970.32 JOY.
   * Commission: 20% of 2970.32 JOY = 594.06 JOY (goes to validator 1)
   * Validator Share: (80% of 2970.32) \* 20M/40M = 1188.12 JOY
   * Nominators Share: (80% of 2970.32) \* 20M/40M = 1188.12 JOY
2. **Validator 2's Total Reward:**
   * Era Points Share: `v2s` \* 8910.96 JOY = 2970.32 JOY.
   * Commission: 40% of 2970.32 JOY = 1188.12 JOY (goes to validator 2)
   * Validator Share: (60% of 2970.32) \* 20M/40M = 950.4 JOY
   * Nominators Share: (60% of 2970.32) \* 20M/40M = 950.4 JOY
3. **Validator 3's Total Reward:**
   * Era Points Share: `v3s` \* 8910.96 JOY = 2227.74 JOY.
   * Commission: 10% of 2227.74 JOY = 222.77 JOY (goes to validator 3)
   * Validator Share: (90% of 2227.74) \* 15M/30M = 1002.48 JOY
   * Nominators Share: (90% of 2227.74) \* 15M/30M = 1002.48 JOY,
4. **Validator 4's Total Reward:**
   * Era Points Share: `v4s` \* 8910.96 JOY = 742.58 JOY.
   * Commission: 0% of 2227.74 JOY = 0 JOY (goes to validator 4)
   * Validator' Share: (100% of 742.58) \* 2M/10M = 148.51 JOY.
   * Nominators Share: (100% of 742.58) \* 8M/10M = 593.94 JOY.

### Nominator Rewards:

Single nominators reward are distributed according to share of staked JOY, so for example if in the Validator 1 case there are two nominators staking 10M JOY each then every one of them gets:

* Nominator Share \* 50% = 950.4 \* 50% = 475.2 JOY

### Conclusion

From the numbers above validators can compute the respective USD revenue per era, which can then be used for budgeting

## Glossary

**`bonding duration`**

The amount of `eras` before a `bonded` account that unbonds has to wait until their tokens **can** be unlocked by the `staking` lock.

This will allow the tokens to be staked for "rivalrous" purposes, and, if no other locks are applied, be spent freely.

Set to 112 `eras`, eg \~403200 blocks, or 28 days.

**`commission`**

**`candidates`**

Validators and nominators get paid from block production on the network, where validators can set a variable commission rate, which is initially subtracted from the total rewards that validator is entitled to (for that period), where the commission determines the rate of distribution for the remaining rewards set out for the nominators that are backing that validator.

Set by the validator as a percentage of the reward for each `era`.

**`election`**

An operation performed automatically on-chain, by the runtime, to elect a new set of validators for the upcoming `era`. Who gets elected depends on a variety of factors, such as the amount of `candidates`, the number of `slots` and the `total active stake` for each validator.

**`era`**

A (whole) number of `sessions`, which is the period that the validator set (and each validator's active nominator set) is recalculated and where rewards are paid out.

Target is 6 `sessions`, eg \~3600 blocks, or 6h.

**`era points`**

Every time a specific validator produces a block, they earn points. The rewards for the individual validator for that `era` are proportional to their era points, which are reset when a new `era` begins.

**`nominator`**

Accounts that select a set of validators to nominate by bonding their tokens. Nominators receive some of the validators' rewards, but are also liable for slashing if their nominated validators misbehave.

**`rewards`**

The shared rewards earned by the entire validator set for each `era`. For the individual `validator`, they are proportional to the `era points` earned.

Note that the `total active stake` does not impact the `era points` or rewards directly, but of course, unless the validator gets a slot, they will not earn any rewards.

**`session`**

A session is a Joystream implementation term for a period that has a constant set of validators. Validators can only join or exit the validator set at a session change.

Target is \~600 blocks, or 1h.

**`session keys`**

Hot (must be online) keys that are used for performing network operations by validators, for example, signing GRANDPA commit messages.

**`slashing`**

The removal of a percentage of an account's JOY as a punishment for a validator acting maliciously or incompetently (e.g., equivocating or remaining offline for an extended period).

Will apply equally to nominators of a validator that gets slashed.

**`slots`**

Total amount of spots in the validator set at any given time. Can be adjusted up or down through a proposal.

**`staking`**

The act of bonding JOY tokens by putting them up as "collateral" for a chance to produce a valid block (and thus obtain a block reward). Validators and nominators stake their JOY in order to secure the network.

**`total active stake`**

The sum of stake put up by the validator itself, plus the amount each of the (potential) `nominators` backs the validator with. Used to the determine whether the validator gets a `slot` in the validator set or not in the `election` for the upcoming era.

### Validator General Requirements

* Experienced with how to setup and maintain high performance IT infrastructure
* Access to highly performant and reliable IT infrastructure, with high storage, (up & down) bandwidth and processing capacity
* Able to securely store keys
* Hold sufficient amount of the native platform token to put at stake
  * currently **at least** JOY 41.667k in a single account, which is the minimum to sign up – actually getting a validator slot likely requires more

### Validator Hardware Requirements

The Joystream blockchain, and therefore the `joystream-node` is built on the [substrate](https://substrate.io/) framework, developed for the [Polkadot](https://polkadot.network/) ecosystem. As Joystream is still in infancy on mainnet, we refer to the their expertise for the technical specification and [recommendations](https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware):

* **CPU**
  * x86-64 compatible;
  * Intel Ice Lake, or newer (Xeon or Core series); AMD Zen3, or newer (EPYC or Ryzen);
  * 4 physical cores @ 3.4GHz;
  * Simultaneous multithreading disabled (Hyper-Threading on Intel, SMT on AMD);
  * Prefer single-threaded performance over higher cores count. A comparison of single-threaded performance can be found [here](https://www.cpubenchmark.net/singleThread.html).
* **Storage**
  * An NVMe SSD of 1 TB (As it should be reasonably sized to deal with blockchain growth). An estimation of current chain snapshot sizes can be found [here](https://paranodes.io/DBSize). In general, the latency is more important than the throughput.
* **Memory**
  * 16GB DDR4 ECC.
* **System**
  * Linux Kernel 5.16 or newer.
* **Network**
  * The minimum symmetric networking speed is set to 500 Mbit/s (= 62.5 MB/s). This is required to support a large number of parachains and allow for proper congestion control in busy network situations.

It should be obvious that given the life span and size – thus state and transaction activity – of Polkadot relative to Joystream at this stage, it is certainly fine to scale down on things like storage...

## Guides

The instructions below cover Linux binaries only. If you want to build from source, clone the [repo](https://github.com/Joystream/joystream) and follow the build steps there.

### Install and Deploy

* Every time something is written in `<brackets>`, this means you have to replace this with your input, without the `<>`.
* When something is written in `"double_quotes"`, it means the number/data will vary depending on your node or the current state of the blockchain.
* For terminal commands:
  * `$` means you must type what comes afterwards
  * `#` means it's just a comment/explanation for the readers convenience

```

# This is just a comment, don't type or paste it in your terminal!

$ cd ~/

# Only type/paste the "cd ~/, not the preceding $ !

```

For the purposes of simplicity, we will assume:

1. You are user `joystream`, with sudo priveliges.
2. You want to save everything in `/home/joystream/bin/`

#### Download Node Binary

Find the latest release [here](https://github.com/Joystream/joystream/releases/latest) or get the tag from the command line with:

```

$ curl -sL https://api.github.com/repos/Joystream/joystream/releases/latest | jq -r ".tag_name"

```

At the time of writing, the latest release is `v12.1001.0`, whereas the last node binary is from version `v12.1000.0` (mainnet)

```

# Create the directory, and go there

$ mkdir ~/bin && cd ~/bin

# Assuming the latest version is still v12.1000.0

# Download the binary:

$ wget https://github.com/Joystream/joystream/releases/download/v12.1000.0/joystream-node-8.0.0-1a0d1f677df-x86_64-linux-gnu.tar.gz

# unzip it:

$ tar -vxf joystream-node-8.0.0-1a0d1f677df-x86_64-linux-gnu.tar.gz

# Download the chain spec:

$ wget https://github.com/Joystream/joystream/releases/download/v12.1000.0/joy-mainnet.json

# test that your node works:

$ ./joystream-node --chain-spec joy-mainnet.json --pruning archive

```

Assuming it starts syncing, you can stop it right away with `ctrl+c`

#### Configuration

The node lets you set a variety of option flags. You can display them all with `./joystream-node --help` Some basic `options` you should enable or consider:

> \--chain \<CHAIN\_SPEC>
>
> Specify the chain specification. It can be one of the predefined ones (dev, local, or staging) or it can be a path to a file with the chainspec (such as one exported by the `build-spec` subcommand).

* **Required**
  * Without this flag, you will not connect the chain.

> \--pruning \<PRUNING\_MODE>
>
> Specify the state pruning mode, a number of blocks to keep or 'archive'. Default is to keep all block states if the node is running as a validator (i.e. 'archive'), otherwise state is only kept for the last 256 blocks.

* **Required for validators**
  * If you want to be a validator, the node must run with `--pruning archive`
  * If you start syncing without that flag enabled, you will have to wipe your node and sync again if you change your mind.

> \--validator
>
> Enable validator mode. The node will be started with the authority role and actively participate in any consensus task that it can (e.g. depending on availability of local keys).

* **Required for validators**
  * Unlike with `--pruning`, it only has to be set when you are actually in the validator set to have an effect, so you don't have to re-sync if you forget while syncing.

> \--name
>
> The human-readable name for this node. The node name will be reported to the telemetry server, if enabled.

* **Optional**
  * May serve some benefits if you want someone to nominate you, but may make it easier to identity you.

As a validator, you should (as a bare minimum) be very restrictive in terms of RPC access to your node. Go through the options, and double check that the defaults are in line with your preferences and risk tolerance.

#### Run as a Service

Running as a service means that the node will continue running as a daemon, and you can enable it to restart in case of crashes and on reboot.

It requires sudo privileges. If you are not user `root`, add `sudo` before commands.

Example file below, with essentials only:

```

[Unit]
Description=Joystream Node
After=network.target

[Service]
Type=simple
User=joystream
WorkingDirectory=/home/joystream/bin/
ExecStart=/home/joystream/bin/joystream-node \
 --chain /home/joystream/bin/joy-mainnet.json \
 --pruning archive \
 --validator
Restart=on-failure
RestartSec=3
LimitNOFILE=10000

[Install]
WantedBy=multi-user.target

```

```

# Create/open a file with your favorite editor (I use nano below)

$ sudo nano /etc/systemd/system/joystream-node.service

# Paste in the example file above, and do "ctrl+x", then "y" and "return" to save

# start it up:

$ sudo systemctl start joystream-node

# check that it's working:

$ sudo systemctl status joystream-node

# For a brief status, OR

$ sudo journalctl -f -n 100 -u joystream-node

# To monitor the log

# If you are happy, enable it so it will start automatically on boot:

$ sudo systemctl enable joystream-node

```

### Setup Keys and Validate

With your validator node up and running, you are now ready to set up keys and announce your intentions on chain.

#### Generate Session Keys

In the terminal on your node (will only work if you are on running the chain on the same machine!):

```

$ curl -H "Content-Type: application/json" -d '{"id":1, "jsonrpc":"2.0", "method": "author_rotateKeys", "params":[]}' http://localhost:9933

# Which should return:

{"jsonrpc":"2.0","result":"0xabc...123","id":1}

```

Where `0xabc...123` (a much longer string in reality) is a concatenation of four public keys hex-encoded. The private keys should have been injected in your `base-path`. Make sure you copy this string over somewhere, as you need it later.

Assuming you only did this once (while running this *this* chain), and you didn't set a different `--base-path` flag:

```

# On the current network, replace <chain-name> with joy_testnet_7

$ ls -a ~/.local/share/joystream-node/chains/<chain-name>/keystore

# Which should return 4 files, each a long string starting with a 6.

# You can confirm more precisely by:

$ curl -H "Content-Type: application/json" -d '{"id":1, "jsonrpc":"2.0", "method": "author_hasSessionKeys", "params":["0xabc...123"]}' http://localhost:9933

# Which, if you have the corresponding ready to sign, should return:

{"jsonrpc":"2.0","result":true,"id":1}

```

**Warning:**

* It's both bad practice, and a possible slashing risk, to keep multiple set of session keys on one node. If you wanted to try the command, made a mistake, or for whatever reason want to switch them, delete them. If you have had set them on chain (see next steps), you can change them before you get into the validator set.
* Keeping the same set of keys, on multiple nodes, all running with the `--validator` enabled, will cause a slash. A good backup system could include backup nodes, but be careful. It's better to get "booted" for an era than to double sign blocks. It will be treated as an attack even if just by accident.

#### Configure Validator on Chain

For the time being, we will only show how to do this with [Polkadot{.js} apps](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc.joystream.org#/explorer), a web based UI, and the [Polkadot{.js} extension](https://polkadot.js.org/extension/), a that works with both the aforementioned UI and Joystreams [own Pioneer](https://pioneerapp.xyz/#/profile).

As the polkadot-js UI serves lots of projects in the substrate ecosystem, you have to make sure to set the correct endpoint, meaning which network (and node) you connect to. The link above does Joystream automatically, and sets it as default in local storage (until another is set).

The network endpoint can also be set manually by clicking the top left corner, and selecting "Live Networks" -> "Joystream" (hosted by Jsgenesis) before clicking "Switch", from the meny that appears.

This will display the logo, network name, node version and latest block height of the chain you are currently connected to as shown below.

<figure><img src="/files/ihmq77fLyLkdNU9WoU5b" alt=""><figcaption></figcaption></figure>

**With Polkadot-js**

You need two keys for this, one to be the `controller` and one as the `stash`. The latter holds the stake and must sign at least once to "delegate" to the `controller` which is running the "day to day" operations.

Assuming you are fully synched, your node is running, and you have

**Steps:**

1. Go to the "staking actions" tab - "Network" -> "Staking" -> "Accounts", and click the "+ Validator" button in the top right corner.
2. Select a `stash` and `controller` account from the dropdown, set the "value bonded" as the amount you want to stake, and choose a "payment destination", then hit "next".
3. Paste in the public session keys (`0xabc...123`), choose a "reward commission percentage" and whether you want to allow nominations or not, then click "Bond & Validate".

If you are preparing this for later, click the "+ Stash" button instead. This allows you to wait for your session keys and/or synching your node.

#### Being a Validator

Assuming the transaction went through, you will now appear under the "waiting" tab [here](https://polkadot.js.org/apps/#/staking). That means you are in the queue for joining the validator set, but when (and whether) you actually join depends on the competition for getting a slot.

At all times, there is a limit to how many can become validators. What that number is set by the council. The current value can be found in the [chain state](https://polkadot.js.org/apps/#/chainstate) -> "staking" -> "validatorCount".

Suppose that number is `n`, and that there are `m` validators that, towards the end of each `era` were already validating or joined the queue:

* If `n >= m`, all will be elected
* If `n<m`, the `n` validators with the highest **total active stake** will be elected for the upcoming `era`

*Notes:*

* An `era` lasts \~6h.
* **total active stake** refers to the active stake for the validator itself, plus all of their nominators.

**Transaction Rejected**

There is a minimum threshold of funds required to stake as a validator. As you can get slashed, that means you can not "re-use" tokens that are staked for other "slashable" purposes, such as role stake.

That number can be found in the [chain state](https://polkadot.js.org/apps/#/chainstate) -> "staking" -> "minValidatorBond". (In base value `HAPI`, meaning `1*10^-10 JOY`)

Another reason the transaction can be rejected is if the maximum number of `bonded` accounts have declared as validators. That number can be found in the [chain state](https://polkadot.js.org/apps/#/chainstate) -> "staking" -> "maxValidatorsCount". How many there are currently can be found in the [chain state](https://polkadot.js.org/apps/#/chainstate) -> "staking" -> "counterForValidators".

There are of course a limitless amount of other reasons the transaction could be rejected if you constructed it yourself in the cli.

### Advanced Setup

`TODO`

##

```
```


# Nomination

Deciding who gets to be a validator.

## Introduction

The Joystream blockchain is a PoS system, meaning that what actors are involved in validation, that is producing blocks and maintaining agreement over the canonical history of blocks as it is extended, is determined by how much stake is devoted to the candidacy of any prospective validator. Nominators are those native token holders who choose to be involved in the activity of voting for validation candidates, and they do so in return for a share of the validation rewards of this validator, and under the risk of loosing some of their funds if this validator misbehaves.

## Nomination

### Responsibilities

* Select and monitor validator performance.

### Requirements

* Able to securely store keys
* Hold sufficient amount of the native platform token to put at stake

## Risks and Rewards

`TODO: Martin`

## Selecting Validators

{% embed url="<https://wiki.polkadot.network/docs/learn-nominator#what-to-take-into-consideration-when-nominating>" %}
How to Pick a validator
{% endembed %}

## Meet Your Validators

{% embed url="<https://joystream.notion.site/Meet-Your-Validators-a4a1d8dd629d4c9fa49cdfb3e252b86d>" %}
Here is a list of validator identities
{% endembed %}

## Guides

### Configure Nominator on Chain

For the time being, we will only show how to do this with [polkadot-js](https://polkadot.js.org/apps/#/explorer). This requires that you have your accounts stored in the app itself, or in the [Polkadot{.js} extension](https://polkadot.js.org/extension/).

We will add another option for doing this, using the joystream-cli and (optionally) signing offline, if you don't want to expose them on the internet.

### **With Polkadot-js**

Go to [polkadot-js](https://polkadot.js.org/apps/#/explorer), where you may have to set the correct endpoint to connect to.

Unless it says `Joystream - joystream-node/7` (update for mainnet) in the top left corner, click on whatever it says, and select the Joystream network you want to connect to.

You need two keys for this, one to be the `controller` and one as the `stash`. The latter holds the stake and must sign at least once to "delegate" to the `controller` which is running the "day to day" operations.

#### **Steps:**

1. Go to the [staking actions tab](https://polkadot.js.org/apps/#/staking/actions), and click the "+ Nominator" button in the top right corner.
2. Select a `stash` and `controller` account from the dropdown, set the "value bonded" as the amount you want to stake, and choose a "payment destination", then hit "next".
3. Select which candidates you want to nominate, and click "Bond & Nominate"

If you are preparing this for later, click the "+ Stash" button instead.

### **With joystream-cli**

`TODO`

### Being a Nominator

Assuming the transaction went through, you will now appear under the "waiting" tab [here](https://polkadot.js.org/apps/#/staking). That means you are in the queue for joining the validator set, but when (and whether) you actually join depends on the competition for getting a slot.

At all times, there is a limit to how many can become validators. What that number is set by the council. The current value can be found in the [chain state](https://polkadot.js.org/apps/#/chainstate) -> "staking" -> "validatorCount".

Suppose that number is `n`, and that there are `m` validators that, towards the end of each `era` were already validating or joined the queue:

* If `n >= m`, all will be elected
* If `n<m`, the `n` validators with the highest **total active stake** will be elected for the upcoming `era`

*Notes:*

* An `era` lasts \~6h (on mainnet, \~1h on the current testnet).
* **total active stake** refers to the active stake for the validator itself, plus all of their nominators.

### **Transaction Rejected**

There is a minimum threshold of funds required to stake as a validator. As you can get slashed, that means you can not "re-use" tokens that are staked for other "slashable" purposes, such as role stake.

That number can be found in the [chain state](https://polkadot.js.org/apps/#/chainstate) -> "staking" -> "minValidatorBond". (In base value `HAPI` = 1\*10^-10 JOY)

Another reason the transaction can be rejected is if the maximum number of `bonded` accounts have declared as validators. That number can be found in the [chain state](https://polkadot.js.org/apps/#/chainstate) -> "staking" -> "maxValidatorsCount". How many there are currently can be found in the [chain state](https://polkadot.js.org/apps/#/chainstate) -> "staking" -> "counterForValidators".

There are of course a limitless amount of other reasons the transaction could be rejected if you constructed it yourself in the cli.


# Memberships

## Introduction

A membership is a representation of an actor on the platform, and it exist to serve the following purposes

* **Profile:** A membership has an associated rich profile that includes information that support presenting the actor in a human friendly way in applications, much more so than raw accounts in isolation.
* **Reputation:** Facilitates the consolidation of all activity under one stable identifier, allowing an actor to invest in the reputation of a membership through prolonged participation with good conduct. This gives honest and competent actors a practical way to signal quality, and this quality signal is a key screening parameter allowing entry into more important and sensitive activities. While nothing technically prevents an actor from registering for multiple memberships, the value of doing a range of activities under one membership should be greater than having it fragmented, since reputation, in essence, increases with the length and scope of the history of consistent good conduct.

It's important to be aware that a membership is not an account, but a higher level concept that involves accounts for authentication. The membership subsystem is responsible for storing and managing all memberships on the platform, as well as enabling the creation of new memberships, and the terms under which this may happen.

## Concepts

### Membership

A membership includes the following

* **Id:** A unique immutable non-negative integer identifying the member, automatically assigned when membership is created.
* **Root Account:** A required account that is used only to update the controller account. Need not be unique across members, but in practice probably will be.
* **Controller Account:** A required account that is used to authenticate as the member, both in this and other parts of the platform. Need not be unique across members, but in practice probably will be.
* **Name:** A human readable mutable string.
* **Handle:** A unique mutable string handle.
* **Invites:** A mutable non-negative integer that represents how many invitations this member has.
* **Verified:** A mutable boolean indicator that reflects whether the implied real world identity in the profile corresponds to the true actor behind the membership.
* **Avatar:** A mutable URI for an avatar image.
* **About:** A mutable human readable text description.
* **Founding Member**: A signifier that this member holds some specific historical significance to the launch of the platform. This value will be stored in the chain state when mainnet launches, but for now, since we want to grant founding member status on an ongoing member through a SUDO call, this is in history.
* **Staking Accounts:** A set of accounts that have been bound to this membership for the purpose of holding staked funds. One account can only be used to stake for at most two separate purposes simultaneously, and one of them has to be an election related purpose, i.e. voting or council candidacy. One account can only be a staking account for a single member, and once associated in this way, it cannot be de-associated and associated with another member. Every candidate as an staking account requires staking to stay as such.

#### Metadata

For the sake of the runtime the following fields are completely opaque:

* Name
* Avatar
* About

Therefore these fields aren't saved into the storage, aren't checked, and in the extrinsics parameters they are all bundled together in a single `meta_data` field.

### Working Group

The membership subsystem has a working group. The purpose of the group is to effectively distribute invitation quotas and verified status. The lead, called the *membership lead* has the extra task of refreshing the quotas to workers, which they can in turn then distribute to other members. Workers are referred to as *membership evangelists*.

### Buying a Membership

The primary means of establishing a membership is buying one by burning tokens, the number of which is held in a mutable parameter denoted as `membership_price`. When purchasing a membership, another member, called a *reference,* can be referenced, resulting in a portion of the burned funds being credited to the reference. This portion is a mutable parameter denoted as `referral_cut` and defined as the membership fee percentage. Currently, there is a limit of 50% for the referral cut.

### Invitations

The long-term objective is to have most memberships established by being purchased, however, currently the various costs associated with gaining access to a digital asset are considerable. As a result, person-to-person invitations is an alternative mechanism for giving new community members direct access to participate on the platform. When buying a membership, an initial number of invitations are granted, the number of which is held in a mutable parameter denoted as `default_invite_count`. On accepting membership invitations make sure that you're controlling both controller and root accounts. Losing (or not having in the first place) control of the root account could result in a hostile takeover of your membership.

#### Quotas

This works by giving each person an *invite quota*, that is a certain number of outstanding invitations available at any given time. The quota is initially set when buying a membership, but it can also be increased by having another member transfer some of theirs. The quota of the lead can also be set by the council through a proposal.

#### Initial Balance

When a member is invited, they are also credited some initial balance to their controller account so that they can engage in some initial set of activities. These funds are however only spendable on transaction fees, nothing else, such as transferring to another member. The amount credited is held in a mutable parameter denoted as `invited_initial_balance`.

#### What can you do?

`<WIP: add detailed breakdown of what invited funds allow you to do, as its a nuanced topic>`

## Constants

The following constants are hard coded into the system, they can only be updated with a runtime upgrade.

| Name                    | Description                                                               | Value     |
| ----------------------- | ------------------------------------------------------------------------- | --------- |
| `INVITE_LOCK_ID`        | The identifier value for the lock applied to root account of a new member | `fill-in` |
| `MAX_NUMBER_OF_WORKERS` |                                                                           | `fill-in` |

## Metadata

### Membership

| Field             | Type                 | Label    | Description                                               |
| ----------------- | -------------------- | -------- | --------------------------------------------------------- |
| name              | string               | optional | Member's real name                                        |
| about             | string               | optional | Member's md-formatted about text                          |
| avatar\_uri       | string               | optional | Member's avatar - uri to the avatar                       |
| externalResources | `ExternalResource[]` | optional | 0f0e7832a35641a5be498f5d73804b6f                          |
| Field             | Type                 | Label    | Description                                               |
| ResourceType      | enum                 | optional | One of: `EMAIL`, `HYPERLINK`, `DISCORD`, `GTIHUB`, etc... |
| value             | string               | optional | Member's identifier for the given service                 |

| Field        | Type   | Label    | Description                                               |
| ------------ | ------ | -------- | --------------------------------------------------------- |
| ResourceType | enum   | optional | One of: `EMAIL`, `HYPERLINK`, `DISCORD`, `GTIHUB`, etc... |
| value        | string | optional | Member's identifier for the given service                 |

## Validator Verification

In order to help in the nominating process, each validator account can be bond to a Membership which will constitute profile for this validator account. These profiles are presented in Pioneer as information contained in the bound memberships, plus a "verified" status, and a few generated statistics. To in order to complete a their profile each Validator needs to:

1. Bind their Validator account to a membership through Pioneer. On chain this will be achieved by calling:

   * First: `members.addStakingAccountCandidate(memberId)`. This transaction will be signed with either: the validator controller account (what Pioneer will be recommending), or the validator stash account (which will be viable once Joystream adds supports for [Proxy Accounts](https://wiki.polkadot.network/docs/learn-proxies)).
   * Second: `members.confirmStakingAccount(memberId, account)` Here the `account` is the one used to sign the previews transaction. This transaction is signed with the membership controller account.

   This step will done in Pioneer in two way:

   * **Existing membership case:** If the binding is done to an existing membership, the "Edit membership" modal is used. In this case Pioneer will ask the validator to sign `members.addStakingAccountCandidate` first, then a batch transaction calling `members.updateProfile` and `members.confirmStakingAccount`. The option of binding several validator accounts to the same membership is offered by the design. In this case for each account to bind one `addStakingAccountCandidate` will have to be signed, after what all `confirmStakingAccount` can be batched together with the `updateProfile` call.
   * **New membership case:** If the binding is done to a new membership, the "Add membership" modal should be used. Here the membership will have to be created first by signing a `members.createMember` transaction, only then `members.addStakingAccountCandidate` can be signed, and finally `members.confirmStakingAccount` is signed last. In this case the membership is still created first then `addStakingAccountCandidate` is called for each account and finally all `confirmStakingAccount` are batched together.
2. Once the Profile is bound it should be manually verified by the Membership Working Group. The profile should be set to "verified" only if: the membership social links, about section, and any other information mentioned in the membership data is genuine and actually describes the person or company operating the validator account. It is achieved by either a worker signing a `membershipWorkingGroup.workerRemark` transaction or the lead signing `membershipWorkingGroup.leadRemark`. These transactions will be called via a CLI command to be created. Both of these extrinsics are simply sending a message of type `RemarkMetadataAction`. This message only takes one `action` field which in the case of this verification is a `VerifyValidator` message has follow:

   | Field        | Type   | Label    | Description                                          |
   | ------------ | ------ | -------- | ---------------------------------------------------- |
   | member\_id   | uint64 | required | Id of the membership                                 |
   | is\_verified | bool   | required | Whether the profile should be verified or unverified |

> \[!WARNING] Whenever `updateProfile` is called for a membership bond to a validator the "verified" status is reset to `false` and step 2 has to be repeated.

## Extrinsics

### Buy a Membership

**Parameters**

| Name                 | Description                                                                                                                    |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `root_account`       | To be root account of membership.                                                                                              |
| `controller_account` | To be controller account of membership.                                                                                        |
| `handle`             | To be handle of membership.                                                                                                    |
| `metadata`           | Encoded [membership metadata](https://github.com/Joystream/handbook/blob/master/system/memberships/broken-reference/README.md) |
| `referer_id`         | Optional identifier of some existing member.                                                                                   |

#### Conditions

* Free balance of the signer account exceeds `membership_price`.
* `handle` must be unique among all existing handles.
* If provided, `referer_id` must correspond to an existing member.

#### Effect

* A new membership is created with the provided information, and initial invites set to `default_invite_count`.
* If `referer_id` is provided, then the corresponding member has`X = membership_price * referral_cut / 100%` credited to their controller account and `membership_price - X` is burned, otherwise `membership_price` is burned.

### Invite a Member

**Parameters**

| Name                 | Description                                                                                                                    |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `member_id`          | Identifier of inviting member.                                                                                                 |
| `root_account`       | To be root account of membership.                                                                                              |
| `controller_account` | To be controller account of membership.                                                                                        |
| `handle`             | To be handle of membership.                                                                                                    |
| `metadata`           | Encoded [membership metadata](https://github.com/Joystream/handbook/blob/master/system/memberships/broken-reference/README.md) |

#### Conditions

* Signer matches controller account of member corresponding to `member_id`.
* Invitation quota of member is non-zero.
* `handle` must be unique among all existing handles.
* Working group budget is no less than `invited_initial_balance`.

#### Effect

* A new membership is created with the provided information, and initial invites set to zero, and the root account is credited with`invited_initial_balance`.
* Working group budget is deducted by `invited_initial_balance`.
* `controller_account` is encumbered with lock with ID `INVITE_LOCK_ID` and of size `invited_initial_balance` that only allows transaction fees, and credited with the same amount of tokens.
* Invite quota of member corresponding to `member_id` is decremented.

### Update Profile

**Parameters**

| Name           | Description                                                                                                                                                                            |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `member_id`    | Identifier of member wishing to update profile.                                                                                                                                        |
| `handle`       | Optional new handle for membership.                                                                                                                                                    |
| `new_metadata` | Optional new encoded [membership metadata](https://github.com/Joystream/handbook/blob/master/system/memberships/broken-reference/README.md) (only the provided fields will be updated) |

#### Conditions

* Signer matches controller account of member corresponding to `member_id`.
* if set `handle` must be unique among all existing handles.
* at least one of the optional fields must be set.

#### Effect

Profile of member corresponding to `member_id` is updated with new field values.

### Transfer Invites

**Parameters**

| Name                  | Description                                       |
| --------------------- | ------------------------------------------------- |
| `member_id`           | Identifier of member wishing to send invites.     |
| `recipient_member_id` | Identifier of member to be credited with invites. |
| `number_of_invites`   | Number of invites to transfer.                    |

#### Conditions

* Signer matches controller account member corresponding to `member_id`.
* `recipient_member_id` corresponds to a existing recipient member.
* `number_of_invites` is no greater than invitation quota of sender.

#### Effect

`number_of_invites` is transferred from invite quota of sender to recipient.

### Update Accounts

**Parameters**

| Name                 | Description                                       |
| -------------------- | ------------------------------------------------- |
| `member_id`          | Identifier for member wishing to update accounts. |
| `root_account`       | Optional new root account for membership.         |
| `controller_account` | Optional new controller account for membership.   |

#### Conditions

* Signer matches the `root_account` corresponding to `member_id`.
* At least one new account is provided.

#### Effect

Update provided accounts on member.

### Update Verified Status

**Parameters**

| Name          | Description                                  |
| ------------- | -------------------------------------------- |
| `worker_id`   | Identifier of membership evangelist.         |
| `member_id`   | Identifier of member to have status updated. |
| `is_verified` | New status of member.                        |

#### Conditions

* Signer matches controller account of worker corresponding to `worker_id`.
* `member_id` corresponds to some existing member.

#### Effect

Verification status of member is set to `is_verified`.

### Bind Staking Account

**Parameters**

| Name        | Description                           |
| ----------- | ------------------------------------- |
| `member_id` | Identifier of member to bind account. |
| `account`   | Account to be bound.                  |

#### Conditions

* Signer matches controller account of member corresponding to `member_id`.
* `account` is not already bound to any other member.

#### Effect

`account` is bound to member.


# Council

The politically selected governance body responsible for managing the proposal system for the benefit of voters.

## Introduction

The council is a fixed size committee, up for election at regular intervals by token holders, tasked with the role of voting on proposals in the proposal system. At the heart of the governance process on the platform is the proposal system, which allows anyone to submit some suggestion for changing the state or policy of the platform in some way. These proposals are processed and voted on by a council, where the participants are referred to as council members. A seat on the council is won through an election process, and lasts for some period of time until a new election.

## Notion Space

The community maintains a distinct Notion space which holds more dynamic information on the activities of the council.

{% embed url="<https://joystream.notion.site/Council-809dd7bcf6744844ba0dcccf98670dcc>" %}

## Council Member

### Responsibilities

* Discuss the meaning and merits of incoming proposals, covering a broad range of topics
* Vote on proposals
* Represent the community members and your constituency to make day-to-day operations decisions

### Requirements

* Proficiency with basic data analysis and sufficient reputation and standing within the community to earn supporting votes in elections from other platform members
* A deep understanding of the Joystream platform structure, function and resource allocation
* Hold sufficient amount of the native platform token to put at stake

## Roles

The relevant roles in the council system are

* **Voters:** Anyone who stakes for the purposes of influencing the outcome of an election. Is not tied to membership, so a member can vote multiple times for different candidates from different accounts.
* **Candidate:** A member who has staked and stands as an alternative for councilorship in an ongoing election cycle.
* **Councilor:** A member who has stood as a candidate in an election and won a place in a council. Has the primary responsibility to participate in voting on and deliberating around proposals in the proposal system.

## Concepts

### Staking

The main idea behind how stake is managed is that we want to allow reuse of stake in ways that encourage participation, so specifically

1. any stake that has been deployed for voting, should be reusable for any other purpose, and vice versa.
2. stake used to sit on the council should be reusable for being a candidate in the next.

One easy way to manage reuse across purposes, where the new use may require more or less stake than the existing use, is to take advantage of the non-stacking property of locks. For this reason, there are three kinds of locks associated with the council, as described below.

#### Voting

When voting, one votes simply with an account, and it does not matter what other locks exist, except for voting, on that account, whatever amount you lock fully counts towards the election outcome. A single member can of course vote multiple times with different accounts on different candidates.

If someone voted for a candidate in an election, they will and can free their stake at a later time. Importantly, a vote which was devoted to a losing candidate can be freed the moment the election cycle is over, while a vote which was devoted to a winner can only be freed after the announcing period of the next election begins. The idea behind this asymmetry is to more closely expose the winners to the consequences of their decision.

#### Candidacy

When announcing candidacy, one does so as a member. One has to stake with an account bound to the membership, and, this account can only be simultaneously locked for voting in some past election, using lock id `VOTING_LOCK_ID`. If someone announced their candidacy in an election, but did not end up winning, then they can at any time after the conclusion of that election cycle free their stake.

#### Council

When winning an election, your candidacy lock will be automatically removed, and a council specific lock will be applied, with the same amount locked. When that council is replaced, this lock is removed, if you did not get re-elected.

### Budget

The budget of the council is the root resource pool for all token minting on the platform, *with the exception of validator rewards*. The lifetime of the budget is divided into periods, called *budget periods*, which occurs every `BUDGET_PERIOD_LENGTH` blocks. The number of tokens added to the budget at the end of each such period is held in a mutable parameter denoted as `budget_increment` .

Whenever one of the following actions occur, the budget is impacted as described.

| Event                                     | Budget Impact       |
| ----------------------------------------- | ------------------- |
| Working group budget increased by `X > 0` | `-X`                |
| Working group budget decreased by `X > 0` | `+X`                |
| Spending proposal with amount `X > 0`     | `-X`                |
| Council reward payout `X > 0`             | `-X`                |
| Budget period ends                        | `+budget_increment` |

Events that negatively impact the budget balance can only occur if the impact does not occur if they exceed the balance of the budget.

*Notice that working group spending, such as lead spending, or subsystem specific spending, such as minting initial credit for invited new members, does not directly count against the council budget, but against the relevant working group budget.*

### **Council**

The council has a fixed number of seats `NUMBER_OF_COUNCIL_SEATS` occupied by members, called *councilors*. The seats are always occupied, allowing the platform to dispose of all proposals they may come in at any time. The council body has two high level states described as follows.

* **Normal:** During this stage the council operates normally. After `NORMAL_PERIOD_LENGTH` blocks have passed since this period started, a transition is made to the election stage.
* **Election:** During this stage, not only does the council operate, but there is an election ongoing. Read more about elections the [Council](https://github.com/Joystream/handbook/blob/master/system/council/broken-reference/README.md) section below.

#### Rewards

Every `REWARD_PERIOD_LENGTH` blocks all councilors are paid out the same flat reward rate and any possibly outstanding owed reward. This rate is held in a mutable parameter, called the *councilor reward*, denoted as `councilor_reward` . During this payout, where councilors are processed in some consistent order, the crediting only occurs while the budget constraint is respected. For each payout, the constraint is tightened. If a councilors cannot be paid out in full, then the difference is added to their owed reward. When a council period ends, any owed reward and outstanding reward from the last payout, are attempted paid out, however if the budget does not allow it, then the councilor suffers the loss.

### Candidacy

A candidacy is defined by the following information

* **Member:** The member behind the candidacy.
* **Program:** A human readable description of the candidacy. Some socially enforced schema for the encoding of the program.
* **Cycle Id:** The election cycle to which this candidacy corresponds.
* **Staking account:** The account holding the stake for the candidate. After announcing the staking account will have locked up `REQUIRED_CANDIDACY_STAKE` under the relevant council lock. If the candidacy fails - either because the election cycle fails or the candidate receives too few votes, then this lock can be removed by the candidate, otherwise it remains on into the councilorship. Be aware that this stake contributed towards the candidacy does *not* contribute towards the final election outcome, hence exceeding the minimum bound would only be done for signaling or other social purposes.
* **Votes Received:** The total amount token votes received by this candidate in revealing period ofthe election, is zero before that time.

Note that, while there is no explicit identifier, a candidacy can be implicitly identified by a combination of the member, the order of this announcement for this member - as one could in principle announce and withdraw multiple times, and finally the election cycle number.

### Councilor

A councilor is defined by the following information

* **Member:** The membership to which this role corresponds.
* **Role account**: The account currently used to authenticate as this role.
* **Reward account**: The destination account to which periodic rewards are paid out.
* **Staking account:** Holds the stake currently associated with the role. Locks`REQUIRED_CANDIDACY_STAKE` under the relevant council lock which is recoverable when councilorship ends.
* **Owed reward:** The total reward this councilor was not paid over a number of payout periods where there was not sufficient funds in the council budget.

Notice that, while there is no explicit identifier, a councilorship can be implicitly identified by a combination of the member and the election cycle number.

### Vote

A vote is a defined by the following

* **Staking account:** Holds the stake associated with the vote. A given account can only be involved in a single vote for a given election cycle (see [Council](https://github.com/Joystream/handbook/blob/master/system/council/broken-reference/README.md)).
* **Staking balance:** The amount of funds in the staking account encumbered for this vote, will be no less than `MINIUMUM_VOTING_STAKE`.
* **Cycle Id:** The election cycle in which the vote was cast.
* **Stage:** The vote has two stages, being *sealed* and *unsealed, each having the following associated information*
  * **Sealed:** This is the initial stage when a vote is submitted during a the voting period of an election. The only information available is called a voting commitment, which is a **opaque hash digest**.
  * **Unsealed:** This is the stage which occurs if the voter chooses to reveal the their sealed vote during the revealing stage. This has stage has information about a **valid candidate**, and a **nonce**, which when concatenated together are the pre-image of the initial hash digest.

Notice that, while there is no explicit identifier, a vote can be implicitly identified by a combination of the staking account and the election cycle number.

Unlocking the voting lock on the staking account requires an active recovery action on the voter, and it follows the following rules

* If the vote is for an ongoing election, then it is not recoverable.
* If the vote is for the last concluded election and one is still in the immediately following idle period, then it is recoverable only if it was unsealed in favor of a losing candidate, otherwise it is not.
* If the vote is for any election before the last concluded, the it is always recoverable.

### Election

An election is the periodic process by which a new council is selected by voters among candidates running for a seat on the next council. Elections occur periodically, and each one has a sequence of stages referred to as the election cycle. Each cycle is identified with an id, called the *election cycle id,* which is just the cycle number. An election will begin while the current council is active, and the sitting council is only relieved once a new one has been successfully elected. As will become clear, this process can go on for a unknown amount of time. The election cycle has the following stages

* **Announcing Period:** This is the first stage in the election cycle. During this time members can announce that they will stand as candidates for the next council. Such an announcement can later be withdrawn within this same period, without consequences. The same member can only have a single candidacy active at any given time, but can in principle announce and withdraw an unlimited number of times. Importantly, if less than the minimum number of candidates have announced by the end of this period, a new election cycle starts. All candidates can recover their stake from such a failed cycle instantly, but it requires action, and anyone wanting to stand for the next election will need to announce again.
* **Voting Period:** This is the stage where voters can submit votes in favor of candidates. The votes are sealed, meaning that it is only known that some account voted for an unknown, possibly invalid candidate, with a known amount of tokens.
* **Revealing Period:** During this stage, voters can reveal their sealed votes. Any valid vote which is unsealed is counter, and in the end a winning set of candidates is selected. Importantly, even if there is an insufficient number of valid votes revealed to render a set of winners with non-zero backing stake, the runtime will just pick a winning set deterministically. Importantly, if less than `NUMBER_OF_COUNCIL_SEATS` candidates receive at least one vote by the end of this period, a new election cycle starts. All candidates can recover their stake from such a failed cycle instantly, but it requires action, and anyone wanting to stand for the next election will need to announce again. Lastly, any outstanding owed reward is not carried over between different council.

The stages and transitions, are summarized in the figure below.

![Election life-cycle stages.](/files/7fGlemH6GJNeTVsp00RN)

## Constants

The following constants are hard coded into the system, they can only be updated with a runtime upgrade.

| Name                         | Description                                                                              | Value              |
| ---------------------------- | ---------------------------------------------------------------------------------------- | ------------------ |
| `NUMBER_OF_COUNCIL_SEATS`    | The number of council seats.                                                             | 3                  |
| `IDLE_PERIOD_LENGTH`         | The number of blocks in the normal period.                                               | 201 600            |
| `ANNOUNCING_PERIOD_LENGTH`   | The number of blocks in the announcing period                                            | 86 400             |
| `VOTING_PERIOD_LENGTH`       | The number of blocks in the voting period.                                               | 57 600             |
| `REVEALING_PERIOD_LENGTH`    | The number of blocks in the revealing period.                                            | 57 600             |
| `REWARD_PERIOD_LENGTH`       | <p>The number or blocks between each reward<br>payout to councilors.</p>                 | 14 400             |
| `BUDGET_PERIOD_LENGTH`       | <p>The number of blocks between each time the the</p><p>council budget is topped up.</p> | 14 400             |
| `REQUIRED_CANDIDACY_STAKE`   | The required amount of stake for a candidate.                                            | 166 666.67 JOY     |
| `MINIMUM_VOTING_STAKE`       | The minimum allowable stake in a vote.                                                   | 166.67 JOY         |
| `MAX_SALT_LENGTH`            | The maximum length of salt is used to calculate a vote's sealed commitment.              | 32                 |
| `MINIMUM_CANDIDATES_COUNT`   | The minimum number of candidates needed for the election to become legitimate.           | 1                  |
| `VOTING_LOCK_ID`             | The Id for the lock used to vote.                                                        | 0x766f74696e672020 |
| `CANDIDACY_LOCK_ID`          | The Id for the lock used for candidacy.                                                  | 0x63616e6469646163 |
| `COUNCILOR_LOCK_ID`          | The Id for the lock used for councilorship.                                              | 0x636f756e63696c6f |
| Last Update Date: 2024-03-13 |                                                                                          |                    |

## Extrinsics

### Announce Candidacy

#### Parameters

| Name                 | Description                                                              |
| -------------------- | ------------------------------------------------------------------------ |
| `membership_id`      | Membership id uniquely identifying the user.                             |
| `staking_account_id` | Staking account.                                                         |
| `reward_account_id`  | Account receiving councilors rewards in the case candidate gets elected. |
| `stake`              | Amount of currency user wants to stake for the candidacy.                |

#### Conditions

* Signer is controller account of member with identifier `membership_id`.
* There is an active election in the **Announcement Period**.
* There is no prior candidacy in this election for `membership_id`.
* `staking_account_id` has no conflicting locks (see [Staking](https://github.com/Joystream/handbook/blob/master/system/council/broken-reference/README.md)).
* `staking_account_id` has enough balance to be locked as candidacy stake.
* `staking_account_id` is associated with the member.
* The `stake` must be at least `REQUIRED_CANDIDACY_STAKE`.

#### Effect

Any past candidacy and lock is removed, and a new candidacy is created for the given election cycle, and a candidacy lock is applied with the amount `stake`.

### Withdraw Candidacy

#### Parameters

| Name            | Description                                  |
| --------------- | -------------------------------------------- |
| `membership_id` | Membership id uniquely identifying the user. |

#### Conditions

* Signer is controller account of member with identifier `membership_id`.
* There is an active election in the **Announcement Period**.
* The member has announced candidacy for this election cycle.

#### Effect

The candidacy and candidacy lock is removed.

### Submit Sealed Vote

#### Parameters

| Name         | Description                                          |
| ------------ | ---------------------------------------------------- |
| `commitment` | The sealed vote representation.                      |
| `stake`      | Amount of currency user wants to stake for the vote. |

#### Conditions

* Is signed with some account `staking_account_id`.
* There is an active election in **Voting Period**.
* The `staking_account_id` account has no associated vote for the current election cycle.
* The `staking_account_id` total balance no less than `stake`.
* The `stake` must be at least `MINIUMUM_VOTING_STAKE`.

#### Effect

Any possible vote and corresponding voting lock from a prior election is removed. A sealed vote for the current cycle is created, including `commitment`.

### Reveal Vote

#### Parameters

| Name           | Description                                    |
| -------------- | ---------------------------------------------- |
| `salt`         | The salt used to verify the sealed commitment. |
| `candidate_id` | Member identifier for candidate.               |

#### Conditions

* There is an active election in the **Revealing Period**.
* Is signed with some account `staking_account_id` which has vote in the current election cycle.
* Vote is in **Sealed** stage.
* `salt`length is not higher than `MAX_SALT_LENGTH`.
* `candidate_id` \_\_identifies a candidate in the current election.
* The commitment in the vote is verified to correspond to the provided `salt` and `candidate_id`.

#### Effect

The amount of the voting lock is added to votes received of the candidate, and the vote has stage updated to **Unsealed**.

### Recover Voting Stake

#### Parameters

None.

#### Conditions

* Is signed with some account `staking_account_id` which has vote.
* Vote is either for
  * a prior election cycle
  * the current election cycle, and is **Unsealed** for a candidate which did not make it into the council

#### Effect

Voting lock is removed from account and vote is removed.

### Recover Failed Candidacy Stake

#### Parameters

| Name           | Description                      |
| -------------- | -------------------------------- |
| `candidate_id` | Member identifier for candidate. |

#### Conditions

* Signer matches controller account of member identified with `candidate_id`.
* Member has existing candidacy.
* If candidacy is for the current election cycle, the there election must be in the idle phase.

#### Effect

Candidate lock is removed from staking account of candidate, and candidate is removed.

### Submit Candidacy Note

#### Parameters

| Name            | Description                                                                                                                                                                                            |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `membership_id` | Membership identifier.                                                                                                                                                                                 |
| `note`          | Encoded [candidacy note](https://github.com/Joystream/handbook/blob/master/system/council/broken-reference/README.md) metadata (subsequent calls will only affect explicitly provided metadata fields) |

#### Conditions

* Signer is controller account of member corresponding to `membership_id`.
* Member has existing candidacy.
* The candidacy is for the current election cycle, and the election phase is not idle.

#### Effect

The note is associated with a candidate.


# Working Groups

Working groups organize subcommittees of incentivized and staked contributors around making a subsystem of the platform to work.

## Introduction

A working group is an organizational body, subject to the oversight of the council, which is responsible for the day-to-day functioning of some subsystem of the platform. There is exactly one working group per subsystem. The rationale for having a working group for this purpose, rather than having the council directly involved, has three parts. First, since all council members are supposed to be fully informed on all matters the cumulative workload of overseeing all subsystems would not be feasible for a single council. Second, even if it was feasible, voting is not a sound means of making such decisions, because there is a lack of guaranteed coherence in the decisions over time. Third, each subsystem will over time likely require a differentiated skill set, knowledge base and social capital. The appropriate analogy for understanding the role of the working groups in the overall operation of the system would be a commission or agency body in a political institution.&#x20;

## Groups

{% content-ref url="/pages/D9WVldX3pBCYnv9OEL9u" %}
[Human Resources](/system/human-resources)
{% endcontent-ref %}

{% content-ref url="/pages/cXaBfasXNVOEXvMzIr5U" %}
[Builders](/system/builders)
{% endcontent-ref %}

{% content-ref url="/pages/ChK7iZptJiUTa94iyoOd" %}
[Content Directory](/system/content-directory)
{% endcontent-ref %}

{% content-ref url="/pages/7xa27KLaLIjEP5k0K5tN" %}
[Marketers](/system/marketers)
{% endcontent-ref %}

{% content-ref url="/pages/DEsIljvjuxzVi1sMQdHX" %}
[Storage & Bandwidth](/system/storage)
{% endcontent-ref %}

{% content-ref url="/pages/Bxda8mwShXdLh2hnSAkP" %}
[Applications](/system/gateways)
{% endcontent-ref %}

## Operations Working Groups

Working groups which do not have dedicated on-chain subsystem, such as the content directory or storage system, are called *operations working groups*. They mainly exist to coordinate people are activity purely, and currently we have the following groups of this kind

* Builders
* Human Resources
* Marketers

## Roles

The relevant roles in a working group are

* **Applicant:** A member who has submitted an application to join an opening for a worker role in the working group. A given member may apply more than once to a given opening, and also if they already occupy the role as worker the same group. Openings are created by the lead (see below), or by the council when wanting to fill the lead role.
* **Worker:** A member who has, through an application, entered the working group.The worker may or may not be staked, and is receiving payouts to a designated account at regular intervals. The worker role gives some ability to act in a domain specific way within the given subsystem. So for example in the context of the forum, a worker in the forum working group can be assigned to be a moderator in certain forum categories, and have associated moderation privileges. Lastly, a member may act as multiple works simultaneously, or over time, in the same working group.
* **Lead:** A designated worker who is responsible for hiring and managing the other workers, as well as allocating funds from a budget towards purposes that support the success of the subsystem. Also the leader could set the general working group status, like:
  * a one line status message on the subsystem,
  * new upcoming expected positions,
  * a link to a subsection of the forum devoted to the subsystem,
  * a message feed including information updates.

## Concepts

### Worker

A has the following information associated

* **Id:** A unique non-negative integer identifier.
* **Membership**: The membership to which this role corresponds. Comes from the initial application to the opening by which worker is hired.
* **Role account**: The account currently used to authenticate as this role in the relevant subsystem. Authentication in the working group is done using the controller account of the member, so as to allow for division of labor behind a single membership across multiple roles, while not requiring full trust. Is updatable by member.
* **Staking profile:**
  * **Staking account:** Holds the stake currently associated with the role. .
  * **Leaving unstaking period:** The number of blocks required from a worker initiating leaving the group until their staked funds are unlocked.
* **Reward account**: The destination account to which periodic rewards are paid out.

  All roles have the following information associated.
* **Reward rate per block:** The number of tokens the worker earns per block, although payouts do not occur per block, but every `REWARD_PAYOUT_PERIOD` blocks. This is earned for every block from being hired to being terminated, or initiating leaving the group. It is not earned during unstaking.
* **Owed reward:** The total reward this worker was not paid over a number of payout periods where there was not sufficient funds in the working group budget.
* **Unstaking status:** Is either *normal*, or *unstaking*. The initial status is the former, and the latter is only entered into when the worker attempts to leave.

### Lead

A designated worker may be identified as the lead at any time, let `current_lead` represent the worker id of this worker when set. It is the role of the council to manage what worker, if any, is the lead in a given working group.

### Budget

The budget is the root resource pool for all token minting in the working group, and the size of the pool is denoted by `budget`. The budget can be increased or reduced by the council. Whenever rewards are paid, or the leader does discretionary spending, it drains the budget, and these events can only take place if the budget allows it. There may be other additional subsystem specific expenditures that depend on the budget, such as minting initial balances for new invited members in the membership system.

### Rewards

All workers are paid every `REWARD_PAYOUT_PERIOD` blocks, and each worker is to be credited according to their own reward rate, and any possibly outstanding owed reward. During this payout, where workers are processed in some consistent order (for a given set of workers), the crediting only occurs while the budget is respected. Also, workers unstaking are ignored. For each payout, the budget is tightened. If a worker cannot be paid out in full, then the difference is added to their owed reward. The budget will then have to be reset by the council. When a worker is terminated, or leaves, any owed reward and outstanding reward from the last payout, are attempted paid out, however if the budget does not allow it, then the worker suffers the loss.

### Spending

In addition to rewards, the lead can spend from this budget for arbitrary purposes, to fund expenses and initiatives that are in line with the purpose of the group.

### Staking

Worker roles require staking in order to apply and remain in the role. Staking for worker roles is done using a designated working group lock on a single account per worker role. The amount required is set by the discretion of the lead. The staking requirement could be decreased by the lead (or by the council for leaders). The worker is able to increase their own stake, for example, in response for leader demand. Consult the [Staking](https://github.com/Joystream/handbook/blob/master/system/working-groups/broken-reference/README.md) article to see a list of other staking purposes, and corresponding locks, which can be combined with staking for a given working group.

#### Staking Policy

A *staking policy* describes the requirements involved in staking for a role. It has two components, it has an *amount*, the minimum number of tokens required to stake, and a *leaving unstakig period,* the unstaking period incurred form the time a worker initiates leaving the group.

### Slashing

Slashing is initiated by the council, or the lead, by pure discretion. The full staked amount is at risk of getting slashed, but need not be, and there is no limit to the number of times one may get slashed. Importantly, slashing can also occur while a worker is unbonding. This is of particular importance to avoid a lead attempting to leave the role the moment a slashing proposal is observed. Such last minute exits cannot avoid slashing so long as the unbonding period chosen by the council for the lead, is sufficiently long compared to the inherent delays in the proposal system. There is a similar, but much less severe, concern about workers trying to race with slashing transactions submitted by the lead by attempting to preempt slashing by observing the pool of unconfirmed transactions. The unbonding period required to make this infeasible can be much shorter, essentially just however long is required to have a sufficiently high certainty that the slashing transaction is included in a block.

### Hiring

Hiring is the process by which a worker enters the group. Both normal workers and the lead worker, are hired, in the latter case the council initiates the hiring. Hirings are organized into *openings*, where zero or more *applicants* may be selected as winners, and becoming workers. An opening for the lead position will always result in hiring at most one worker, and this worker becomes the lead.

#### Application

An application has the following information

* **Id:** A unique immutable non-negative integer identifying an individual application across all openings, is automatically assigned when an application is created.
* **Role account:** A required account that is used to authenticate as the worker if selected, in other parts of the platform. Need not be unique across workers, but in practice probably will be.
* **Staking account:** The account holding the stake of the application.
* **Member:** Identifier of member from which application originates.
* **Description:** [Application description metadata](https://github.com/Joystream/handbook/blob/master/system/working-groups/broken-reference/README.md).

#### Opening

An opening has the following information associated

* **Id:** A unique immutable non-negative integer identifying an individual opening, is automatically assigned when an opening is created.
* **Type:** Whether the opening is for the lead or for a non-lead worker.
* **Description:** [Opening description metadata](https://github.com/Joystream/handbook/blob/master/system/working-groups/broken-reference/README.md).
* **Staking policy:**
  * **Balance:** The required non-zero balance required.
  * **Leaving unstaking period:** The number of blocks required from a worker initiating leaving the group until their staked funds are unlocked.
* **Applications:** All applications created, but not yet withdrawn.

### Status

A working group has an associated *status* that is updatable by the lead. The status is text signal, denoted by `status`, which follows some yet to be standardized encoding.

## Constants

Hard-coded values are defined *for each working group*, and they can only be altered with a runtime upgrade.

| Name                         | Description                                                                                |
| ---------------------------- | ------------------------------------------------------------------------------------------ |
| `MAX_NUMBER_OF_WORKERS`      | The maximum number of workers that can be part of the working group simultaneously.        |
| `REWARD_PAYOUT_PERIOD`       | The number of blocks between each time workers are paid their total reward for the period. |
| `LOCK_ID`                    | The Id for the lock used to stake in this working group.                                   |
| `MIN_UNSTAKING_PERIOD_LIMIT` | Minimum unstaking period in this working group.                                            |
| `MINIMUM_STAKE_FOR_OPENING`  | Minimum stake required for any opening.                                                    |

## Metadata

### Working Group Action <a href="#working-group-action" id="working-group-action"></a>

\*\*Only one of the fields should be set.\*\*​

| Field                     | Type                                            | Label    | Description |
| ------------------------- | ----------------------------------------------- | -------- | ----------- |
| set\_group\_metadata      | [SetGroupMetadata](#setgroupmetadata)           | optional |             |
| add\_upcoming\_opening    | [AddUpcomingOpening](#addupcomingopening)       | optional |             |
| remove\_upcoming\_opening | [RemoveUpcomingOpening](#removeupcomingopening) | optional |             |

### SetGroupMetadata

| Field                  | Type                                          | Label    | Description                                                 |
| ---------------------- | --------------------------------------------- | -------- | ----------------------------------------------------------- |
| new\_metadata          | [WorkingGroupMetadata](#workinggroupmetadata) | optional | New working group metadata to set (can be a partial update) |
| ### AddUpcomingOpening |                                               |          |                                                             |

### RemoveUpcomingOpening

| Field | Type   | Label    | Description                    |
| ----- | ------ | -------- | ------------------------------ |
| id    | string | optional | Upcoming opening query-node id |

### UpcomingOpeningMetadata

| Field                   | Type                                | Label    | Description                        |
| ----------------------- | ----------------------------------- | -------- | ---------------------------------- |
| expected\_start         | uint32                              | optional | Expected opening start (timestamp) |
| reward\_per\_block      | uint64                              | optional | Expected reward per block          |
| min\_application\_stake | uint64                              | optional | Expected min. application stake    |
| metadata                | [OpeningMetadata](#openingmetadata) | optional | Opening metadata                   |
| Field                   | Type                                | Label    | Description                        |
| expected\_start         | uint32                              | optional | Expected opening start (timestamp) |
| reward\_per\_block      | uint64                              | optional | Expected reward per block          |
| min\_application\_stake | uint64                              | optional | Expected min. application stake    |
| metadata                | [OpeningMetadata](#openingmetadata) | optional | Opening metadata                   |

| Field                   | Type                                | Label    | Description                        |
| ----------------------- | ----------------------------------- | -------- | ---------------------------------- |
| expected\_start         | uint32                              | optional | Expected opening start (timestamp) |
| reward\_per\_block      | uint64                              | optional | Expected reward per block          |
| min\_application\_stake | uint64                              | optional | Expected min. application stake    |
| metadata                | [OpeningMetadata](#openingmetadata) | optional | Opening metadata                   |

### Working Group Status

| Field           | Type   | Label    | Description                                     |
| --------------- | ------ | -------- | ----------------------------------------------- |
| description     | string | optional | Group description text (md-formatted)           |
| about           | string | optional | Group about text (md-formatted)                 |
| status          | string | optional | Current group status (expected to be 1-3 words) |
| status\_message | string | optional | Short status message associated with the status |

### Working Group Opening Metadata

| Field                        | Type                                                | Label    | Description                                                  |
| ---------------------------- | --------------------------------------------------- | -------- | ------------------------------------------------------------ |
| short\_description           | string)                                             | optional | Short description of the opening                             |
| description                  | string                                              | optional | Full description of the opening                              |
| hiring\_limit                | uint32                                              | optional | Expected number of hired applicants                          |
| expected\_ending\_timestamp  | uint32                                              | optional | Expected time when the opening will close (Unix timestamp)   |
| application\_details         | string                                              | optional | Md-formatted text explaining the application process         |
| application\_form\_questions | [ApplicationFormQuestion](#applicationformquestion) | repeated | List of questions that should be answered during application |

### ApplicationFormQuestion

| Field    | Type                    | Label    | Description                                     |
| -------- | ----------------------- | -------- | ----------------------------------------------- |
| question | string                  | optional | The question itself (ie. "What is your name?"") |
| type     | [InputType](#inputtype) | optional | Suggested type of the UI answer input           |

### InputType

| Name     | Number | Description |
| -------- | ------ | ----------- |
| TEXTAREA | 0      |             |
| TEXT     | 1      |             |

### Working Group Application Metadata <a href="#working-group-application-metadata" id="working-group-application-metadata"></a>

| Field   | Type   | Label    | Description                                           |
| ------- | ------ | -------- | ----------------------------------------------------- |
| answers | string | repeated | List of answers to opening application form questions |

### Council Candidacy Note

| Field              | Type   | Label    | Description                                |
| ------------------ | ------ | -------- | ------------------------------------------ |
| header             | string | optional | Candidacy header text                      |
| bullet\_points     | string | repeated | Candidate program in form of bullet points |
| banner\_image\_uri | string | optional | Image uri of candidate's banner            |
| description        | string | optional | Candidacy description (md-formatted)       |

## Extrinsics

### Creating an Opening for Workers

**Parameters**

| Name               | Description                                                                                                                                 |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `description`      | Endoded [opening description](https://github.com/Joystream/handbook/blob/master/system/working-groups/broken-reference/README.md) metadata. |
| `staking_policy`   | Staking policy of new opening.                                                                                                              |
| `reward_per_block` | Initial per reward block.                                                                                                                   |

#### Conditions

* A lead worker is set.
* Signer uses role account of lead worker.
* `stake` for `staking_policy` is equal to or more than `MINIMUM_STAKE_FOR_OPENING`
* `leaving_unstaking_period` is more than `MIN_UNSTAKING_PERIOD_LIMIT`

#### Effect

A new opening is added with the given information and for hiring a worker, not lead.

### Apply on Opening

**Parameters**

| Name              | Description                                                                                                                                     |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `member_id`       | Member identifier.                                                                                                                              |
| `opening_id`      | Identifier of opening being applied to.                                                                                                         |
| `role_account`    | Role account of future worker.                                                                                                                  |
| `staking_account` | Account holding stake.                                                                                                                          |
| `staking_balance` | Balance to stake.                                                                                                                               |
| `description`     | Encoded [application description](https://github.com/Joystream/handbook/blob/master/system/working-groups/broken-reference/README.md) metadata. |

#### Conditions

* Signer uses controller account of member corresponding to `member_id`.
* `opening_id` corresponds to an existing opening.
* `staking_account`is set and
  * `staking_balance` is no less than balance in staking policy
  * is bound to the member,
  * has free balance no `staking_balance`
  * there are no conflicting staking locks present.

#### Effect

A new application is created for the opening, using the provided information, and `staking_account` has lock with Id `LOCK_ID` and of size`staking_balance` if set, otherwise of staking policy of opening.

### Withdraw Application

**Parameters**

| Name             | Description                                 |
| ---------------- | ------------------------------------------- |
| `application_id` | Identifier for application to be withdrawn. |

#### Conditions

* `application_id` corresponds to an existing application.
* Signer uses role account of of application.

#### Effect

The staking is removed by removing the lock on the staking account. The application is removed.

### Fill an Opening for Workers

**Parameters**

| Name         | Description                                |
| ------------ | ------------------------------------------ |
| `opening_id` | Identifier of opening.                     |
| `winners`    | Set of application identifiers of winners. |

#### Conditions

* A lead worker is set.
* Signer uses role account of lead worker.
* `opening_id` corresponds to existing opening.
* opening is for hiring a worker, not lead.
* all identifiers in `winners` correspond to existing applications.
* Opening type is for worker, and the number of workers in the opening plus the number of `winners` does not exceed `MAX_NUMBER_OF_WORKERS`.

#### Effect

Create a worker for each application in `winners`, each having a reward rate per block associated with the opening and no owed reward, and remove opening.

NB: Notice that all losing applications are still around in order to allow recovering stake later.

### Cancel an Opening for Workers

**Parameters**

| Name         | Description            |
| ------------ | ---------------------- |
| `opening_id` | Identifier of opening. |

#### Conditions

* A lead worker is set.
* Signer uses role account of lead worker.
* `opening_id` corresponds to existing opening.
* opening is for workers, not lead.

#### Effect

The opening is removed.

NB: Notice that all applications are still around in order to allow recovering stake later.

### Update Role Account

**Parameters**

| Name           | Description                 |
| -------------- | --------------------------- |
| `worker_id`    | Worker identifier.          |
| `role_account` | New role account of worker. |

#### Conditions

* `worker_id` corresponds to existing worker.
* Signer uses controller account of member corresponding to member identifier in worker.

#### Effect

Worker role account is updated to `role_account`.

### Update Reward Account

**Parameters**

| Name             | Description                   |
| ---------------- | ----------------------------- |
| `worker_id`      | Worker identifier.            |
| `reward_account` | New reward account of worker. |

#### Conditions

* `worker_id` corresponds to existing worker.
* Signer uses controller account of member corresponding to member identifier in worker.

#### Effect

Worker reward account is updated to `reward_account`.

### Update Reward Amount for Worker

**Parameters**

| Name               | Description                           |
| ------------------ | ------------------------------------- |
| `worker_id`        | Worker identifier.                    |
| `reward_per_block` | New reward rate per block for worker. |

#### Conditions

* A lead worker is set.
* Signer uses role account of lead worker.
* `worker_id` corresponds to existing worker, not lead.

#### Effect

Worker reward rate per block is set to `reward_per_block`.

### Leave Worker Role

**Parameters**

| Name        | Description          |
| ----------- | -------------------- |
| `worker_id` | Worker identifier.   |
| `rationale` | Human readable text. |

#### Conditions

* `worker_id` corresponds to existing worker.
* Signer uses controller account of member corresponding to member identifier in worker.
* worker unstaking status is normal.

#### Effect

* Staking status is set to unstaking - where final removal of worker and staking lock occurs after leaving unstaking period.
* When the worker leaves, if worker has owed reward, then as much as is possible under the current budget is paid out, and whatever could be paid out is used to updated the owed field.

### Terminate Worker

**Parameters**

| Name              | Description                    |
| ----------------- | ------------------------------ |
| `member_id`       | Member identifier.             |
| `slashing_amount` | Optional amount to be slashed. |
| `rationale`       | Human readable text.           |

#### Conditions

* A lead worker is set.
* Signer uses role account of lead worker.
* `worker_id` corresponds to existing worker, not lead.
* If `slashing_amount` is set, then it is greater than zero and the worker
  * staked balance is no less than `slashing_amount`,
  * staking status is normal.

#### Effect

* If worker has owed reward, then as much as is possible is paid out of the current budget, and whatever could be paid out is used to updated the owed field.
* If `slashing_amount` is set, it's slashed from the staking account.
* Worker is removed.

### Slash Worker

**Parameters**

| Name              | Description           |
| ----------------- | --------------------- |
| `worker_id`       | Worker identifier.    |
| `slashing_amount` | Amount to be slashed. |
| `rationale`       | Human readable text.  |

#### Conditions

* A lead worker is set.
* Signer uses role account of lead worker.
* `worker_id` corresponds to existing worker, not lead.
* `slashing_amount` is greater than zero.
* staked balance is no less than `slashing_amount`.

#### Effect

The staking account is slashed by `slashing_amount`.

### Decrease Worker Stake

**Parameters**

| Name           | Description                           |
| -------------- | ------------------------------------- |
| `worker_id`    | Worker identifier.                    |
| `stake_amount` | Amount to decrease staked balance by. |

#### Conditions

* A lead worker is set.
* Signer uses role account of lead worker.
* `worker_id` corresponds to existing worker, not lead.
* worker has staking profile set.
* `stake_amount` is greater than zero.

#### Effect

Staking lock is reduced by `slashing_amount`.

### Increase Stake

**Parameters**

| Name           | Description                           |
| -------------- | ------------------------------------- |
| `worker_id`    | Worker identifier.                    |
| `stake_amount` | Amount to increase staked balance by. |

#### Conditions

* `worker_id` corresponds to an existing worker.
* Signer uses role account of worker.
* worker has staking profile set.
* `stake_amount` is greater than zero.

#### Effect

Staking lock is increased by `stake_amount`.

### Leader Spending

**Parameters**

| Name         | Description                |
| ------------ | -------------------------- |
| `account_id` | Account to get the tokens. |
| `amount`     | Transfer amount.           |
| `rationale`  | Spending rationale.        |

#### Conditions

* A lead worker is set.
* Signer uses role account of lead worker.
* `amount` is greater than zero.
* `budget` is not less than `amount`.

#### Effect

Account balance is increased by `amount`, and `budget` is reduced correspondingly.

### Set Status

**Parameters**

| Name         | Description                                                                                                                                             |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `new_status` | Encoded [working group status](https://github.com/Joystream/handbook/blob/master/system/working-groups/broken-reference/README.md) new status metadata. |

#### Conditions

* A lead worker is set.
* Signer uses role account of lead worker.

#### Effect

`status` is set to `new_status`.


# Proposal System

The proposal system is the way changes to the platform state and policy are suggested, discussed, voted on by the council, and finalized as accepted or rejected.

## Introduction

A proposal is a motion to change the state or policy of the system in some way. There are a wide variety of such proposal types, each type having a different

* set of required input parameter values
* requirements and risks of proposing
* barrier for getting accepted
* delay to being put into motion when accepted

The reason for this differentiation across types is because different proposals have very different effects, and carry very different risks of failure or abuse. The proposal system has the responsibility of coordinating the different actors involved in the lifetime of a proposal, from submission to finalization.

## Roles

The relevant roles in the proposal system are

* **Proposer:** A member that has submitted an instance of a specific proposal type. A given member can submit multiple proposals at once, or over time.
* **Council Members:** They are tasked with voting on proposals, which determines whether the proposals are accepted or not, as well as discussing a proposal with the proposer, and leaving a rationale for their vote.

## Concepts

### Vote

Each council member can submit at most one vote per proposal, and it includes the following:

* **Rationale:** A human readable description of why they are voting as they are.
* **Type:** There three types
  * **Approve:** Proposal should be approved.
  * **Reject:** Proposal should be rejected. Additionally, it can be expressed whether it should be slashed as part of the rejection.
  * **Abstain:** Voter has no position on outcome.

### Proposal Type

A proposal type is a parametrized intention to have some effect on the platform. The set of proposal types will increase considerably in the future, and the current types are listed below.

#### Constants

All proposal types have constant values for a shared set of parameters that are common across all types, thee are called *proposal constants.* The name and semantics of each constant is listed in the table below.

| **Name**             | Description                                                                                                                                        |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DECIDING_PERIOD`    | <p>Maximum number of blocks for deciding period.</p><p>Integer no less than 1.</p>                                                                 |
| `GRACING_LIMIT`      | <p>Minimum number of blocks that must pass after a<br>proposal is approved until it has its intended effect.</p><p>Integer no less than 0.</p>     |
| `APPROVAL_QUORUM`    | <p>Number of votes cast below which the proposal cannot be approved.</p><p>Integer no less than 1.</p>                                             |
| `APPROVAL_THRESHOLD` | <p>Minimum percentage of approval votes as a share of<br>all cast votes that result in approval.</p><p>Integer in \[0, 100].</p>                   |
| `SLASHING_QUORUM`    | <p>Number of votes cast below which the proposal<br>cannot be slashed.</p><p>Integer no less than 1.</p>                                           |
| `SLASHING_THRESHOLD` | <p>Minimum percentage of cast votes as share that slash relative<br>to those that vote approve, abstain or reject.</p><p>Integer in \[0, 100].</p> |
| `STAKE`              | <p>Exact stake required to create a proposal of this type.</p><p>Integer no less than 0.</p>                                                       |
| `CONSTITUTIONALITY`  | <p>The number of councils in that must approve the proposal<br>in a row before it has its intended effect.<br>Integer no less than 1.</p>          |

#### Parameters: General & Specific

Whenever a proposal of a given type is created, the proposer must provide values for a set of parameters. The parameters fall into one of two categories: *general* and *type-specific*. Each parameter will also have some constraint on the valid range of values. The general proposal parameters are

* **Proposer:** Member identifier of the proposer.
* **Title:** A human readable title.
* **Rationale:** A human readable description text that is intended to hold the rationale for the proposal should be accepted. It is expected that some social convention will emerge on the appropriate encoding of this text, for example markdown, that would facilitate consistent input and display across client applications.
* **Trigger:** An optional block number where the proposal is to be executed.
* **Staking Account:** The account that holds the funds that will be locked for staking, if required.

The type-specific parameters for each proposal type are listed with the proposals below.

#### Creation Conditions

When a proposal is submitted, a set of conditions on the values of the input parameters (only) are evaluated, these are called *creation conditions*, and creating the proposal fails if they are not satisfied. These are things like for example respecting the upper bound on the amount of money you are asking for in a spending proposal. Importantly, these checks are *pure*, they only depend on parameters, not the state of the system.

#### Execution Conditions

A proposal may be approved, and at some point the actual business logic that embodies its intended effect has to be executed. This is always much later than when the proposal was first created, and it may very well be possible to have a proposal which originally looked would have had its intended effect to no longer be applicable because the state of he system has changed in the intermediate. An example could be that a funding proposal requires more money than the council currently can spend due to other spending that may have occurred since the proposal was approved. These conditions are called *execution conditions*, and importantly, they are not checked at any time prior to execution of this business logic.

### Proposal

A proposal is defined by the following information

* **Id:** A unique non-negative integer identifier.
* **Type:** Which type of proposal this is.
* **General Parameters:** Values for general proposal parameters.
* **Type-Specific Parameters:** Values for type-specific proposal parameters.
* **Stage:** The life-cycle stage of a proposal, as defined precisely in the next section.
* **Votes:** The set of votes currently associated with the proposal.
* **Council Approvals:** How many prior councils have approved the proposal, starts at 0.
* **Starting Block:** The block where the deciding period was initiated.
* **Discussion:** A single threaded discussion about the proposal, as defined in the discussion section.

**Stage**

Below is a list of the stages a proposal can be in, and what each of them mean:

* **Deciding:** Initial stage for all successfully created proposals. This is the only stage where votes submitted can actually impact the outcome. If a new council is elected, any present stake is slashed by `REJECTION_FEE` , the staking lock is removed and the proposal transitions to the rejected stage.

  When a vote is submitted it is evaluated as such:

  1. If `APPROVAL_QUORUM` and `APPROVAL_THRESHOLD` are satisfied, then increment council approvals counter. If counter now is `CONSTITUTIONALITY` then remove staking lock and transition to gracing stage, otherwise transition to dormant stage.
  2. If `SLASHING_QUORUM` and `SLASHING_THRESHOLD` are satisfied, but point (1) is not, then slash full stake, remove the lock and transition to the rejected stage.
  3. If points (1) and (2) are not and cannot be satisfied by any future a outstanding votes, then slash stake by up to `REJECTION_FEE`, remove lock and transition to rejected stage.

If `DECIDING_PERIOD` blocks pass while still in this stage, apply checks (1-3) with same transition and side-effect rules as above.

* **Dormant:** Was approved by current council, but requires further approvals to satisfy `CONSTITUTIONALITY` requirement. Transitions to deciding stage when next council is elected.
* **Gracing:** Is awaiting execution for until trigger block, or `GRACING_LIMIT` blocks since start of period if no trigger was provided. When this duration is over, the execution conditions are checked, if they are satisfied the proposal transitions to the execution succeeded stage, if they are not, it transitions to the execution failed stage.
* **Vetoed:** Was halted by SUDO, nothing further can happen. This is removed at mainnet.
* **Slashed:** Was rejected with full stake penalty by the current council.
* **Execution Succeeded:** Execution succeeded, nothing further can happen.
* **Execution Failed:** Execution failed due to unsatisfied execution conditions, nothing further can happen.
* **Rejected:** Was not approved, nothing further can happen.

It useful to designate any proposal in the stages deciding, dormant or gracing, as an *active proposal*, and any other proposal is said to be an *inactive proposal*. Votes can not be submitted for *inactive proposal*.

Before mainnet, an extra transition rule is worth bearing in mind is that, for any active proposal, SUDO can initiate veto, which results in transition to vetoed stage.

The stages and transitions, excluding SUDO dynamics, are summarized in the image below.

![Proposal life-cycle stages.](/files/gyjD3lAaBQB4opyYSYjp)

### Staking

As described, proposals may require staking to be submitted. A single account must be used to provide the stake for a proposal, and it cannot be used to hold stake for any other proposals or purpose, except voting, at the same time. The staking is implemented as a lock with id `PROPOSAL_LOCK_ID`.

### Discussion

A single threaded discussion is opened for each successfully created discussion. A thread can be in two *discussion modes*, open or closed. In open mode, any member can post a message, while in closed mode, only the active council, the original proposer, or one among a set of whitelisted members can post. Mode can be changed by member or council member at any time, and default mode is open. Both council members and proposer can curate whitelist by adding and removing members. A poster can edit a post an unlimited number of times, but only if they have access. A thread can no longer be updated in any way (mode, posting, edits, etc.) when `DISCUSSION_LINGERING_DURATION` have passed since being rejected or executed. Lastly, at most `MAX_POSTS_PER_THREAD` can be posted in a single thread.

## Proposals

This section includes proposals that concern the platform as whole in terms intended effect and type-specific parameters.

### Signal

#### Parameters

| Name     | Description                               |
| -------- | ----------------------------------------- |
| `signal` | The actual human readable signaling text. |

Note that the distinction between `signal` and the rationale parameter is that the rationale is the *why* and this is the *what.*

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

* `signal` is non-empty.

#### Execution Conditions

None.

#### Effect

None.

### Amend Constitution

#### Parameters

| Name               | Description                                  |
| ------------------ | -------------------------------------------- |
| `new_constitution` | The actual human readable constitution text. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

None.

#### Effect

None.

### Funding Request

#### Parameters

| Name       | Description                                     |
| ---------- | ----------------------------------------------- |
| `amounts`  | The amount of tokens requested to each account. |
| `accounts` | Recipients of funds.                            |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

* each `amount` is greater than zero.
* each `amount` is no more than `MAX_SPENDING_PROPOSAL_VALUE`
* there is at least one account

#### Execution Conditions

* the council budget is no less than the sum of `amounts`.

#### Effect

* the council budget is reduced by the sum of `amounts`.
* for each account in `accounts` its fund is augmented by its corresponding amount in `amounts`.

### Runtime Upgrade

#### Parameters

| Name   | Description                                               |
| ------ | --------------------------------------------------------- |
| `blob` | The raw WebAssembly object to be used as the new runtime. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

`blob` is non-empty.

#### Execution Conditions

None.

#### Effect

The block after this proposal is executed will follow the rules of the runtime captured in `blob`.

### Create Working Group Lead Opening

#### Parameters

| Name                      | Description                            |
| ------------------------- | -------------------------------------- |
| `group`                   | Identifier for working group.          |
| `description`             | Human readable description of opening. |
| `stake_policy`            | Optional staking policy.               |
| `per_block_reward_amount` | Block denominated reward.              |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

Same as when creating an opening for workers in the given group with given inputs, except signer check.

#### Effect

Same as when creating an opening for workers in the given group with given inputs, except the opening type is for lead.

### Cancel Working Group Lead Opening

#### Parameters

| Name         | Description                      |
| ------------ | -------------------------------- |
| `group`      | Identifier for working group.    |
| `opening_id` | Identifier for opening in group. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

Same as when cancelling an opening for workers in the given group with given inputs, except signer check.

#### Effect

Same as when cancelling an opening for workers in the given group with given inputs.

### Fill Working Group Lead Opening

#### Parameters

| Name             | Description                          |
| ---------------- | ------------------------------------ |
| `group`          | Identifier for working group.        |
| `opening_id`     | Identifier for opening in group.     |
| `application_id` | Identifier for successful applicant. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

Same as when filling opening in group for worker with given inputs, except `application_id` as `winners` and signer check.

#### Effect

Same as when filling opening in group for worker with given inputs, except `application_id` as `winners`.

### Slash Working Group Lead

#### Parameters

| Name              | Description                   |
| ----------------- | ----------------------------- |
| `group`           | Identifier for working group. |
| `worker_id`       | Worker identifier.            |
| `slashing_amount` | Amount to be slashed.         |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

Same as when slashing a worker in group with given inputs, except

* signer check,
* worker corresponding to `worker_id` must be lead.

#### Effect

Same as when slashing a worker in group with given inputs.

### Terminate Working Group Lead

#### Parameters

| Name              | Description                    |
| ----------------- | ------------------------------ |
| `group`           | Identifier for working group.  |
| `worker_id`       | Worker identifier.             |
| `slashing_amount` | Optional amount to be slashed. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

Same as when terminating a worker in group with given inputs, except signer check.

#### Effect

Same as when terminating a worker in group with given inputs, and removing lead designation.

### Set Working Group Lead Reward

#### Parameters

| Name               | Description                           |
| ------------------ | ------------------------------------- |
| `group`            | Identifier for working group.         |
| `worker_id`        | Worker identifier.                    |
| `reward_per_block` | New reward rate per block for worker. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

Same as when updating reward of a worker in group with given inputs, except signer check.

#### Effect

Same as when updating reward of a worker in group with given inputs.

### Decrease Working Group Lead Stake

#### Parameters

| Name           | Description                        |
| -------------- | ---------------------------------- |
| `group`        | Identifier for working group.      |
| `worker_id`    | Worker identifier.                 |
| `stake_amount` | Amount by which to decrease stake. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

`stake_amount` is greater than zero.

#### Execution Conditions

Same as when decreasing worker stake in group with given inputs, except signer check.

#### Effect

Same as when decreasing worker stake in group with given inputs.

### Update Working Group Budget

#### Parameters

| Name            | Description                     |
| --------------- | ------------------------------- |
| `group`         | Identifier for working group.   |
| `budget_update` | Signed amount change in budget. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

If `budget_update` is non-negative, then this it must be no more than the council budget, otherwise the absolute value must be no more than the current group budget.

#### Effect

If `budget_update` is non-negative, then this amount is reduced from the council budget and credited to the group budget, otherwise the reverse.

### Set Max Validator Count

#### Parameters

| Name                  | Description              |
| --------------------- | ------------------------ |
| `new_validator_count` | New max validator count. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

`new_validator_count` is no less than the `MinimumValidatorCount` value in `pallet_staking` module storage and no greater than `MAX_VALIDATOR_COUNT`.

#### Execution Conditions

None.

#### Effect

Same as `set_validator_count` in `pallet_staking` module with given input.

### Set Membership Price

#### Parameters

| Name                   | Description           |
| ---------------------- | --------------------- |
| `new_membership_price` | New membership price. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

None.

#### Effect

The membership price is set to `new_membership_price`.

### Set Referral Cut

#### Parameters

| Name               | Description                  |
| ------------------ | ---------------------------- |
| `new_referral_cut` | New referral cut percentage. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

None.

#### Effect

The referral cut is set to `new_referral_cut`.

### Set Initial Invitation Count

#### Parameters

| Name                       | Description                    |
| -------------------------- | ------------------------------ |
| `new_default_invite_count` | New default invitations count. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

None.

#### Effect

The default invitations count is set to `new_default_invite_count`.

### Set Initial Invitation Balance

#### Parameters

| Name                          | Description                  |
| ----------------------------- | ---------------------------- |
| `new_invited_initial_balance` | New invited initial balance. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

None.

#### Effect

The new invited initial balance is set to `new_invited_initial_balance`.

### Set Membership Lead Invitation Quota

#### Parameters

| Name               | Description           |
| ------------------ | --------------------- |
| `new_invite_count` | New invitation count. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

The membership working group has an assigned lead with membership id `membership_id`.

#### Effect

The invitation quota of member is set to `new_invite_count`.

### Set Council Budget Increment

#### Parameters

| Name                   | Description                   |
| ---------------------- | ----------------------------- |
| `new_budget_increment` | New council budget increment. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

None.

#### Effect

The budget increment is set to `new_budget_increment`.

### Set Councilor Reward

#### Parameters

| Name                   | Description           |
| ---------------------- | --------------------- |
| `new_councilor_reward` | New councilor reward. |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

None.

#### Execution Conditions

None.

#### Effect

The councilor reward is set to `new_councilor_reward`.

### Update Global NFT Limit

#### Parameters

| Name               | Description                                               |
| ------------------ | --------------------------------------------------------- |
| `nft_limit_period` | The type of period for which the limit should be updated. |
| `limit`            | The value for the limit.                                  |

#### Constants

| Constant             | Value     |
| -------------------- | --------- |
| `DECIDING_PERIOD`    | `fill-in` |
| `GRACE_PERIOD`       | `fill-in` |
| `APPROVAL_QUORUM`    | `fill-in` |
| `APPROVAL_THRESHOLD` | `fill-in` |
| `SLASHING_QUORUM`    | `fill-in` |
| `SLASHING_THRESHOLD` | `fill-in` |
| `PROPOSAL_STAKE`     | `fill-in` |
| `CONSTITUTIONALITY`  | `fill-in` |

#### Creation Conditions

#### Execution Conditions

Global limit value for specified period is updated

## Constants

The following constants are hard coded into the system, they can only be updated with a runtime upgrade.

| Name                            | Description                                                                     |   Value   |
| ------------------------------- | ------------------------------------------------------------------------------- | :-------: |
| `MAX_RUNTIME_UPGRADE_BYTES`     | Maximum allowed number of bytes in a runtime upgrade Wasm blob.                 | `fill-in` |
| `REJECTION_FEE`                 | Up to number of tokens slashed if proposal rejected, but not with slashing.     | `fill-in` |
| `DISCUSSION_LINGERING_DURATION` | Number of blocks after proposal inactivation a proposal discussion is closed.   | `fill-in` |
| `MAX_POSTS_PER_THREAD`          | Max posts per thread.                                                           | `fill-in` |
| `MAX_ACTIVE_PROPOSALS`          | Max active proposals allowed at any given time.                                 | `fill-in` |
| `PROPOSAL_LOCK_ID`              | The lock id used for proposal staking locks.                                    | `fill-in` |
| `MAX_VALIDATOR_COUNT`           | The maximum number of validators accepted by validator staking system.          | `fill-in` |
| `MAX_WHITELIST_SIZE`            | The maximum number of whitelisted participants in a closed propsoal discussion. | `fill-in` |

## Extrinsics

### Submit Proposal

**Parameters**

| Name        | Description                                                  |
| ----------- | ------------------------------------------------------------ |
| `proposer`  | Member identifier of proposer.                               |
| `title`     | Title for proposal.                                          |
| `rationale` | Rationale for proposal.                                      |
| `trigger`   | Optional trigger block for executing proposal.               |
| `account`   | Optional staking account for proposal.                       |
| `type`      | The of proposal and all associated type specific parameters. |

#### Conditions

* Signer matches controller account of `proposer`
* Number of active proposals is no greater than `MAX_ACTIVE_PROPOSALS`.
* If `PROPOSAL_STAKE` is greater than zero, then `account` must have a free balance no less than that. Also`account` is bound to `proposer`, and only has a voting lock if anything.
* If `trigger` is provided, it must be no less than current block plus `GRACING LIMIT` + `DECIDING_PERIOD`.
* Creation conditions for `type` are satisfied.

#### Effect

A new proposal , of type `type` , is created in the deciding period stage, and a new discussion thread is opened in the open mode. Moreover, if `PROPOSAL_STAKE`is greater than zero, a new lock with id `PROPOSAL_LOCK_ID` and amount `PROPOSAL_STAKE` is set.

### Vote

**Parameters**

| Name        | Description                    |
| ----------- | ------------------------------ |
| `proposal`  | Identifier for proposal.       |
| `vote_type` | The type of vote.              |
| `rationale` | The rationale for the vote.    |
| `councilor` | Identifier for council member. |

#### Conditions

* `proposal` corresponds to an existing proposal in **Deciding** stage.
* Signer is role account of councilor identified by `councilor`.
* Councilor has not yet voted on this proposal.

#### Effect

Record vote of `councilor`, and follow steps in **Deciding** stage for processing a vote.

### Post to Thread

**Parameters**

| Name       | Description                                          |
| ---------- | ---------------------------------------------------- |
| `proposal` | Identifier for proposal.                             |
| `text`     | Post text.                                           |
| `author`   | Either identifier of council member, or of a member. |

#### Conditions

* `proposal` corresponds to an existing proposal in where discussion is active, that is either the proposal is active, or no more than `DISCUSSION_LINGERING_DURATION` blocks have passed since it became inactive.
* `author` corresponds to signer.
* If `author` is a member, either is the proposer, or the discussion mode is open, or it is closed and the `author` is on the whitelist for this thread.
* The current number of posts in this thread is less than `MAX_POSTS_PER_THREAD`.

#### Effect

Post is added to thread.

### Update Post

**Parameters**

| Name       | Description              |
| ---------- | ------------------------ |
| `proposal` | Identifier for proposal. |
| `post`     | Identifier for post.     |
| `text`     | New post text.           |

#### Conditions

* `proposal` corresponds to an existing proposal in where discussion is active, that is either the proposal is active, or no more than `DISCUSSION_LINGERING_DURATION` blocks have passed since it became inactive.
* `post` corresponds to an existing post on proposal.
* author of post corresponds to signer.

Note that editing is possible, regardless of mode, so long as the `author` is owner.

#### Effect

Update text of post.

### Change Thread Mode

**Parameters**

| Name        | Description                             |
| ----------- | --------------------------------------- |
| `proposer`  | Identifier for proposal.                |
| `member_id` | Identifier of member initiating action. |
| `mode`      | New discussion mode.                    |

#### Conditions

* `proposal` corresponds to an existing proposal in where discussion is active, that is either the proposal is active, or no more than `DISCUSSION_LINGERING_DURATION` blocks have passed since it became inactive.
* signer corresponds to member identified with `member_id`
* member is either proposal author or council member.
* `mode` respects `MAX_WHITELIST_SIZE`.

#### Effect

Update thread discussion mode to `mode`.

### Create Blog Post

**Parameters**

| Name    | Description            |
| ------- | ---------------------- |
| `title` | Title of the blog post |
| `text`  | Text of the blog post  |

#### Creation Conditions

None.

#### Execution Conditions

None.

#### Effect

A blog post is created.

### Veto Proposal

**Parameters**

| Name          | Description              |
| ------------- | ------------------------ |
| `proposal_id` | Identifier for proposal. |

#### Creation Conditions

None.

#### Execution Conditions

* Proposal corresponding to `proposal_id` is either in Vote period, Grace period or pending constitution.

#### Effect

* Proposal corresponding to `proposal_id` is automatically discarded.


# Content Directory

A public index of all creators, content and metadata.

## Introduction

The content directory is public index of all the channels and content, and associated social interactions like comments and reactions, and associated digital assets like video NFTs and Creator Tokens. Review the relevant subarticle to understand more about the given topic. This article will need to be expanded further, as not all features in the conent directory are currently covered.

{% content-ref url="/pages/wkonaHOzg8pmWUnjRx4E" %}
[Video NFTs](/system/content-directory/nft)
{% endcontent-ref %}

{% content-ref url="/pages/ArzMswvAyN0ZShlXzgGZ" %}
[Creator Tokens](/system/content-directory/projecttoken)
{% endcontent-ref %}

{% content-ref url="/pages/3JYIwokI3O9nYD7webor" %}
[Creator Payouts](/system/content-directory/payout)
{% endcontent-ref %}

{% content-ref url="/pages/FZtDkneWElnqTIhqcb59" %}
[Curation Model](/system/content-directory/curation-model)
{% endcontent-ref %}

## Notion Space&#x20;

The community maintains a distinct Notion space which holds more dynamic information on the activities of the content directory working group.

{% embed url="<https://joystream.notion.site/Content-Directory-6e4b6d211b174526889464d263475cab>" %}

## Content Creator

### Responsibilities

* Publish content and manage channel.
* Mint NFTs as desired.
* Issue and manage Creator Tokens as desired.
* Manage comments section on content.

### Requirements

* Create original content or have right to publish content of third party.

## Content Curator

### Responsibilities

* Monitor the publishing of new content into the content directory, and respond to reports about contested publications
* Adjudicate possible dispute processes resulting from reports from users
* Update information on content to be accurate
* Collaborate with [Builders](https://www.joystream.org/roles#builder) to improve both tools, and user facing experiences, to improve the integrity of the content directory

### Requirements

* Fairly adjudicate disputes, and communicate in clear and transparent way with stakeholders and participants
* Hold sufficient amount of the native platform token to put at stake

## Content Curator Lead

The Council has the power to appoint a Content Curator Lead for the network who can hire further Content Curators. The Content Lead also decides on priorities for curation. If necessary, upon discussing with the council, the Lead can also decide to fire curators who are not performing their jobs adequately.

### Responsibilities

* Monitor the publishing of new content into the content directory, and respond to reports about contested publications
* Adjudicate possible dispute processes resulting from reports from users
* Update information on content to be accurate
* Manage and coordinate the actions of the Content Working Group

### Requirements

* Fairly adjudicate disputes, and communicate in clear and transparent way with stakeholders and participants
* Hold sufficient amount of the native platform token to put at stake

##


# Creator Tokens

Financial and marketing super powers for content creators

## Introduction

Gives token-like functionality for a content creator to capitalize on his channel and popularity.\
A token works similarly to shares of a company, meaning that holders can claim the channel's profit (via revenue split), but also it can be transferred between accounts and exchanged for $JOYs. From now on we'll denote by the word CRT the token ecosystem for a particular channel that possesses the functionalities described in this document. A Joystream member who owns (or has owned in the past) some non-zero amount of tokens for a particular project or channel is referred to as a CRT account (or a CRT holder).

## Concepts

### Token Supply

Tokens are minted and burned due to issuance and burning events, made possible by the CRT ecosystem. The number of tokens outstanding for a particular channel/project will be called *Total Supply* throughout the document and it will denote the number of tokens that have been minted minus the tokens that have been burned since the particular CRT instance has been issued by the Content Creator.

### Permissioned and Permissionless Mode

A particular Token can be labeled at any time as either *Permissioned* or *Permissionless*. Permissioned mode allows for transfers only between selected accounts. Only the Token issuer can change a mode from Permissioned to Permissionless. When Permissionless mode is activated it stays active forever. As mentioned in the account section, an account can be dusted by anyone in permissionless mode and only by its Joystream controller member in permissoned mode. If a CRT is issued in permissioned mode then the Issuer can also specify a particular whitelist of members that are allowed to create a CRT account for themselves and thus interact with the pallet functionalities via `join_whitelist` extrinsic.

### Account

Denotes the information about the number of tokens that a particular Joystream member holds. There are different types of balances in a CRT account according to their usage:

* *Transferrable balance*: denotes the amount that can be transferred at any time (i.e. liquidity)
* *Vested balance*: the amount that is due but not yet credited and is subjected to a vesting schedule
* *Staked balance*: the amount that is not accessible due to staking (locking funds for a certain period) The total amount for an account is the following quantity: $$\text{totalBalance}= \text{transferrableBalance} + \max(\text{stakedBalance}, \text{vestedAmount})$$ (This is because we allow vested tokens to be used for staking as it will be explained later).\
  When an account is created a *Bloat Bond* for on-chain storage protection must be deposited into the CRT treasury account. Dusting refers to the process of removing the information for an account from the on-chain storage state. An account can be dusted when its Total Balance is equal to zero and one of the following is satisfied:
* Token mode is Permissionless
* Token mode is Permissioned and the user doing the dusting is the Joystream member controller for that account. Whoever does the dusting gets accredited with the associated bloat bond for the account removed.

### Vesting schedule

An account can be credited with tokens directly and the transferrable balance is increased as a consequence, or through vesting. An account upon whom a vesting schedule is pending is said to be vested. A vesting schedule is specified by the following parameters:

* `cliff_amount`: the amount that is assigned immediately to the transferrable balance of an account
* `start`: start of the schedule vesting period
* `end`: end of the schedule vesting period Crediting a given `amount` through vesting works in the following way:
* first, a certain `cliff_amount` is assigned to the transferrable balance
* the remaining `amount - cliff_amount` is uniformly distributed over the period specified by the `start, end` parameters

### Issuing a token

The content creator (herein called *Token Issuer*) can issue a CRT. The act of issuing a token is called token issuance and it requires the issuer to specify the following parameters:

* `patronage_rate`: rate for the patronage functionality, which allows the token Issuer to mint tokens into his account according to such rate.
* `initial_allocation`: a mapping between Joystream members and their transferrable balance, specifying who receives what at issuance.
* `revenue_split_rate`: percentage of channel profit that a CC can retain for himself in a revenue split distribution.
* `symbol`: ticker for the Token.
* `transfer_policy`: whether to allow a transfer only between a selected group of accounts (*Permissioned* mode) or not (*Permissionless*). In case a CRT is issued as *Permissioned* an initial whitelist containing the selected accounts must be specified.

### Transferring Tokens

Transfer denotes the act of moving a given token amount (which can be zero) from one sender to one or more receiver CRT accounts. The are two possible ways for transferring tokens: `issuer_transfer`: when the sender is the Token Issuer, in which case the receiver can be any Joystream member `transfer`: when the sender is not the token Issuer and in which case the receiver must be a valid CRT account. In case the receiver doesn't have an account a new one will be automatically created and it is up to the issuer to pay the bloat bond for each created account.\
During a issuer, transfer tokens can be both credited to the receiver transferrable balance and vested, meanwhile during a simple transfer amounts are credited to the recipients' transferrable balance. The maximum amount of recipients regardless of the type of transfer is specified by the `MAX_OUTPUTS` constant.

### Patronage

The patronage functionality allows the token issuer to claim a certain percentage of the current supply. The claim can be exercised at any time and the patronage amount will be minted into the token issuer account. The amount of tokens minted through patronage is computed according to:

$$
\text{patronageAmount} = \text{totalSupply} ( \times (1 + \text{patronageRate})^{c} - 1 )
$$

with

$$
c = \frac{blocksSinceLastPatronageClaim}{blocksInAYear}
$$

The `patronage_rate` can range from $0$ to a certain upper bound set by the governance. It can be also reduced at any given time by the Token Issuer, in this case, the patronage rate used for computing the patronage amount will be the latest value set.

### Revenue split

Allows the Token issuer to dispense a certain amount of $JOY between himself (called *Issuer Amount*) and CRT accounts (called *Revenue Allocation*) in a predefined time window. Is up to the token issuer to start a revenue split at any given time (provided that there's no other ongoing) in which case he will receive his Issuer Amount immediately, the remaining Revenue Allocation amount is thus destined for CRT accounts. At any time since the start of the revenue split any CRT holder willing to participate can stake some of his token (both from his transferrable and vesting balance) to participate in the revenue split and claim some JOYs (referred to as *JOY Dividend*). The tokens will remain staked until the split is ended and it is up to the CRT holders to unstake their tokens via the appropriate extrinsic. In particular, the JOY Dividend amount is transferred to the CRT holders' JOY balance upon staking and according to the following formula:

$$
\text{JOYDividend} = \frac{\text{TokensStaked}}{\text{TotalSupply}} \times \text{RevenueAllocation}
$$

### Token Sale

Allows the Token issuer to sell part of his allocation to any Joystream Member. A sale can be started at any given time (provided that there are no other ongoing sales) by the Issuer who has to specify the following:

* a particular amount of Tokens to sell
* and a start, end block number pair to identify a time window over which the sale is going to be held (herein referred to as *Sale Timeline*).
* a unit sale price
* (optional) parameters for vesting the token purchased during the sale The sale timeline can be updated by the issuer at any time prior to the starting block. Once a sale has started any Joystream member can purchase tokens at the given sale price, the purchased amount can be credited to the transferrable balance or it can be vested. A CRT account is created in case the purchaser doesn't have one yet, and in that case, the purchaser is required to deposit the bloat bond for the account. A sale can be auto-finalized, meaning that once the tokens allocated for the sale have been fully sold the sale is automatically closed and finalized, alternatively, the Issuer can close an ended sale and get any amount of token left over.

### AMM

This type of sale is best thought of as an automated market maker (AMM) which users can buy from and sell tokens at a variable unit price. The sale has an indefinite duration and it can be started immediately at any time by the Token Issuer. Two possible operations are possible in this AMM scenario:

* a `buy` operation, through which any Joystream member can have new Tokens minted into his transferrable balance effectively and immediately in exchange for JOYs (in case of a member not having a CRT account yet the mechanics are the same as in the standard Token Sale). The JOY proceedings from the minting operation are deposited into an internally managed AMM account.
* a `sell` operation, through which any CRT account can sell some of its transferrable balance to the AMM and get some AMM treasury JOYs in exchange. A sell operation is no longer possible if the AMM treasury account is empty. Tokens sold to the AMM are burned. The amount of tokens outstanding from all AMM buy/sell sale operations is referred to as the *AMM-Provided Supply*, which is a subset of the Total supply denoting the number of tokens that have been minted minus the tokens that have been burned by the AMM since its activation. The unit price for each token bought/sold on the AMM is computed using the following formula:

$$
\text{AMMprice} = a \times \text{AMMProvidedSupply} + \text{initialPrice}
$$

Where *Initial Price* and $a$ are respectively the first price and sensitivity parameter the Issuer sets upon AMM activation.

The AMM Sale can be finally closed at any given time by the Issuer provided that the following condition holds: $$\text{AMMProvidedSupply} \le \text{TotalSupply} \times \text{Threshold}$$ Where `THRESHOLD` is a governance-set percentage. Notice that if `THRESHOLD = 0` then every token minted through the AMM must be also burned for the AMM sale to be closed.

## Parameters

| Name                       | Type          | Description                                                           |
| -------------------------- | ------------- | --------------------------------------------------------------------- |
| `BloatBond`                | `Balance`     | Bloat bond value used during account creation                         |
| `MinSaleDuration`          | `BlockNUmber` | Minimum duration of a token sale                                      |
| `MinRevenueSplitDuration`  | `BlockNumber` | Minimum duration for a revenue split                                  |
| `SalePlatformFee`          | `Permill`     | Platform fee percentage charged on top of each sale and burned        |
| `AMMDeactivationThreshold` | `Permill`     | maximum AMMSupply/TotalSupply ration allowed for deactivating the AMM |
| `AMMBuyTxFees`             | `Permill`     | Percentage of fees charged on top of each token purchase from the AMM |
| `AMMSellTxFees`            | `Permill`     | Percentage of fees charged on top of each token sold to the AMM       |
| `MaxYearlyPatronageRate`   | `Permill`     | Maximum patronage rate allowed                                        |

## Constants

| Name                    | Description                                    | Value     |
| ----------------------- | ---------------------------------------------- | --------- |
| `PalletId`              | Identifier for the pallet                      | `fill-in` |
| `JoyExistentialDeposit` | Existential deposit for the JOY account        | `fill-in` |
| `BlocksPerYear`         | (Average) number of blocks finalized in a year | `fill-in` |
| `MaxOutputs`            | Max number of receivers for a single transfer  | `fill-in` |

## Metadata

Add data later.

## Extrinsics

### Issue Creator Token

* Name: `issue_creator_token`
* Pallet: `content`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Init Creator Token Sale

* Name: `init_creator_token_sale`
* Pallet: `content`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Update Upcoming Creator Token Sale

* Name: `update_upcoming_creator_token_sale`
* Pallet: `content`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Creator Token Issuer Transfer

* Name: `creator_token_issuer_transfer`
* Pallet: `content`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Make Creator Token Permissionless

* Name: `make_creator_token_permissionless`
* Pallet: `content`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Reduce Creator Token Patronage Rate

* Name: `reduce_creator_token_patronage_rate_to`
* Pallet: `content`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Claim Creator Token Patronage Credit

* Name: `claim_creator_token_patronage_credit`
* Pallet: `content`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Issue Revenue Split

* Name: `issue_revenue_split`
* Pallet: `content`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Finalize Revenue Split

* Name: `finalize_revenue_split`
* Pallet: `content`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Finalize Creator Token Sale

* Name: `finalize_creator_token_sale`
* Pallet: `content`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Deissue Creator Token

* Name: `deissue_creator_token`
* Pallet: `content`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Activate AMM

* Name: `activate_amm`
* Pallet: `content`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Deactive AMM

* Name: `deactivate_amm`
* Pallet: `content`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Creator Token Issuer Remark

* Name: `creator_token_issuer_remark`
* Pallet: `content`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Transfer

* Name: `transfer`
* Pallet: `project_token`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Burn

* Name: `burn`
* Pallet: `project_token`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Dust Account

* Name: `dust_account`
* Pallet: `project_token`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Join Whitelist

* Name: `join_whitelist`
* Pallet: `project_token`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Purchase Token On Sale

* Name: `purchase_token_on_sale`
* Pallet: `project_token`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Participate In Split

* Name: `participate_in_split`
* Pallet: `project_token`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Exit Revenue Split

* Name: `exit_revenue_split`
* Pallet: `project_token`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Buy On AMM

* Name: `buy_on_amm`
* Pallet: `project_token`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Sell On AMM

* Name: `sell_on_amm`
* Pallet: `project_token`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**

### Set Frozen Status

* Name: `set_frozen_status`
* Pallet: `project_token`

#### Parameters

**WIP**

#### Conditions

**WIP**

#### Effect

**WIP**


# Creator Payouts

Scaleable governance powered creator payouts

## Introduction

The council may find it appropriate to reward some channels based on various merits, such as most viewed content, etc... The payout functionality allows the channel owner (or a content actor with sufficient permission) to cash out a particular sum of JOY rewarded by the council to the channel. This document will discuss the runtime design of it. For how to steps please refer to [gleev documentation](https://joystream.notion.site/Gleev-Operation-2ee6319c7688464c8800ecbfa3b17abd#0ca570ef32cc4226b8289503551f343e)

## Concepts

### Payout & Payouts Commitment

By payout amount we mean the amount of $JOY the council has decided to award to a particular channel at a particular point in time. New payouts will be added as time passes via council proposals. By cumulative payout earned, we will refer to the cumulative amount of payouts awarded by the council to a particular channel since the genesis block. Herein we will refer to as *payment element* the triple consisting of:

* channel id
* cumulative payout earned
* human-readable reason for the latest payout added At any given time the set of payment elements (representing the channel awarded by the council with the respective payout amounts) is stored in the content pallet chain state as its Merkle proof root hash. This value is called *payouts commitment*

#### Claim

By claim we refer to the action of the channel owner (or a delegate) to claim and cash out the payout. The delegate can be whoever has sufficient permission to perform this action. From here on we'll say that a claim is made by the channel to avoid distinguishing between owner & non-owner. The channel can claim the amount via Merkle proof verification. In particular, the claiming at a particular block height is allowed if:

1. The payment element provided by the channel must be verified via Merkle proof verification against the payouts commitment value
2. The claiming channel must not have `CreatorCashout` among its paused features
3. The payout amount must be in the range `[MinCashoutAllowed, MaxCashoutAllowed]` (see param section)
4. The channel is not being transferred to another owner
5. The actor doing the claiming has sufficient permissions, as explained previously. Specific permission references are described in the next section

## Parameters

The following are mutable parameters updated via council proposal

| Name                | Type      | Description                                                              |
| ------------------- | --------- | ------------------------------------------------------------------------ |
| `PayoutsCommitment` | `Hash`    | Payouts commitment hash value at any given time                          |
| `MinCashoutAllowed` | `Balance` | Minimum amount of JOY a channel is allowed to cash out at any given time |
| `MaxCashoutAllowed` | `Balance` | Maximum amount of JOY a channel is allowed to cash out at any given time |
| `CashoutEnabled`    | `boolean` | Whether payout claiming is enabled or not                                |

## Constants

The following constants are hard coded into the system, and they can only be changed via runtime upgrade

| Name                       | Description                                                 | Value     |
| -------------------------- | ----------------------------------------------------------- | --------- |
| `MIN_CASHOUT_ALLOWED`      | minimum payout amount that a channel is allowed to cash out | `fill-in` |
| `MAX_CASHOUT_ALLOWED`      | maximum payout amount that a channel is allowed to cash out | `fill-in` |
| `CHANNEL_CASHOUTS_ENABLED` | Whether payout claiming is enabled or not                   | `TRUE`    |

## Extrinsics

### Payout Parameters Update

Allows the council to update parameters that regulate the action of claiming a payout via a proposal. This also allows the council to set a new value for the payouts commitment, thus endowing new payouts to channels

**Parameters**

| Name                                    | Descripion                                                                                                      |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `new_payout_commitment`                 | hash for the merkle root (optional)                                                                             |
| `object_creation_list`                  | list of `(ipfs_id, object_mb_size)` containing information about object size and creation                       |
| `uploader_account`                      | account from which all the expenses for the storage upload are charged to                                       |
| `expected_data_size_fee`                | expected price for mb, must match the `storage-pallet::data_size_fee` value                                     |
| `expected_data_object_state_bloat_bond` | expected bloat bond for storage occupation. Must match the `storage-pallet::data_object_state_bloat_bond` value |
| `new_min_cashout_allowed`               | minimum amount that a channel is allowed to cash out (optional)                                                 |
| `new_max_cashout_allowed`               | maximum amount that a channel is allowed to cash out (optional)                                                 |
| `channel_cashouts_enabled`              | whether or not to enable the channel payouts functionality (optional)                                           |

#### Condition

* `min_cashout_allowed <= new_max_cashout_allowed` must be respected or `new_min_cashout_allowed <= new_max_cashout_allowed` if both are specified
* `new_min_cashout_allowed <= max_cashout_allowed` must be respected or `new_min_cashout_allowed <= new_max_cashout_allowed` if both are specified
* `new_min_cashout_allowed <= MINIMUM_CASHOUT_ALLOWED_LIMIT` if specified
* `new_max_cashout_allowed <= MAXIMUM_CASHOUT_ALLOWED_LIMIT` if specified
* `origin` must be root, i.e. the extrinsic must be dispatched via proposal
* `storage-pallet::upload_data_objects` preconditions must be verified
* `uploader_account` should be some account i.e. council #out-of-process or the proposer member controller account

#### Effect

* `max_cashout_allowed` set to `new_max_cashout_allowed` if this last value is specified
* `min_cashout_allowed` set to `new_min_cashout_allowed` if this last value is specified
* `channel_cashouts_enabled` updated to the the new value (if specified)
* `commitment` value updated to `new_commitment` if specified
* `storage-pallet::upload_parameters(params)` effects are taken in place where `params` is specified as follows:
  * `bag_id` is the Council Static Bag
  * `object_creation_list` is the `payload_params.object_creation_list`
  * `bloat_bond_source_account_id` is the `payload_params.uploader_account`
  * `expected_data_size_fee` is the `payload_params.expected_data_size_fee`
  * `expected_data_object_state_bloat_bond` is the `payload_params.expected_data_object_state_bloat_bon`

### Channel Reward Claim

Allow a channel to claim a `payout` and have the $JOYs deposited into its `reward_account`

**Parameters**

| Name                | Description                                                                                                                                                 |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `actor`             | either a member, lead or curator claiming the `payout`                                                                                                      |
| `proof`             | merkle proof used for verifying whether the `payout` the channel is claiming is legit or not                                                                |
| `pull_payment_item` | the information containing the `payout` plus some other data used in the validation process and also the `channel_id` for which the `payout` is destined to |

#### Condition

* `channel_id`: must refer to an existing `channel`
* `channel`: must not have the `CreatorCashout` feature paused
* `actor`: must be endowed with a `RewardChannelReward` permission
* `channel_cashout_enabled`: must be `true`
* `channel_total_cashout + payout` amount of $JOY must be in the closed interval `[min_cashout_allowed,max_cashout_allowed]`
* `channel_total_cashout + payout` amount of $JOY must be transferrable from the Council Budget
* `pull_payout_element` must verify the [merkle proof](#merkle-proof) mechanism

#### Effect

* `payout` amount of $JOY is transferred from the Council Budget usable balance into the `reward_account` usable balance
* `cumulative_channel_reward` for channel `channel_id` is increased by `payout`

### Claim And Withdraw Channel Reward

Allow a channel to claim a `payout` and have the $JOYs deposited into the controller account if the channel is member-owned otherwise funds go into the Council Budget for non member-owned channels

**Parameters**

| Name                | Description                                                                                                                                                 |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `actor`             | either a member, lead or curator claiming the `payout`                                                                                                      |
| `proof`             | merkle proof used for verifying whether the `payout` the channel is claiming is legit or not                                                                |
| `pull_payment_item` | the information containing the `payout` plus some other data used in the validation process and also the `channel_id` for which the `payout` is destined to |

#### Condition

* `channel_id`: must refer to an existing `channel`
* `channel`: must not have the `[CreatorCashout, ChannelFundTransfer]` features paused
* `actor`: must be endowed with `[RewardChannelReward,WithDrawFromChannelBalance]` permissions
* `channel_cashout_enabled`: must be `true`
* `channel_total_cashout + payout` amount of $JOY must be in the closed interval `[min_cashout_allowed,max_cashout_allowed]`
* `channel_total_cashout + payout` amount of $JOY must be transferrable from the Council Budget
* `pull_payout_element` must verify the [merkle proof](#merkle-proof) mechanism

#### Effect

* `payout` amount of $JOY is transferred from the Council Budget usable balance into the `reward_account` usable balance first. After that they are transferred from the `reward_account` into the `destination_account` in the following fashion:
  * if channel is member-owned then the `payout` amount is transferred into the member controller account usable balance
  * if channel is curator/lead-owned the `payout` amount is burned from the `reward_account` and minted into the Council budget Account
* `cumulative_channel_reward` for channel `channel_id` is increased by `payout`


# Video NFTs

The canonical association to a piece of video content.

## Preamble

This article will later be incorporated into the document at a later time.

## Introduction

A passionate follower of some creator or topic may find significant value in being able to associated themselves with related creative content. They key is that the association is very public and visible, and thereby allows the owner to signal to their community and related audiences something about who they are, the resources they have - financial, social and human, in the latter for example by having discerning taste or judgement. This behavior is already very prevalent in lots of social arenas in the off-line world, in particular among collectors. People collect stamps, coins, art, cars, sneakers, etc., and motivations span:

1. inherent enjoyment in collecting anything durable, sort of like a hobby, where you also get to curate a collection
2. a tasteful way to demonstrate you have access to resources like time, money and information
3. showing you have good judgement and taste
4. owning a piece of history in some way
5. making a good investment

Video NFTs play into all of these objectives, and arguably substantially amplify your ability to achieve those goals through the following

* **More public:** you and your assets are visible for everyone to see, and is a highly entertaining and viral medium, e.g. unlike a painting hanging on a wall in your house.
* **More financial:** anyone can bid on and buy your assets 24/7/365, giving you increased liquidity and greater price discovery. This makes it particularly valuable to be early, with good judgement, before something goes viral or gains attention, and perhaps even makes it valuable to be able to market something and make it valuable.

Hence the name of the game here is really to empower the owner to be visible as the owner, to support all these goals, and to make it easy to transact in this ownership. For the creator the value is in monetising the exclusive ownership status over their content.

**Critically, owning a video, in the sense described herein, is not a claim on the intellectual property, revenue or any other rivalrous property of using the actual content itself.**

## Concepts

### NFT Owner

An NFT Owner refers to one among two distinct varieties of possible owners of a given NFT, and they are

* **ChannelOwner:** This means that whomever owns the channel in which the video to whicht he NFT corresponds, is also the owner of the NFT.
* **Member:** This means that a given specified member owns the video.

Notice that curator groups, curators or the content directory lead cannot directly own an NFT, only by owning the channel. This also implies that, as will be seen further down, neither of these actors can participate as possible beneficiaries of an NFT, for example as bidders, buyer or offer recipients.

### Auction Type

An *Auction Type* refers to one among the two distinct varieties of auctions which can be used to reallocate ownership of an NFT, hence it is either

* **English:** An *English Auction* is an auction where the highest bid, above some possibly set *reservation price*, is maintained and updated over time until some block in the future, called the *finalization block.* Lastly there is an associated notion of an *extension period*, which is a period before the finalization bloc, where submitting any bid below the possible *buy now* price will result in pushing the finalization block forward by the extension period. This is to avoid sniping.
* **Open:** An *Open Auction* is an auction which operates the same way as an English auction, except that there is no predefined duration, hence no finalization block. For this reason there is a *bid locking duration*, which is the number of blocks which must pass from the time a bid is submitted until the bidder can withdraw the bid if it is not accepted by the current owner.

In both kinds of auctions, there is the concept of a *buy now* price, which if set, means that if any bid matches this amount at any time, the auction is automatically concluded in favor of this bid.

### Bid

A *bid* represents a binding financial commitment from a member to aquire ownership of an NFT from the current owner at a given price in an auction, and it is defined by

* **Bidder:** The identifier of the member who is making the bid.
* **Account:** The identifier for the account which holds the committed funds, which will be encumbered by a reservation for as long as the bid exists.
* **Amount:** The balance of funds committed in the bid.
* **MadeAtBlock:** The block number in which the bid was created.

Notice that only members can be bidders, no other kind of actor.

### Auction

An *auction* represents the structured process through which the current owner can transfer ownership of an NFT through an open non-intermediated bidding process, and it is defined by the following

* **Reservation Price:** The minimum balance required for amount of any bid to be valid.
* **Type:** The representing what type of auction this is.
* **Minimal Bid Step:** The minimum difference in amount between two consecutive bids in order for the new bid to be valid.
* **Last Bid:** If present, is the last which was successfully submitted.
* **Starts At:** The block after which point it becomes possible to submit bids in the auction.
* **Whitelist:** The set, which if non-empty, contains the set of members who are permitted to submit bids in the auction.

### Content Actor

A *Content Actor* represents a potential owner of an NFT, either through direct ownership or control over a channel.

* **Curator:** A given curator in a given curator group. The curator is a valid controller of any channel owned by a curator group in which it is a member.
* **Member:** A given member.
* **Lead:** The lead of content directory working group. The lead is a valid controller of any channel owned by a curator group.

**NB: This is top level content directory concept, so this will def. need to be moved out of the NFT specific section.**

### Transactional Status

The *transactional status* of an NFT represents the state of current opportunities to change the ownership, and it has the following distinct varieties

* **Idle:** There is no currently available opportunity, and the only relevant action is to transition to one of the other three other statuses.
* **Offered:** The current owner has extended an offer for a specific other member, called the *beneficiary* to acquire ownership of the NFT, possibly for some require payment. In this status, the beneficiary can at any time accept the offer, which results in title transfer, or the current owner can withdraw the offer, which results in returning to the idle status.
* **Auction:** There is a currently active auction of some kind, as represented by an . The owner can cancel the auction at any time so long as there is no bid currently present in the auction. Anyone, possible screened by the whitelist, is free to submit a bid, which will only be successfully registered if is the amount is no less than the minimum bid step above any prior existing bid, or if it is the first bid, is no less than the reservation price. Beyond this, things work a bit differently depend on on the `AuctionType`
  * **Open:** Once a bid is submitted, it can also be withdrawn at any time by the bidder, so long as the locking duration has passed since the time of submission of this bid. The winner is selected by being chosen by the owner as the winning bid through.
  * **English:** A bid cannot be withdrawn, and any bid which still is active when the finalization block has passed, is considered the winner. Any bid submitted within the extension period of the current finalization block results in the finalization block being pushed forward by the extension period. In order to trigger the actual competiton of the auction, causing the ownership transfer and payments, an active call must be made after the finalization block has passed.
* **Buy Now:** Anyone can instantly become the owner by paying a given amount. The owner can change the status back to idle at any time.

The stages and transitions are summarized in the image below.

### NFT

An *NFT* represents ownership title over video, and it is defined by the following information

* **Owner:** The representing the current owner.
* **Status: The** representing the state of the NFT currently.
* **Royalty:** If set, it specifies the fraction of the paid value of later transactions which must accrue to the issuer.

### Minting Limits

Minting limits on NFTs are applied to avoid massive proliferation of low quality NFT projects.

There are several types of limits, depending on the time window considered and context:

**Global Limits**

* Global weekly limit: max number of NFT that can be minted each week, stored on chain
* Global daily limit: max number of NFT that can be minted each day, stored on chain

The above values are updated via a [`UpdateGlobalNftLimit`](/system/proposal-system#update-global-nft-limit) proposal.

**Channel Limits**

* Channel weekly limit: max number of NFT that can be minted each week by a particular channel, they are set at channel creation with a value of `DefaultWeeklyChannelLimit`
* Channel daily limit: max number of NFT that can be minted each day by a particular channel, they are set at channel creation with a value of `DefaultDailyChannelLimit`

Their value can be [modified](#update-channel-nft-limit) on a per-channel basis by the Lead or a Curator having sufficient permission level.

#### Toggling the limit functionality

The whole limiting functionality can be disabled. The status of the NFT limit functionality can be inspected by looking at the `Content.nft_limit_enabled` value on chain

## Parameters

The following mutable parameters.

| Name                        | Type          | Description                                                       |
| --------------------------- | ------------- | ----------------------------------------------------------------- |
| `MinRoundTime`              | `BlockNumber` | Min auction round time.                                           |
| `MaxRoundTime`              | `BlockNumber` | Max auction round time.                                           |
| `MinBidLockDuration`        | `BlockNumber` | Min bid lock duration.                                            |
| `MaxBidLockDuration`        | `BlockNumber` | Max bid lock duration.                                            |
| `MinStartingPrice`          | `Balance`     | Min auction staring price.                                        |
| `MinCreatorRoyalty`         | `Perbill`     | Min creator royalty percentage.                                   |
| `MaxCreatorRoyalty`         | `Perbill`     | Max creator royalty percentage.                                   |
| `MinBidStep`                | `Balance`     | Min auction bid step.                                             |
| `MaxBidStep`                | `Balance`     | Max auction bid step.                                             |
| `AuctionFeePercentag`       | `Perbill`     | Auction platform fee percentage.                                  |
| `AuctionStartsAtMaxDelta`   | `BlockNumber` | Max delta between current block and starts at.                    |
| `DefaultWeeklyChannelLimit` | `u64`         | Max amount of NFT that a newly created channel can mint in a week |
| `DefaultDailyChannelLimit`  | `u64`         | Max amount of NFT that a newly created channel can mint in a day  |

## Constants

The following constants are hard coded into the system, they can only be updated with a runtime upgrade.

| Name             | Description                                                               | Value     |
| ---------------- | ------------------------------------------------------------------------- | --------- |
| `INVITE_LOCK_ID` | The identifier value for the lock applied to root account of a new member | `fill-in` |
| `xxx`            |                                                                           | `fill-in` |

## Extrinsics

### Issue NFT

**Parameters**

| `actor`    | The `ContentActor` attempting to issue the NFT.                |
| ---------- | -------------------------------------------------------------- |
| `video_id` | Video on which NFT is to be issued.                            |
| `royalty`  | If present, the royalty for all future auctions                |
| `metadata` | The raw metadata for the issuance.                             |
| `to`       | If present, the member who should be set as the initial owner. |

#### Conditions

* WIP.

#### Effect

* WIP.

### Offer NFT

**Parameters**

| Name       | Description                                                               |
| ---------- | ------------------------------------------------------------------------- |
| `video_id` | The video corresponding to the NFT for which offer is to be made.         |
| `owner_id` | `ContentActor` identifying caller.                                        |
| `to`       | The beneficiary member of the offer.                                      |
| `price`    | If present, the amount which must be paid by beneficiary to accept offer. |

#### Conditions

* WIP.

#### Effect

* WIP.

### Accept Offer

**Parameters**

| Name           | Description                                                       |
| -------------- | ----------------------------------------------------------------- |
| `video_id`     | The video corresponding to the NFT for which offer is to be made. |
| `recipient_id` | Identifier of beneficiary.                                        |

#### Conditions

* WIP.

#### Effect

* WIP.

### Sell NFT

**Parameters**

| Name       | Description                                                       |
| ---------- | ----------------------------------------------------------------- |
| `video_id` | The video corresponding to the NFT for which offer is to be made. |
| `owner_id` | `ContentActor` identifying the caller.                            |
| `price`    | Price of buying the NFT.                                          |

#### Conditions

* WIP.

#### Effect

* WIP.

### Buy NFT

**Parameters**

| Name             | Description                                                        |
| ---------------- | ------------------------------------------------------------------ |
| `video_id`       | The video corresponding to the NFT for which offer is to be made.  |
| `participant_id` | Member attempting to buy the NFT.                                  |
| `metadata`       | User provided metadata associated with the purchase and ownership. |

#### Conditions

* WIP.

#### Effect

* WIP.

### Start NFT Auction

**Parameters**

| Name                | Description                                                                                                   |
| ------------------- | ------------------------------------------------------------------------------------------------------------- |
| `owner_id`          | `ContentActor` who owns the NFT.                                                                              |
| `video_id`          | Video identifier for video to which NFT corresponds.                                                          |
| `auction_type`      | AuctionType designating what kind of auction should be used.                                                  |
| `reservation_price` | Balance that sets lower bound for first valid bid amount.                                                     |
| `minimal_bid_step`  | Balance that sets lower bound for how much each new bid must exceed last valid bid amount, if it exists.      |
| `buy_now_price`     | If provided, the Balance at which one would instantly be able to buy the NFT.                                 |
| `starts_at`         | If provided, the BlockNumber at which it becomes possible to bid in the auction or buy now.                   |
| `whitelist`         | Set of membership identifiers which, at leats one is provided, restricts bidders or buyers to be among these. |

#### Conditions

* WIP.

#### Effect

* WIP.

### Make Bid

**Parameters**

| Name             | Description                                                       |
| ---------------- | ----------------------------------------------------------------- |
| `participant_id` | Member attempting to buy the NFT.                                 |
| `video_id`       | The video corresponding to the NFT for which offer is to be made. |
| `bid_amount`     | The amount of the bid.                                            |
| `metadata`       | User metadata in the bid.                                         |

#### Conditions

* WIP.

#### Effect

* WIP.

### Cancel Open Auction Bid

**Parameters**

| Name             | Description                                                       |
| ---------------- | ----------------------------------------------------------------- |
| `participant_id` | Member attempting to buy the NFT.                                 |
| `video_id`       | The video corresponding to the NFT for which offer is to be made. |

#### Conditions

* WIP.

#### Effect

* WIP.

### Claim Won English Auction

**Parameters**

| Name        | Description                                                        |
| ----------- | ------------------------------------------------------------------ |
| `member_id` | To be root account of membership.                                  |
| `video_id`  | The video corresponding to the NFT for which offer is to be made.  |
| `metadata`  | User provided metadata associated with the purchase and ownership. |

#### Conditions

* WIP.

#### Effect

* WIP.

### Cancel Transaction

**Parameters**

| Name       | Description                                                        |
| ---------- | ------------------------------------------------------------------ |
| `owner_id` | `ContentActor` who owns the NFT.                                   |
| `video_id` | The video corresponding to the NFT for which offer is to be made.  |
| `metadata` | User provided metadata associated with the purchase and ownership. |

#### Conditions

* WIP.

#### Effect

* WIP.

### Pick Open Auction Winner

**Parameters**

| Name       | Description                                                        |
| ---------- | ------------------------------------------------------------------ |
| `owner_id` | `ContentActor` who owns the NFT.                                   |
| `video_id` | The video corresponding to the NFT for which offer is to be made.  |
| `metadata` | User provided metadata associated with the purchase and ownership. |

#### Conditions

* WIP.

#### Effect

* WIP

### Sling Back

**Description**

Returns ownership of idle status NFT from owner to channel itself, even if the channel is owned by the same member who possibly is the owner.

**Parameters**

| Name       | Description                                                            |
| ---------- | ---------------------------------------------------------------------- |
| `video_id` | The video corresponding to the NFT for which sling back is to be done. |
| `owner_id` | `ContentActor` who owns the NFT.                                       |

#### Conditions

* WIP.

#### Effect

* WIP.

### Update Channel NFT Limit

**Parameters**

| Name               | Description                                                       |
| ------------------ | ----------------------------------------------------------------- |
| `actor`            | `ContentActor` either Lead or Curator with sufficient permission. |
| `nft_limit_period` | Type of period for the limit: either Weekly or Daily.             |
| `channel_id`       | Channel which limit we want to set.                               |
| `limit`            | Value for the new limit to be set.                                |

#### Conditions

* WIP.

#### Effect

* WIP


# Curation Model

A temporary one page explainer for curators

This is meant as a placeholder description for the benefit of the content curation working group before a full article can be prepared.

The curator model is centered around workers, and the lead, in the curator working group. These actors can engage in a range of special activities in the content directory which others canno

## Frozen Features

* Update permissions of a curator group.
* Update channel privilege level.
* Update channel NFT limit.
* Update paused features of a channel.

Certain features are currently implemented, but frozen - hence not accessible on the runtime, due limit scope of testing required for mainnet.

## Moderation Actions

These actions, referred to as *curation*, are currently

```
pub enum ContentModerationAction {
    // Related extrinsics:
    // - `set_video_visibility_as_moderator`
    HideVideo,
    // Related extrinsics:
    // - `set_channel_visibility_as_moderator`
    HideChannel,
    // Related extrinsics:
    // - `set_channel_paused_features_as_moderator` - each change of `PausableChannelFeature` `x` requires permissions
    //   for executing `ChangeChannelFeatureStatus(x)` action
    ChangeChannelFeatureStatus(PausableChannelFeature),
    // Related extrinsics:
    // - `delete_video_as_moderator`
    DeleteVideo,
    // Related extrinsics:
    // - `delete_channel_as_moderator`
    DeleteChannel,
    // DeleteVideoAssets(is_video_nft_status_set)
    // Related extrinsics:
    // - `delete_video_assets_as_moderator` - deletion of assets belonging to a video which has an NFT issued
    //   requires permissions for `DeleteVideoAssets(true)` action, deleting other video assets requires permissions for
    //   `DeleteVideoAssets(false)`.
    DeleteVideoAssets(bool),
    // Related extrinsics:
    // - `delete_channel_assets_as_moderator`
    DeleteNonVideoChannelAssets,
    // Related extrinsics:
    // - `update_channel_nft_limit`
    UpdateChannelNftLimits,
}
```

Notice a few things

* hiding is a signal which causes Argus to not distribute this content to end-users, for channels it implies all dataobjects for the channel and any video in the channel, and for a video it implies all data objects for that video.
* deleting a video is only possible if no NFT has been issued, however, deleting data objects for this video is always possible.
* deleting a channel is only possible if it has no videos, and it involves delting all data objects for this channel.
* where `PausableChannelFeature` encompasses the following channel features which can be paused.

```
pub enum PausableChannelFeature {
// Affects:
// -`withdraw_from_channel_balance`
// -`claim_and_withdraw_channel_reward`
ChannelFundsTransfer,
// Affects:
// - `claim_channel_reward`
// - `claim_and_withdraw_channel_reward`
CreatorCashout,
// Affects:
// - `issue_nft`
// - `create_video` (if `auto_issue_nft` provided)
// - `update_video` (if `auto_issue_nft` provided)
VideoNftIssuance,
// Affects:
// - `create_video`
VideoCreation,
// Affects:
// - `update_video`
VideoUpdate,
// Affects:
// - `update_channel`
ChannelUpdate,
// Affects:
// - `issue_creator_token`
CreatorTokenIssuance,
}
```

## Curator Groups and Permissions

Since many of these actions are quite invasive, there is a permissioning system which constrains exactly what curator can do what actions on a given channel. To make the management of curators, and their permissions, practical at scale, the concept of *curator groups* has been introduced. A curator can be part of none or many such groups, and the permissions exist at the group level, not curator. Channels are partitioned into permission levels called *channel privelege levels*. When first created a channel has a default level, but this can be updated later by the lead. The levels themselves have no direct meaning, and are identified by integers, but there is no relationship between the permissions of a channel and the level. The permissions of a curator group describe what moderation actions group members can do for different levels. If no such action description exists for a given permission level, then group members cannot do any moderation action at all for channels with this level.

##


# Storage & Bandwidth

Storing and distributing static assets, such as videos, avatars, covers and attachments, to end users is a key service of the network, and a dedicated subset of actors in the DAO operate dedicated nod

## Preamble

This subsystem is under active development, and this document attempts to both explain how the current production system (as of Giza) works, as well as give indications about what is expected to be added later before mainnet.

## Introduction

The network has a variety of static data assets used in different context. In the content directory, for example, there are avatars and cover images used for channels, and also preview thumbnail images for videos, as well as the video media itself, which may exist in multiple different resolutions and encodings for the same content. In the membership system images are used for the avatar of a user, and there is also a generalized storage capability. There is also a generalised storage service for the benefit of the council and each individual working group, which are intended to be used for storing assets that are of use to actors occupying the given roles over time, or to the platform as a whole.

## Notion Space&#x20;

The community maintains a distinct Notion space which holds more dynamic information on the activities of the storage and bandwidth working groups.

{% embed url="<https://joystream.notion.site/Storage-9dc5a16444934dc4bda08b596bc15375>" %}

{% embed url="<https://joystream.notion.site/Distribution-1f4cfbbb2e934c79bf20b8db7f019d32>" %}

## Storage Provider

### Responsibilities

* Run and maintain storage nodes that store very large quantities of static data, synchronize with other storage nodes, share data with distributors, and accepts inbound uploads from end users

### Requirements

* Experienced with how to setup and maintain high performance IT infrastructure
* Access to highly performant and reliable IT infrastructure with high storage capacity
* Hold sufficient amount of the native platform token to put at stake

## Storage Provider Leader

### Responsibilities

* The Storage Lead is responsible for ensuring that Storage Providers are performing adequately. Each one must hold a complete and up-to-date copy of the content directory and ensure uptime in order to effectively serve content consumers.

### Requirements

* Experienced with how to setup and maintain high performance IT infrastructure
* Ability to effectively manage and coordinate the actions of the Storage Providers
* Hold sufficient amount of the native platform token to put at stake

## Distributor

### Responsibilities

* Run and maintain distributor nodes that deliver large volumes of upstream data to a large number of simultaneous end users

### Requirements

* Experienced with how to setup and maintain high performance IT infrastructure
* Access to highly performant and reliable IT infrastructure, with high storage capacity and a lot of upstream capacity
* Located within certain bounds to designated geographic areas, in order to limit latency
* Hold sufficient amount of the native platform token to put at stake

## Distributor Lead

`wip`

## Philosophy

### Introduction

The handbook largely attempts to not motivate or frame how the system works, however, in the case of this subsystem it is sufficiently complex that it invariably generates a range of questions when confronted by newcomers, hence we attempt to address this more directly here.

### Model

There is an obvious first-principles question of why the network has these capabilities built in, in the sense that it directly finances and coordinates the provisioning of these services internally through its own administrative bureaucracy, and using its own custom technology stack. Why can't the system just rely on AWS or some other similar offering?

The reason for this is that the network, as represented by its governance apparatus, cannot enter into traditional contractual arrangements directly with off-chain entities, like the operators of traditional cloud infrastructure. This is a consequence of both the contemporary business model constraints of such operators, and the limits of what most jurisdictions would consider valid counterparties in an enforceable contract. The only way to bridge this gap would be if the platform selected some on-chain intermediary who effectively custodied the business relationship with the platform on behalf of the network, however, this would be prohibitively risky should this actor become faulty or byzantine, as there would be no recourse.

A third alternative would have been to rely on other middleware blockchain systems which have been explicitly built for the purpose of providing these services, this includes offerings such as [Arweave](https://www.arweave.org), [Sia+SkyNet](https://siasky.net), [Filecoin](https://filecoin.io), [Storj](https://www.storj.io), [Meson](https://meson.network) and similar systems. Each of these systems have their own idiosynchratic reason for why they may not be an ideal fit, or at least complete fit for the Joystream network. Some our pure storage solutions, other are pure CDN solutions, others are mixtures. Some focus on permanent storage, others on pay-as-you-go. They all provide different security models. However, uniformly, they all have the following problems

* **Immature:** All of them are still very technically immature, both in tooling, documentation and community of developers using at any sort of serious scale. It is difficult to commit building anything beyond a prototype or proof-of-concept
* **Opaque:** It is notoriously difficult to get a complete and accurate picture of what exact technical and economical guarantees the systems provide, and will provide, in the future. This is largely a consequence of the immaturity, but also that many question are likely unresolved, making it infeasible for the developers to commit to highly specific longer term roadmaps and timeline. The credibility of any actual public commitments are hard to evaluate.
* **Isolated:** A critical requirement for any efficient platform is that it covers the costs and management on a variety of services on behalf of the users. This applies to the Joystream network as well, which means it must be able to deeply interoperate with any external middleware system, including control funds, issue service provisioning and manage ongoing expenses. This kind of complex interoperability between these systems is not feasible or simple at this time.
* **Temporary:** When looking at the evolution of other large scale video provisioning platforms, such as YoutTube and Netflix, they invariably start out relying on external infrastructure, but over time they converge to relying on their own internally provisioned infrastructure. This is both because this infrastructure ends up becoming too strategically valuable to risk having to periodically bargain over it, and also because any discrepancy between the ideal cost structure and feature set required by client and their infrastructure ends up becoming prohibitibley costly when magnified by scale.

An important nuance to be aware of is that once the immaturity and opacity is no longer an issue, it would be feasible for the Joystream network to adopt, or augment, the technology - in terms of protocol and software - of the most appropriate third party alternative, while sidestepping the latter two problems of isolation and temporaryness. This is because there is a fundamental distinction between using the same technology, and using the same specific instantiated running external system for which that technology was initially built. So as an example, this would be the difference between using Filecoin the network and [Lotus](#bandwidth) the full node software for that network.

### Design

WIP.

## Roles

There are two working groups involved in storage and bandwidth provisioning, one per subsystem. To learn more about working groups in general, please consult the [https://github.com/Joystream/handbook/blob/master/system/storage/broken-reference/README.md](https://github.com/Joystream/handbook/blob/master/system/storage/broken-reference/README.md "mention") document. The roles are here tasked with the following, beyond the normal working groups activities inherent to each:

### Storage

* **Storage Lead:** Briefly stated, the lead manages
  * what set of storage providers should store what data.
  * what storage workers can actively participate as storage providers.
  * how different categories of data should be automatically stored once uploaded.
  * the size sensitive component of the upload price.
  * the replication factor on future uploads.
  * the upload blacklist.
  * whether uploads are globally allowed or not at any given time.
* **Storage Worker/Provider:**
  * Accepts and validates data uploaded from users for storage.
  * Replicates data initially stored with other storage providers.
  * Shares data with other storage providers and bandwidth providers.
  * Maintains public host resolution metadata.
  * Is incentivized by a mix of
    * user payment for uploads
    * probabilistic on-chain proof-of-storage challenges (not in Giza)
    * payment from peer providers & bandwidth providers when providing data (not in Giza)
    * slashing by discretion from group lead, with subsequent loss of reputational capital of membership in this role.
    * working group payments

### Bandwidth

* **Bandwidth Lead:**
  * what set of bandwidth providers should distribute what data.
  * what bandwidth providers can actively participate as providers.
  * how different categories of data should be automatically distributed.
  * policy metadata for groups
* **Bandwidth Worker/Provider:**
  * Sends data to users on demand.
  * Replicates data from storage providers following local caching policy.
  * Maintains public host resolution metadata.
  * Is incentivized by a mix of
    * slashing by discretion from group lead, with subsequent loss of reputational capital of membership in this role.
    * payment from gateway providers (not in Giza)
    * working group payments

## Concepts

### Data Directory

An on-chain system which holds relevant state required to represent the data which is currently being stored, along with information about who owns it, how it is stored and how it is distributed. It also holds policy information about how to handle requests to introduce new data into the system. The node software that is operated by compliant storage and bandwidth providers uses this state as the ultimate source of truth for what they should be doing at any given time. There are a range of different extrinsic which facilitate updating the state of this system, such as uploading or deleting new data, or updating what a given provider should be doing.

For a detailed overview of how this system works, please review the [https://github.com/Joystream/handbook/blob/master/system/storage/broken-reference/README.md](https://github.com/Joystream/handbook/blob/master/system/storage/broken-reference/README.md "mention") document.

### Nodes

There are two distinct node type, storage nodes and bandwidth nodes, each being a network peer that the corresponding provider type operates in order to provision their service to the network. There is a reference software implementation of each node, called *Colossus* and *Argus, respectively*, but in principle there could be alternative node implementations for the same underlying protocol described in document.

Storage nodes are primarily involved in

* accepting uploads from users,
* downloading data to be stored from other storage nodes,
* uploading data to other storage nodes and bandwidth nodes that may require it,
* dropping data which is deleted from the network,

and bandwidth nodes are primarily involved in

* uploading data to users upon request,
* downloading data from storage providers in accordance with local caching policy,

There is no direct protocol level enforcement of what service-level agreement each node should conform to, for example in terms of

* storage capacity
* up-time
* up-speed
* down-speed
* connection capacity
* latency w\.r.t. a given location

but each provider type faces a range of different incentives that aim to encourage them to comply with agreed upon standards out-of-band, in the working group. There exists on-chain information for resolving the, or a, host corresponding to the node of a given provider, and the provider is free to update this mapping as needed. There is also no protocol level awareness of what kind of underlying infrastructure is powering the node, but for the purposes of this documentation one can imagine it to be a single host serving the reference API in a way described by the protocol, and with a single canonical internal state and view of the data directory and blockchain.

For a detailed overview of how each node works, please review the [https://github.com/Joystream/handbook/blob/master/system/storage/broken-reference/README.md](https://github.com/Joystream/handbook/blob/master/system/storage/broken-reference/README.md "mention")and [https://github.com/Joystream/handbook/blob/master/system/storage/broken-reference/README.md](https://github.com/Joystream/handbook/blob/master/system/storage/broken-reference/README.md "mention") respectively.

### Service Game

WIP.

## Architecture

The key architectural properties of the system is as follows

* Distinct roles for storage and distributing data.
* Storage with redundancy and only partial replication in nodes, but no erasure coding on individual data.
* Bandwidth provisioning with flexible policy space, allowing for Content Delivery Network (CDN) like organization.
* The blockchain holds index of data, including ownership and metadata, and critical information about what service provider is obliged to perform storage and distribution for a given piece of data.
* When new data is added to the system, the blockchain has built in policies for deciding how storage and bandwidth services should be provisioned, but there is also room for manual intervention later to augment or change these initial determinations.

This can all be succinctly summarize in the following figure.

![System Architecture](/files/ZXSznvS2PQod1pLwTl0e)


# Data Directory

An on-chain index of all data stored and distributed in the system, with associated information about ownership and what providers are tasked with storing and providing bandwidth.

## Introduction

This subsystem serves to retain an index of all static assets, including who owns them, and information about what actors are currently tasked with storing and distributing them to end users. It has no awareness of the underlying content or purpose represented by such an object, as it is used by different parts of the system. It can represent assets as diverse as

* Video media, with a particular resolution and encoding, living in the content directory.
* Image media used for the avatar of a member, an election candidate or for the cover of a channel in the content directory.
* A data attachment to a blog post, proposal or role application.

In the future it will be extended with autonomous interactive auditing features for storage providers and payments between service providers to incentive replication.

## Concepts

### Data Directory Account

The *data directory account* is a module account for holding funds in the data directory, and it has an account id denoted by `DATA_DIRECTORY_ACCOUNT_ID`.

### Data Object

A *data object* represents a single static data asset, like an image or video media, and it is defined by the following information

* `id`: A unique immutable non-negative integer identifying an individual data object, is automatically assigned by the blockchain upon creation.
* `accepted`: Whether the object has been verified as correctly uploaded to an initial storage providers. The storage provider making such a confirmation for a given object is referred to as the *liasion* for the object.
* `deletion_prize`: An amount of funds locked up as a state bloat bond for the object.
* `size`: The claimed size of the object, as stipulated during creation by the owner, and implicitly understood to be verified by the liasion.
* `hash`: The IPFS CID of the object, specifically SS58 format of Multihash with blake3 hashing algorithm.

### Bag Id

A *Bag Id* is a value which can identify a specific bag, see section on [#bag](#bag "mention") for further elaboration, and it takes one of the following varieties

* `static`: a *static bag id* identifies one of the built in bags in the system, and it comes in one of the following subvarieties
  * `council`: identifies the bag reserved for the council to manage through its proposal system.
  * `membership`: identifies the bag for the membership working group.
  * `storage`: identifies the bag for the storage working group.
  * `bandwidth`: identifies the bag for the bandwidth working group.
  * `content`: identifies the bag for the content directory working group.
  * `forum`: identifies the bag for the forum working group
  * `operations_alpha`, `operations_beta` ...: each identifies the bag for the corresponding operations working group.
* `dynamic`: a *dynamic bag id* identifies one of the dynamic, so not built in, bags in the system, and it comes in one of the following subvarieties:
  * `member`: has associated membership id and identifies bag for this member.
  * `channel`: has associated channel id, and identifies the bag for this channel.

### Bag

A *data object bag*, or *bag* for short, is a dynamic collection of data objects which can be treated as one subject in the system. Each bag has an owner, which is established when the bag is created. A data object lives in exactly one bag, but may be moved across bags by the owner of the bag. Only the owner can create new data objects in a bag, or opt into absorbing objects from another bag. The purpose of the concept of bags is to limit the on-chain transactional footprint of administrating multiple objects which should be treated the same way. This is achieved by establishing a small immutable identifier for these objects. The canonical example would be assets that will be consumed together, such as the cover photo and different video media encodings of a single piece of video content. Storage and distribution nodes have commitments to bags, not individual data objects.

A bag is defined by the following information

* `id` : an id of type [#bag-id](#bag-id "mention") for this bag.
* `stored_by`: set of ids for [#storage-bucket](#storage-bucket "mention") tasked with storing the objects in the bag.
* `distributed_by`: set of ids for [#distributor-bucket](#distributor-bucket "mention") tasked with distributing the objects in the bag.
* `deletion_prize`: amount of money placed in [#undefined](#undefined "mention") as staking bond for cleaning up unused bag.
* `object_size`: cumulative size of all data objects in the bag.
* `object_count`: cumulative number of objects in the bag.

### Storage Bucket

A *storage bucket* represents a commitment to hold some set of bags for long term storage. A bucket may have a *bucket operator*, which is a single worker in the storage working group. There is distinct *bucket operator metadata* associated with each, which describes things such as how to resolve the host. The operator of a bucket may change over time. As previously described, when new dynamic bags are created, they are allocated to one or more such buckets, unless the bucket has been temporarily disabled from accepting new bags.

* `id` **:** a unique immutable non-negative integer identifying an individual storage bucket, is automatically assigned by the blockchain upon creation.
* `operator_status`**:** status of bucket operator, is one of the following varieties
  * `missing`: when there is no operator.
  * `invited`: with associated [https://github.com/Joystream/handbook/blob/master/system/storage/broken-reference/README.md](https://github.com/Joystream/handbook/blob/master/system/storage/broken-reference/README.md "mention") id in storage working group, when the lead has invited the given worker to operate the bucket.
* `accepting_new_bags`: whether this this bucket is an acceptable destination for additional bags.
* `total_size_limit`: upper bound on cumulative size of all data objects in bucket.
* `object_count_limit`: upper bound on cumulative number of all data objects in bucket.
* `total_size`: cumulative size of all data objects in bucket.
* `object_count`: cumulative number of all data objects in bucket.

### Distribution Bucket

A *distribution bucket* represents a commitment to distribute a set of bags to end users. A bucket may have multiple *bucket operators*, each being a worker in the distribution working group. The same metadata concept applies here as well, and additionally covers whether the operator is live or not. Bags are assigned to buckets when being uploaded, or later by the lead by manual intervention.

* `id` : a unique immutable non-negative integer identifying an individual distributor bucket, is automatically assigned by the blockchain upon creation.
* `accepting_new_bags` : whether this bucket is an acceptable destination for additional bags.
* `distributing`: whether assigned operators are servicing this bucket at the moment.
* `pending_invitations`: set [https://github.com/Joystream/handbook/blob/master/system/storage/broken-reference/README.md](https://github.com/Joystream/handbook/blob/master/system/storage/broken-reference/README.md "mention") ids from bandwidth working group for workers who have been invited and can join as operators.
* `operators`: set [https://github.com/Joystream/handbook/blob/master/system/storage/broken-reference/README.md](https://github.com/Joystream/handbook/blob/master/system/storage/broken-reference/README.md "mention") ids from bandwidth working group for workers currently acting as operators.
* `assigned_bags`: the number of bags currently assigned to this bucket.

### Distribution Bucket Family

Buckets are partitioned into so called *distribution bucket families*. These families group buckets with interchangeable semantics from distributional point of view, and the purpose of the grouping is to allow sharding over the bag space for a given service level when creating new bags. Here is an example that can make this more clear. A subset of families could for example represent each country in East Asia, where each family corresponds to a specific country. The buckets in a family, say the family for Mongolia, will be operated by infrastructure which can provide sufficiently low latency guarantees w\.r.t. the corresponding country. The bag for a channel known to be particularly popular in this area could be setup so as to use these buckets disproportionately.

* `id`: a unique immutable non-negative integer identifying an individual distribution bucket family, is automatically assigned by the blockchain upon creation.
* `distribution_buckets`: a map which sends [#distribution-bucket](#distribution-bucket "mention") `id` to the corresponding bucket, and holds all buckets that are part of this family.

### Dynamic Bag Creation Policy

A *dynamic bag creation policy* holds parameter values impacting how exactly the creation of a new dynamic bag occurs, and there is one such policy for each type of dynamic bag, so two, one for `member` and one for `channel`. It describes how many storage buckets should store the bag, and from what subset of distribution bucket families (described below) to select a given number of distribution buckets, specifically

* `number_of_storage_buckets`: number of storage buckets which should replicate the new bag.
* `families`: map of [#distribution-bucket-family](#distribution-bucket-family "mention") id to the number of distribution buckets in the given family one must assign to a new bag for distribution when subject to this policy.

### Blacklist

The *blacklist* is a collection hashes, managed by the lead, which are not allowed for future introductions of data objects in the directory.

## Figures

### Overview

The following overview summarizes the main relationships between the primary concepts.

![Entity Diagram](/files/Vydwuc9LG55u7FJ2J1jD)

## Parameters

The following mutable parameters are part of the system.

| Name                            | Type      | Description                                 |
| ------------------------------- | --------- | ------------------------------------------- |
| `uploading_blocked`             | `Bool`    | Whether all new uploads blocked.            |
| `data_object_per_mega_byte_fee` | `Balance` | Size based pricing of new objects uploaded. |

## Internal Methods

The following set of method can be invoked from within the blockchain itself by other systems, and it is the way that different subsystems unlock the ability to have end-users interact with the storage and bandwidth system, for example allowing channel owners to publish video media into this infrastructure.

### can\_upload\_data\_objects

Validates upload parameters and conditions (like global uploading block). Validates voucher usage for affected buckets.

### upload\_data\_objects

Upload new data objects.

### can\_move\_data\_objects

Validates moving objects parameters, voucher usage for affected buckets.

### move\_data\_objects

Move data objects to a new bag.

### can\_delete\_data\_objects

Validates `delete_data_objects` parameters, voucher usage for affected buckets.

### delete\_data\_objects

Delete storage objects. Transfer deletion prize to the provided account.

### delete\_dynamic\_bag

Delete dynamic bag. Updates related storage bucket vouchers.

### can\_delete\_dynamic\_bag

Validates `delete_dynamic_bag` parameters and conditions.

### create\_dynamic\_bag

Creates dynamic bag. BagId should provide the caller.

### can\_create\_dynamic\_bag

Validates `create_dynamic_bag` parameters and conditions.

### ensure\_bag\_exists

Checks if a bag does exists and returns it. Static Always exists

### get\_data\_objects\_id

Get all objects id in a bag, without checking its existence

## Constants

| Name                                                 | Description                                                                   |
| ---------------------------------------------------- | ----------------------------------------------------------------------------- |
| `DataObjectDeletionPrize`                            | A prize for a data object deletion.                                           |
| `BlacklistSizeLimit`                                 | maximum size of the "hash blacklist" collection.                              |
| `DATA_DIRECTORY_ACCOUNT_ID`                          | A prize for a data object .                                                   |
| `StorageBucketsPerBagValueConstraint`                | "Storage buckets per bag" value constraint.                                   |
| `DistributionBucketsPerBagValueConstraint`           | "Distribution buckets per bag" value constraint.                              |
| `DefaultMemberDynamicBagNumberOfStorageBuckets`      | The default dynamic bag creation policy for members (storage bucket number).  |
| `DefaultChannelDynamicBagNumberOfStorageBuckets`     | The default dynamic bag creation policy for channels (storage bucket number). |
| `MaxRandomIterationNumber`                           | Max random iteration number (eg.: when picking the storage buckets).          |
| `MaxDistributionBucketFamilyNumber`                  | Max allowed distribution bucket family number.                                |
| `MaxDistributionBucketNumberPerFamily`               | Max allowed distribution bucket number per family.                            |
| `MaxNumberOfPendingInvitationsPerDistributionBucket` | Max number of pending invitations per distribution bucket.                    |
| `MaxDataObjectSize`                                  | Max data object size in bytes.                                                |

## Extrinsics

### create\_storage\_bucket

WIP.

### update\_storage\_buckets\_for\_bag

WIP.

### delete\_storage\_bucket

WIP.

### invite\_storage\_bucket\_operator

WIP.

### cancel\_storage\_bucket\_operator\_invite

WIP.

### remove\_storage\_bucket\_operator

WIP.

### update\_uploading\_blocked\_status

WIP.

### update\_storage\_buckets\_per\_bag\_limit

WIP.

### update\_storage\_buckets\_voucher\_max\_limits

WIP.

### update\_number\_of\_storage\_buckets\_in\_dynamic\_bag\_creation\_policy

WIP.

### update\_blacklist

WIP.

### set\_storage\_bucket\_voucher\_limits

WIP.

### accept\_storage\_bucket\_invitation

WIP.

### set\_storage\_operator\_metadata

WIP.

### accept\_pending\_data\_objects

WIP.

### create\_distribution\_bucket\_family

WIP.

### delete\_distribution\_bucket\_family

WIP.

### create\_distribution\_bucket

WIP.

### delete\_distribution\_bucket

WIP.

### update\_distribution\_bucket\_status

WIP.

### update\_distribution\_buckets\_for\_bag

WIP.

### distribution\_buckets\_per\_bag\_limit

WIP.

### update\_families\_in\_dynamic\_bag\_creation\_policy

WIP.

### cancel\_distribution\_bucket\_operator\_invite

WIP.

### remove\_distribution\_bucket\_operator

WIP.

### set\_distribution\_bucket\_family\_metadata

WIP.

### accept\_distribution\_bucket\_invitation

WIP.

### set\_distribution\_operator\_metadata

WIP.


# Storage Node

Storage nodes accept user uploads and do long term archiving of user data objects, as well as share these objects with bandwidth nodes which do low latency last mile on-demand delivery.

## Preamble

This document is not yet complete and is primarily informed by the following more comprehensive documentation

{% embed url="<https://github.com/Joystream/joystream/tree/giza_staging/storage-node-v2>" %}

## Introduction

WIP.

## API

### files/{id}

#### GET

Returns a media file.

{% openapi src="<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>" path="/files/{id}" method="get" %}
<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>
{% endopenapi %}

#### HEAD

Returns media file headers.

{% openapi src="<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>" path="undefined" method="undefined" %}
<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>
{% endopenapi %}

### files

Upload data.

{% openapi src="<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>" path="/files" method="post" %}
<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>
{% endopenapi %}

### authToken

Get auth token.

{% openapi src="<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>" path="/authToken" method="post" %}
<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>
{% endopenapi %}

### state/data-objects

Returns all local data objects.

{% openapi src="<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>" path="/state/data-objects" method="get" %}
<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>
{% endopenapi %}

### state/bags/{bagId}/data-objects

Returns local data objects for a bag.

{% openapi src="<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>" path="/state/bags/{bagId}/data-objects" method="get" %}
<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>
{% endopenapi %}

### version

Returns server version.

{% openapi src="<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>" path="/version" method="get" %}
<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>
{% endopenapi %}

### state/data

Returns local uploading directory stats.

{% openapi src="<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>" path="/state/data" method="get" %}
<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/storage-node-v2/src/api-spec/openapi.yaml>
{% endopenapi %}

## Scenarios

WIP.


# Bandwidth Node

Bandwidth nodes deliver data objects on-demand to end users at low latency by caching data objects and replicating them from storage providers upon cache misses.

## Preamble

This document is not yet complete and is primarily informed by the following more comprehensive documentation

{% embed url="<https://github.com/Joystream/joystream/tree/giza_staging/distributor-node>" %}

## Introduction

WIP.

## API

### status

{% openapi src="<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/distributor-node/src/api-spec/openapi.yml>" path="/status" method="get" %}
<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/distributor-node/src/api-spec/openapi.yml>
{% endopenapi %}

### buckets

{% openapi src="<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/distributor-node/src/api-spec/openapi.yml>" path="/buckets" method="get" %}
<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/distributor-node/src/api-spec/openapi.yml>
{% endopenapi %}

### asset

#### GET

{% openapi src="<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/distributor-node/src/api-spec/openapi.yml>" path="/asset/{objectId}" method="get" %}
<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/distributor-node/src/api-spec/openapi.yml>
{% endopenapi %}

#### HEAD

{% openapi src="<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/distributor-node/src/api-spec/openapi.yml>" path="/asset/{objectId}" method="head" %}
<https://raw.githubusercontent.com/Joystream/joystream/giza_staging/distributor-node/src/api-spec/openapi.yml>
{% endopenapi %}

## Scenarios

WIP.


# Forum

An immutable, auditable, public forum is the main communication and coordination forum among platform members.

## Introduction

The forum is the primary place for community-wide asynchronous written communication about all topics relevant to the platform among members. It is hierarchically organized into a tree of categories, each with designated moderators responsible for policing and encouraging effective and beneficial interactions among members. The moderators are part of a designated forum working group, and the lead of that working group can decide what moderators are responsible for what categories. Categories contain subcategories, and threaded topic-based discussions, called *threads*, where any member can open a thread, and others can come and make replies in the form of *posts*. Some threads can also include a poll, allowing any member to weight in on a question. To create a thread an initial deposit is needed that goes into a thread account, all posts also requires a deposit that goes into the thread account related to the post, whenever a thread is deleted the funds from the thread account are transferred to the caller of the delete extrinsic.

## Notion Space

The community maintains a dedicated Notion space to the operational activities of the forum working group.

{% embed url="<https://joystream.notion.site/Forum-9d4eae77ce7544e0a860fbce4386805d>" %}

## Forum Moderator

### Responsibilities

* Monitor and supervise public communication channels for compliance with usage policies as decided through the governance system
* Communicate with end users about any possible violations and sanctions
* Collaborate to come up with new policies as circumstances change

### Requirements

* A deep understanding of the Joystream platform structure and function
* Clear written communicator, ideally with good command of more than one language
* Hold sufficient amount of the native platform token to put at stake

## Forum Moderator Lead

`wip`

## Roles

The relevant actors in the forum are

* **Member:** A members participate as normal forum users, creating and responding to threads, participating in polls, and so on.
* **Moderator:** A moderator is assigned to one or more categories. Importantly, when assigned to a category, the moderator can act as a moderator in any descendant category. The root category is an exception where moderator can act, only the lead can moderate. Lastly, a moderator cannot participate in normal forum activities *as a moderator,* only in distinct moderator activities.
* **Lead:** The forum lead is a member occupying the lead role in the forum working group. Beyond the normal working group lead obligations, the lead can act as a moderator as well, including in the root category.

## Concepts

### Archiving

Both categories and threads can be *archived.* When a category is directly archived, or is an ancestor of a directly archived category, it is considered archived. If not, its considered *active*. If not, its considered active. In either case, being archived prevents normal users from updating any associated forum state, for example through user level interactions like creating threads, creating posts, reacting to posts, etc. However, actions associated with moderators and the lead are still unconstrained.

### Category

Any non-root category is defined by the following

* **Id:** A global unique category identifier, effectively the number of categories created in the forum prior to this one.
* **Parent:** An optional reference to a parent category. When not set, it indicates this is a root level category.
* **Title:** A human-readable category name.
* **Description:** A human-readable description.
* **Stickied Threads**: A list of threads in this category that have been designated as having long-run importance to the category.
* **Subcategories:** The categories with this category as its parent.
* **Threads:** The threads directly contained in this category.
* **Moderators:** The moderator directly assigned to this category.
* **Archival Status:** Whether the category is archived or not. This impacts whether one can use the category, such as creating threads or posts directly, or recursively, within the category.

### Thread

A thread is defined by the following

* **Id:** A global unique thread identifier, effectively the number of threads created in forum prior to this one. From this one can infer a chronological ordering of threads within the category. Importantly, because of the way information is organized in the blockchain state, the identifier for the category is also also required when identifying a thread.
* **Title:** The human readable title.
* **Author:** Member who created the thread.
* **Poll:** An optional poll for the thread, defined by the following
  * **Description:** A human readable description of what question is being polled.
  * **Deadline:** Some block before which is the only time anyone can participate in the poll.
  * **Alternatives:** A list of alternatives, each with its own explainer text, vote count and members who have voted in favor of it.
* **Number of posts:** Number of posts in thread
* **Cleanup Payoff:** Payoff for deleting thread from storage.

### Post

A post in a thread is defined by the following

* **Id:** A global unique post identifier, effectively the number of posts created in the forum prior to this one. From this one can infer a chronological ordering of posts within the thread. Importantly, because of the way information is organized in the blockchain state, the identifiers for the category and thread are also also required when identifying a post.
* **Text:** The post thread
* **Author:** Member who created the pot.
* **Last edited:** Last time the post was modified by the author
* **Cleanup Payoff:** Payoff for deleting thread from storage.

### Editable/Non-Editable

A thread and a post can be either editable or not editable. A non-editable thread/post doesn't take any space in the runtime's storage. When a thread or post is being deleted it just indicates that you no longer want to edit it. To indicate that you want it to be hidden, similar to deletion in a classical forum, you need to set `hidden` to true. The first post in a thread is by default editable as well as any thread at creation. To create a thread or an editable post you require an initial deposit for the usage of storage space. Non-editable posts doesn't require an initial deposit. Furthermore, by deleting a thread/post you can reclaim the initial deposit.

### Reaction

A *reaction* is a signal a member can send to associate a sentiment, in the form of a non-negative integer called a *reaction value*, with a forum post. Importantly, it is only possible to react, not unreact, that is to withdraw a reaction. The semantics of different values, or of submitting the same value more than once, will require social consensus.

## Constants

The following constants are hard coded into the system, they can only be updated with a runtime upgrade.

| Name                              | Description                                                                                                          | Value     |
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------- | --------- |
| `MAX_SUBCATEGORIES`               | <p>Maximum allowed subcategories in any<br>category to be created.</p>                                               | `fill-in` |
| `MAX_THREADS_IN_CATEGORY`         | <p>Maximum number of threads in any category for<br>any new thread.</p>                                              | `fill-in` |
| `MAX_POSTS_IN_THREAD`             | <p>Maximum number of posts allowed in any thread for</p><p>any new post.</p>                                         | `fill-in` |
| `MAX_MODERATORS_IN_CATEGORY`      | <p>Maximum number of moderators allowed in a category</p><p>for any new assignment of the moderator to category.</p> | `fill-in` |
| `MAX_NUM_CATEGORIES`              | <p>Maximum number of categories allowed in total for</p><p>any new category to be created.</p>                       | `fill-in` |
| `MAX_POLL_ALTERNATIVES`           | Upper bound on the number of alternatives in a new poll.                                                             | `fill-in` |
| `MAX_CATEGORY_TREE_DEPTH`         | <p>Maximum category tree depth allowed for any</p><p>new category to be created.</p>                                 | `fill-in` |
| `MAX_NUMBER_OF_WORKERS`           |                                                                                                                      | `fill-in` |
| `BASE_PAY_OFF_FOR_THREAD_CLEANUP` | Base deposit for a thread                                                                                            | `fill-in` |
| `THREAD_DEPOSIT`                  | Base deposit for creating a thread                                                                                   | `fill-in` |
| `POST_DEPOSIT`                    | Base deposit for creating a post                                                                                     | `fill-in` |

Notice that a lot of the limits are forward-looking. In the even to of a runtime upgrade, it may be that the limits are changed in a more restrictive direction, in which case it should not be expected that there is a migration that throws out the storage state, instead, the limit should only be understood to have bearing on future actions.

## Extrinsics

### Create Category

**Parameters**

| Name          | Description                          |
| ------------- | ------------------------------------ |
| `parent`      | Optional parent category identifier. |
| `title`       | Title of category.                   |
| `description` | Category description.                |

#### Conditions

* Signer is working group lead.
* If provided, `parent` corresponds to valid category. \[Remove: , and it is not directly or indirectly archived.]
* Limit `MAX_NUM_CATEGORIES` is respected.
* Limit `MAX_CATEGORY_TREE_DEPTH` is respected.

#### Effect

A new category is created.

### Update Category Archival Status

**Parameters**

| Name                  | Description                                           |
| --------------------- | ----------------------------------------------------- |
| `actor`               | Either lead or working group identifier of moderator. |
| `category_id`         | Category identifier.                                  |
| `new_archival_status` | Whether new status is archived or active.             |

#### Conditions

* Signer uses role account of `actor`.
* `category_id` corresponds to an existing category.
* If signer is moderator, then this moderator must be assigned have control of the category.
* The category has archival status different from `new_archival_status`.

#### Effect

Archival status of category corresponding to `category_id` is updated to `new_archival_status`.

### Update Category Title

**Parameters**

| Name          | Description                                           |
| ------------- | ----------------------------------------------------- |
| `actor`       | Either lead or working group identifier of moderator. |
| `category_id` | Category identifier.                                  |
| `new_title`   | New title for category.                               |

#### Conditions

* Signer uses role account of `actor`.
* `category_id` corresponds to an existing category.
* If signer is moderator, then this moderator must be assigned have control of the category.

#### Effect

The title of the category corresponding to `category_id` is set to `new_title`.

### Update Category Description

**Parameters**

| Name              | Description                                           |
| ----------------- | ----------------------------------------------------- |
| `actor`           | Either lead or working group identifier of moderator. |
| `category_id`     | Category identifier.                                  |
| `new_description` | New description for category.                         |

#### Conditions

* Signer uses role account of `actor`.
* `category_id` corresponds to an existing category.
* If signer is moderator, then this moderator must be assigned have control of the category.

#### Effect

The description of the category corresponding to `category_id` is set to `new_description`.

### Set Category Stickied Threads

**Parameters**

| Name          | Description                                           |
| ------------- | ----------------------------------------------------- |
| `actor`       | Either lead or working group identifier of moderator. |
| `category_id` | Category identifier.                                  |
| `threads`     | List of thread identifiers.                           |

#### Conditions

* Signer uses role account of `actor`.
* `category_id` corresponds to an existing category.
* If signer is moderator, then this moderator must be assigned have control of the category.
* All thread identifiers must have existed at some point in time.

#### Effect

The stickied threads of the category corresponding to `category_id` is set to `threads`.

### Update Category Membership of Moderator

**Parameters**

| Name          | Description                            |
| ------------- | -------------------------------------- |
| `category_id` | Category identifier.                   |
| `moderator`   | Working group identifier of moderator. |
| `is_member`   | Whether moderator should be member.    |

#### Conditions

* Signer is working group lead.
* `category_id` corresponds to an existing category.

*Note: There is no check that the provided moderator identifier corresponds to a genuine working group moderator.*

#### Effect

If `is_member` is true, then the `moderator` identifier is added to the category moderators for category corresponding to `category_id` so long as the `MAX_MODERATORS_IN_CATEGORY` limit is respected. If `is_member`is not true, then the `moderator` is removed if present.

### Delete Category

**Parameters**

| Name          | Description                                           |
| ------------- | ----------------------------------------------------- |
| `actor`       | Either lead or working group identifier of moderator. |
| `category_id` | Category identifier.                                  |

#### Conditions

* Signer uses role account of `actor`.
* `category_id` corresponds to an existing category.
* There are no threads in the category.
* There are no subcategories in the category.
* If the parent of the category is the root, then `actor` must be the lead, otherwise for other categories `actor` must be lead, or moderator must be assigned have control of the category.

#### Effect

The category is dropped.

### Create Thread

**Parameters**

| Name          | Description                           |
| ------------- | ------------------------------------- |
| `member_id`   | Identifier of member.                 |
| `category_id` | Category identifier.                  |
| `title`       | Thread title.                         |
| `text`        | Body text of thread.                  |
| `poll`        | Optional poll information for thread. |

#### Conditions

* Signer uses role account of member corresponding to `member_id`.
* Signer has enough balance to cover deposit for thread creation and post creation(`ThreadDeposit` + `PostDeposit`)
* `category_id` corresponds to an existing category.
* Limit `MAX_THREADS_IN_CATEGORY` is respected.
* The category is not archived.
* If poll information is provided, make sure
  * limit `MAX_POLL_ALTERNATIVES` is respected, and that there are at least two alternatives.
  * the end time is in the future.
* Signer's account has at least `THREAD_DEPOSIT + POST_DEPOSIT` free balance.

#### Effect

* A thread is created.
* The first post is created.
* Post + Thread deposit is substracted from signer account, `ThreadDeposit` is added to thread's `cleanup_payoff` and `PostDeposit` is added to post's `cleanup_payoff`.

### Edit Thread Title

**Parameters**

| Name          | Description          |
| ------------- | -------------------- |
| `member_id`   | Member identifier.   |
| `category_id` | Category identifier. |
| `thread_id`   | Thread identifier.   |
| `new_title`   | Thread title.        |

#### Conditions

* Signer uses role account of member corresponding to `member_id`.
* `category_id` corresponds to an existing category.
* `thread_id` corresponds to an existing thread.
* `member_id` corresponds to the author of the thread.
* The thread is not deleted from storage.
* The category is not archived. \[WIP]
* The thread is not archived. \[WIP]

#### Effect

The title of the thread is set to `new_title`.

### Move Thread

**Parameters**

| Name              | Description                                           |
| ----------------- | ----------------------------------------------------- |
| `actor`           | Either lead or working group identifier of moderator. |
| `category_id`     | Category identifier.                                  |
| `thread_id`       | Thread identifier.                                    |
| `new_category_id` | Category identifier.                                  |

#### Conditions

* Signer uses role account of `actor`.
* `category_id` corresponds to an existing category.
* `new_category_id` corresponds to an existing category.
* `category_id` and `new_category_id` are distinct.
* `thread_id` corresponds to an existing thread that's not deleted from storage.
* If signer is moderator, then this moderator must be assigned have control of the category corresponding to `category_id`.
* If signer is moderator, then this moderator must be assigned have control of the category corresponding to `new_category_id`.
* Limit `MAX_THREADS_IN_CATEGORY` is respected for category corresponding to `new_category_id`. \[WIP]

#### Effect

The thread has relocated to category corresponding to `new_category_id`.

### Delete Thread

**Parameters**

| Name              | Description                                                                 |
| ----------------- | --------------------------------------------------------------------------- |
| `forum_member_id` | Forum member identifier.                                                    |
| `category_id`     | Category identifier.                                                        |
| `thread_id`       | Thread identifier.                                                          |
| `rationale`       | Human-readable text.                                                        |
| `hidden`          | Indicates whether the thread should be hidden or just removed from storage. |

#### Conditions

* Signer corresponds to `forum_member_id`.
* `category_id` corresponds to an existing category.
* `thread_id` corresponds to an existing thread that's not deleted from storage.
* `forum_member_id` corresponds to thread creator.

#### Effect

* The thread is removed from storage.
* `cleanup_payoff` is paid to the thread deleter account.

### Moderate Thread

**Parameters**

| Name          | Description                                                              |
| ------------- | ------------------------------------------------------------------------ |
| `actor`       | Either member identifier, lead or working group identifier of moderator. |
| `category_id` | Category identifier.                                                     |
| `thread_id`   | Thread identifier.                                                       |
| `rationale`   | Human-readable text.                                                     |

#### Conditions

* Signer corresponds to `forum_member_id`.
* `category_id` corresponds to an existing category.
* `thread_id` corresponds to an existing thread that's not deleted from storage.
* If signer is moderator then this moderator must be assigned have control of the category corresponding to `category_id`.

#### Effect

* The thread is removed from storage.
* Thread's `cleanup_payoff` is slashed from thread account.

### Create Post

**Parameters**

| Name          | Description                          |
| ------------- | ------------------------------------ |
| `member_id`   | Member identifier.                   |
| `category_id` | Category identifier.                 |
| `thread_id`   | Thread identifier.                   |
| `text`        | Human-readable text.                 |
| `editable`    | Whether or not the post is editable. |

#### Conditions

* Signer uses role account of member corresponding to `member_id`.
* `category_id` corresponds to an existing category.
* `thread_id` corresponds to an existing thread that's not deleted from storage.
* Signer has at least `PostDeposit` usable balance.
* category is not archived.
* thread is not archived.

#### Effect

* A new post is created in thread with text `text`and author is `member_id` and `cleanup_payoff` is `PostDeposit`.
* `PostDeposit` is transferred from signer's account to thread's account.

### Edit Post

**Parameters**

| Name          | Description          |
| ------------- | -------------------- |
| `member_id`   | Member identifier.   |
| `category_id` | Category identifier. |
| `thread_id`   | Thread identifier.   |
| `post_id`     | Post identifier.     |
| `new_text`    | Human-readable text. |

#### Conditions

* Signer uses role account of member corresponding to `member_id`.
* `category_id` corresponds to an existing category.
* `thread_id` corresponds to an existing thread that's not deleted.
* `post_id` corresponds to an existing post that is in storage.
* member is author of post.
* category is not archived.
* thread is not archived.

#### Effect

Post text is set to `new_text`.

### React to Post

**Parameters**

| Name             | Description          |
| ---------------- | -------------------- |
| `member_id`      | Member identifier.   |
| `category_id`    | Category identifier. |
| `thread_id`      | Thread identifier.   |
| `post_id`        | Post identifier.     |
| `reaction_value` | Reaction value.      |

#### Conditions

* Signer uses role account of member corresponding to `member_id`.
* `category_id` corresponds to an existing category.
* `thread_id` corresponds to an existing thread that is not deleted.
* member is author of post.
* category is not archived.
* thread is not archived.

#### Effect

Reaction with value `reaction_value`is accepted.

### Delete Post

**Parameters**

| Name            | Description                            |
| --------------- | -------------------------------------- |
| `forum_user_id` | Member identifier.                     |
| `category_id`   | Category identifier.                   |
| `thread_id`     | Thread identifier.                     |
| `post_id`       | Post identifier.                       |
| `hidden`        | Whether post should be also be hidden. |

#### Conditions

* Signer uses role account of `actor`.
* `category_id` corresponds to an existing category.
* `thread_id` corresponds to an existing thread.
* `post_id` corresponds to an existing post.
* post is not first post in thread.
* `forum_user_id` is member of forum and is either
  * Post author
  * Or Post's thread has been deleted and `PostLifeTime` has happened since post's `last_edited`
* If `forum_user_id` is not author `hidden` must be false.

#### Effect

* Post is deleted from storage.
* Post original deposit is transferred into the Signer's account.

### Moderate Post

**Parameters**

| Name             | Description                                    |
| ---------------- | ---------------------------------------------- |
| `actor`          | lead or working group identifier of moderator. |
| `category_id`    | Category identifier.                           |
| `thread_id`      | Thread identifier.                             |
| `post_id`        | Post identifier.                               |
| `reaction_value` | Reaction value.                                |

#### Conditions

* Signer uses role account of `actor`.
* `category_id` corresponds to an existing category.
* `post_id` corresponds to an existing post.
* post is not first post in thread.
* If moderator, then this moderator must be assigned have control of the category corresponding to `category_id`.

#### Effect

* Post is deleted from storage.
* Post original deposit is slashed from thread account.

### Vote On Poll

**Parameters**

| Name                | Description                  |
| ------------------- | ---------------------------- |
| `member_id`         | Member identifier.           |
| `category_id`       | Category identifier.         |
| `thread_id`         | Thread identifier.           |
| `alternative_index` | Index of a poll alternative. |

#### Conditions

* Signer uses role account of member corresponding to `member_id`.
* `category_id` corresponds to an existing category.
* `thread_id` corresponds to an existing thread.
* thread has poll which has not expired.
* member has not already voted in poll.
* `alternative-index` identifies an alternative in the poll.

#### Effect

The member is registered as having voted for alternative `aleternative_index`

## Examples

**WIP**


# Bounties

Funding public goods where the community can help with financing, experts can help adjudicate quality of deliverables and service providers have an incentive to find popular initiatives.

## Introduction

The only other way to fund the production of goods that create benefits to a broad set of platform participants is through a financing proposal or discretionary spending by a working group lead out of the group budget. These processes incur the transaction costs of beneficiaries having to convince a number of external decision-makers, such as a council financing quorum, that this is a good idea. For smaller initiatives that ideally should start and finish sooner, or where they depend on knowledge or insight that is not as broadly shared, these processes become too costly.

## Assurance Contracts and Dominant Assurance Contracts

An assurance contract is a funding scheme intended to override the inherent free-riding problem in financing public goods by allowing contributors to enter into binding conditional commitments to provide resources to fund the good if a sufficient level of funding is committed. By setting the level sufficiently high, every public good beneficiary becomes close to pivotal to getting the good produced, which generates a rationale incentive to unilaterally commit. Dominant assurance contracts are such schemes where the funding has to be deployed towards a specific service provider or entrepreneur who will produce the good using the funding, and in exchange for this privileged to possibly generate a profit from this activity, the entrepreneur has to put up an initial bounty, called a *cherry*, which is split among all contributors pro-rata. This cherry generates an incentive for contributors, as even when the funding fails, they get a benefit.

## Roles

* **Creator:** The agent responsible for creating the bounty is either a specific member, or the council.
* **Oracle:** The agent responsible for deciding the outcome of a bounty where entrants have submitted work. Is either a specific member, or the council as a whole.
* **Contributor:** An agent responsible for contributing funds that finance the bounty. It is either a specific member or the council as a whole.
* **Worker:** A member who has announced their participation in producing deliverable in a given bounty.

Notice that when the council is an actor, it means that if the lifetime of a bounty spans the boundary of two councils, then a different set of council members are likely in place to exercise control over the same bounty.

## Concepts

### Bounty Actor

The act of creating or contributing funds to a bounty can be done by either a member or the council as a whole, the concept of a *bounty actor* refers, therefore, to either a specific member or the council, and represents this actor in a unified way.

### Funding Period Type

The funding period type refers to how funds are collected for the benefit of a bounty, and there are fundamentally two types:

* **Perpetual:** The funding has not preset termination date, and new contributors can join on an ongoing basis. There is however a *target* which sets the upper bound for how much can be contributed.
* **Limited:** The funding lasts for no longer than a given number of blocks, called the *funding period*. There is a lower bound and upper bound for how much must be contributed

### Bounty Type

There are two types of bounties in terms of who can participate as a worker. There are *open* bounties, where any member can participate, and *closed* bounties, where the creator can pre-select a set of members who can participate. The primary purpose of closed bounties is to enable dominant assurance contracts, where the creator combines setting themselves as the only feasible worker with also providing a cherry.

### Work Entry

For someone to be able to participate as a worker, with the opportunity to capture some portion of the funds accumulated for the bounty, they have to announce their participation in the bounty in the form of a *work entry*. It describes the status of the involvement of a worker in a bounty, and it is defined by the following information:

* **EntryId:** Unique non-negative integer identifier across all entries.
* **Worker:** Member Id of a worker.
* **Staking Account:** Account holding funds used to stake for participation in bounty.
* **Work:** List of work submissions made during the `Working Period`, encoded as structure data in a standardized format.
* **Status:** The status of an entry has the following disjoint variants.
  * **Working:** Initial status during creation in `Working Period`.
  * **Withdrawn:** When withdrawn during the `Working Period`.
  * **Winner:** Selected as winner during `Judgement Period` , and therefore is due an outstanding share of the bounty funds, called *reward* .
  * **Passed:** Not referenced by oracle in `Judgement Period` judgement, and therefore is not owed any share of the bounty funds, but has outstanding stake that can be recovered.
  * **Rejected:** Rejected by oracle in `Judgement Period` as a malicious entry, and thus has had stake slashed.
  * **CashedOut:** Worker cashed out stake and/or share of bounty reward.

### Bounty

A bounty is defined by the following information

* **BountyId:** Unique non-negative integer identifier across all bounties.
* **Oracle:** Bounty oracle, is either a member or the council.
* **Type:** Bounty type, is open or closed.
* **Creator:** Bounty creator, is a bounty actor.
* **Cherry:** Amount of funds contributed by creator as cherry.
* **Oracle Cherry:** Amount of funds contributed by creator as oracle cherry.
* **Work Period Length:** The number of blocks which must pass, from the end of the funding stage, before the oracle for the bounty can adjudicate the outcome of the bounty.
* **Judging Period Length:** The maximum number of blocks which can pass, from the end of the working period, while the oracle does not adjudicate a the outcome and funds cannot be withdrawn.
* **Contributions:** The set of all contributions made, each identified with a unique bounty actor, and having an associated positive balance contribution.
* **Entries:** The set of all active entries in the bounty.
* **Metadata:** Structured data encoding the purpose and terms of the bounty.
* **Stage:** The stage of the bounty see next subsection

#### Stage

Below is a list of the stages a bounty can be in, and what each of them mean:

* **Funding Period:** This is the initial stage of a bounty once it is created, during this stage the bounty can accept funding contributions. It is also during this stage the bounty can be vetoed by the council. The creator can also cancel the bounty in this stage if there are no contributions. If a contribution is made that brings the cumulative funding equal to or above the upper bound, then the difference is returned, and the bounty proceeds to the `Working Period` stage. Lastly, if the funding period is limited and the time passes this time, then the bounty proceeds to the `Bounty Failed` stage if there was at least one contribution made, otherwise it proceeds to the `Expried Funding Period` stage.
* **Expired Funding Period:** During this stage, the bounty is only waiting to get cancelled by the creator, terminating the bounty.
* **Working Period:** This is the stage where workers announce their entries and submit their work, and optionally also withdraw. After the working period length has expired since the initiation of the working period, the stage transitions to the `Judgement Period`.
* **Judgement Period:** This is the stage during which the oracle can evaluate the submitted work entries during the working periods. The judgement identifies a set of winning contributors, possibly empty, each with a non-zero number of tokens as a reward. The total reward must perfectly consume the contributed funding to the bounty, but reward distribution need not be uniform. Such a successful outcome also automatically returns the cherry to the creator if it exists. If the oracle selects an empty set of winners, or does not provide a judgement within the end of the judgement period, a transition is made to the `Bounty Failed` stage, otherwise a transition to `Bounty Successful` stage is made if the set is non-empty. Cherry is not returned to creator in this case.
* **Withdrawal Period:** This represents the stage where funds can be withdrawn from the bounty, eventually leading to the bounty getting terminated.
  * **Bounty Successful:** This represents the case where the some subset of workers have been selected as winners, and each must cash out to claim their reward. Workers who did not win also have to cash out their possible prior stake. When the last cashout is made, the bounty is terminated.
  * **Bounty Failed:** This represents the case where no winners were selected, and all contributed funds need to be returned to fund contributors, including a portion of a possible cherry. Workers who did not win also have to cash out their possible prior stake.

The stages and transitions are summarized in the image below.

![Bounty life-cycle stages.](/files/RVjEM5jROOLqmOkRqCXA)

## Constants

Hard-coded values are defined *for each working group*, and they can only be altered with a runtime upgrade.

| Name                      | Description                                                                                            |
| ------------------------- | ------------------------------------------------------------------------------------------------------ |
| `ClosedContractSizeLimit` | Max work entry number for a closed assurance type contract bounty.                                     |
| `MinCherryLimit`          | Min cherry for a bounty.                                                                               |
| `MinOracleCherryLimit`    | Min oracle cherry for a bounty.                                                                        |
| `MinFundingLimit`         | Min funding amount for a bounty.                                                                       |
| `MinWorkEntrantStake`     | Min work entrant stake for a bounty.                                                                   |
| `ModuleAccountId`         | The Account id of a built-in account, used to hold funds and cherries contributed across all bounties. |
| `LOCK_ID`                 | The Id for the lock used to stake in the bounty system.                                                |

## Extrinsics

### Create Bounty

**Parameters**

| Name                    | Description                                                         |
| ----------------------- | ------------------------------------------------------------------- |
| `origin`                | Caller origin.                                                      |
| `oracle`                | Bounty actor that is oracle.                                        |
| `bounty_type`           | Bounty type.                                                        |
| `creator`               | Bounty actor that is creator.                                       |
| `cherry`                | Amount of funds dedicated as cherry.                                |
| `oracle_cherry`         | Amount of funds dedicated as oracle cherry.                         |
| `entrant_stake`         | Amount of stake required for prospective workers to create entry.   |
| `funding_period_type`   | The number of blocks in the funding period.                         |
| `working_period_length` | The number of blocks in the working period.                         |
| `judging_period_length` | The number of blocks in the judging period.                         |
| `metadata`              | Structured metadata describing the bounty in a human readable form. |

#### Conditions

* `origin` corresponds to `bounty_actor`.
* If `creator` is
  * a member, then the controller account of the member must have sufficient balance for the `cherry` and the `oracle cherry`.
  * the council, then the council budget must accommodate the `cherry` and the `oracle cherry`.
* `cherry` is no less than `MinCherryLimit`.
* `oracle cherry` is no less than `MinOracleCherryLimit`.
* `entrant_stake` is no less than `MinWorkEntrantStake`.
* If `bounty_type` is closed, the number of members is no greater than `ClosedContractSizeLimit`, and not zero.
* If `funding_period_type` is
  * perpetual, then the target is greater than zero.
  * limited, then the minimum funding amount, max amount and funding period length all must be non-zero, and the max funding amount must be greater than the minimum funding amount.

#### Effect

* A new bounty is created in the `Funding Period`.
* Deduct `cherry` and `oracle cherry` from either the council budget or controller account of the creator, and credited to account `ModuleAccountId`.

### Cancel Bounty

**Parameters**

| Name        | Description               |
| ----------- | ------------------------- |
| `origin`    | Caller origin.            |
| `creator`   | Bounty actor for creator. |
| `bounty_id` | Bounty identifier.        |

#### Conditions

* `origin` corresponds to `creator`.
* `bounty_id` corresponds to an existing `bounty`.
* `creator` created bounty identified by `bounty_id`.
* `bounty` is either in stage `Funding Period` without any contributions, or is in stage `Expried Funding Period`.

#### Effect

* `cherry` is credited to `creator` and deducted from `ModuleAccountId`.
* `oracle cherry` is credited to `creator` and deducted from `ModuleAccountId`.
* `bounty` is terminated.

### Fund Bounty

**Parameters**

| Name        | Description                                                   |
| ----------- | ------------------------------------------------------------- |
| `origin`    | Caller origin.                                                |
| `funder`    | Bounty actor for the funder.                                  |
| `bounty_id` | Identifier for the bounty to be funded.                       |
| `amount`    | Balance to be contributed towards the bounty from the funder. |

#### Conditions

* `origin` corresponds to the `funder`.
* `bounty_id` corresponds to an existing `bounty`.
* `bounty` is in stage `Funding Period`.
* `funder` can cover `amount`.
* `amount` is no less than `MinFundingLimit`.

#### Effect

* `amount` would bring the total amount funded so far, denoted by `current_funding`, equal to or over the target or max value, denoted by `limit`, and transition to the `Working Period` . Let `_amount` denote the quantity of funds that can be contributed to the bounty without overflowing the limit, that is `min(limit - current_funding, amount)` .
* `_amount` is debited from `funder` and credited towards account \*\*\*\* `ModuleAccountId`.
* If `funder` has already contributed to the bounty, then add `_amount` to their net contribution, otherwise note their total contribution to be `_amount`.

### Withdraw Funding

**Parameters**

| Name        | Description                             |
| ----------- | --------------------------------------- |
| `origin`    | Caller origin.                          |
| `funder`    | Bounty actor for the funder.            |
| `bounty_id` | Identifier for the bounty to be funded. |

#### Conditions

* `origin` corresponds to the `funder`.
* `bounty_id` corresponds to an existing `bounty`.
* `bounty` is in stage `Bounty Failed`.
* `funder` has made a contribution to the `bounty` that has not been withdrawn.

#### Effect

* Remove contribution of balance `amount` made by `funder` to `bounty`.
* Credit `funder` with `amount` and debit `ModuleAccountId` the corresponding amount.
* If there is no outstanding contributions or entries to the bounty, then terminate the bounty.

### Announce Work Entry

**Parameters**

| Name                 | Description                                                             |
| -------------------- | ----------------------------------------------------------------------- |
| `origin`             | Caller origin.                                                          |
| `member_id`          | Member identifier of prospective worker.                                |
| `bounty_id`          | Identifier for the bounty in which member wants to join.                |
| `staking_account_id` | Account balance.                                                        |
| `metadata`           | Structured metadata describing the work entry in a human readable form. |

#### Conditions

* `origin` corresponds to identifier `member_id` for a member.
* `bounty_id` corresponds to an existing `bounty`.
* `bounty` is in stage `Working Period`.
* `member_id` does not have an active work entry on `bounty`.
* If `bounty` is a closed bounty, then `memeber_id` is among the permitted participants.
* `staking_account_id` is a staking account associated with the member, and has a free balance no less than `MinWorkEntrantStake` and no conflicting staking locks present.

#### Effect

* A work entry is added for the member to the bounty, in the `Working` state.
* Account with id `staking_account_id` has lock with Id `LOCK_ID` and of size `MinWorkEntrantStake` set.

### Withdraw Work Entry

**Parameters**

| Name        | Description                                            |
| ----------- | ------------------------------------------------------ |
| `origin`    | Caller origin.                                         |
| `member_id` | Member identifier for worker.                          |
| `bounty_id` | Identifier for bounty to which work entry corresponds. |
| `entry_id`  | Identifier for work entry.                             |

#### Conditions

* `origin` corresponds to identifier `member_id` for a member.
* `bounty_id` corresponds to an existing `bounty`.
* `bounty` is in stage `Working Period`.
* `entry_id` corresponds to an entry `entry` with status `Working` and where the worker has identifier

#### Effect

* The `entry` status is set to `Withdrawn`.
* The staking account of the entry has lock with Id `LOCK_ID` removed, and the account is slashed a share of the staked balance equal to the share of the working period length for which the entry had the status `Working`.

### Submit Work

**Parameters**

| Name        | Description                                            |
| ----------- | ------------------------------------------------------ |
| `origin`    | Caller origin.                                         |
| `member_id` | Member identifier for a worker.                        |
| `bounty_id` | Identifier for bounty to which work entry corresponds. |
| `entry_id`  | Identifier for work entry.                             |
| `work_data` | Encoded work metadata.                                 |

#### Conditions

* `origin` corresponds to identifier `member_id` for a member.
* `bounty_id` corresponds to an existing `bounty`.
* `bounty` is in stage `Working Period`.
* `entry_id` corresponds to an entry `entry` with status `Working` and where the worker has identifier

#### Effect

`None`.

### Submit Oracle Judgement

**Parameters**

| Name        | Description                                                                                  |
| ----------- | -------------------------------------------------------------------------------------------- |
| `origin`    | Caller origin.                                                                               |
| `oracle`    | Bounty actor that is oracle.                                                                 |
| `bounty_id` | Identifier for bounty for which judgement is being submitted.                                |
| `judgement` | Set of entries to be identified as winners, and set of entries to be identified as rejected. |

#### Conditions

* `origin` corresponds to identifier `member_id` for a member.
* `bounty_id` corresponds to an existing `bounty`.
* `bounty` is in stage `Working Period`.
* `judgement` references two disjoint sets of entries that all have status `Working`
  * a, possibly empty, set of entries designated as winners, each having submitted at least one work submission, and each with an associated non-zero balance designated as the reward, and where all rewards add up to the total funding of the bounty.
  * a set of entries designated as rejected, to be slashed.

#### Effect

* for each winner entry referenced by `judgement`, credit worker account and debited from `ModuleAccountId`, remove lock with Id `LOCK_ID`, and set status to `Winner`.
* for each rejected entry referenced by `judgement`, apply slashing, remove lock with Id `LOCK_ID`, and set status to `Rejected`.
* transition bounty to stage `Bounty Failed` if there are no winners, otherwise transition to `Bounty Successful`.
* credit oracle account with oracle cherry, and debited from `ModuleAccountId`.

### Cashout Work Entrant Funds

**Parameters**

| Name        | Description                                            |
| ----------- | ------------------------------------------------------ |
| `origin`    | Caller origin.                                         |
| `member_id` | Member identifier for a worker.                        |
| `bounty_id` | Identifier for bounty to which work entry corresponds. |
| `entry_id`  | Identifier for work entry.                             |

#### Conditions

* `origin` corresponds to identifier `member_id` for a member.
* `bounty_id` corresponds to an existing bounty `bounty`.
* `entry_id` corresponds to an entry `entry` where worker has identifier `member_id` and the status is not `CashedOut`or `Rejected` or `Withdrawn` .
* `bounty` is in stage`Withdrawal Period`.

#### Effect

* if `entity` has status `Winner`, then the associated reward is credited to `member_id` controller account and debited from `ModuleAccountId`.
* `entity` staking account has lock with Id `LOCK_ID` removed.
* `entity` status is updated to `CashedOut`.


# V2

Bounty V2 placeholder article

## State Machine

![Bounty state machine](/files/zaw1AdYceuLOhiLKQmCj)


# Builders

At the end of the day, someone actually has to ship something.

## Introduction

The platform runtime, tools, infrastructure software and user facing applications are all meant to evolve over time. A diverse set of contributors are required to facilitate this, including

* **Developers:** Software developers, Data scientists, DevOps and QA
* **Designers:** Web, Mobile, UX/UI, Branding and Visual Design
* **Product Managers:** Digital Product Managers, Product Owners and Analysts

All of these contributors are collectively referred to as Builders. Anyone can contribute in the same mode as any of these possible contributor functions, as all the platform source assets are open source and developed in the open. Being a Builder means that one has some scope of responsibility in ongoing efforts, and that one has some predefined reward scheme associated with this responsibility.

## Notion Space&#x20;

The community maintains a distinct Notion space which holds more dynamic information on the activities of the builders working group.

{% embed url="<https://joystream.notion.site/Builders-ffb1c9d1d1094fc4a6f04eb47677673d>" %}

## Builder

### Responsibilities

* Collaborate with almost every other kind of platform member to maintain and improve the platform

### Requirements

* A deep understanding of the Joystream platform structure and function
* Have specific skills required for contributing in the given way
* Hold sufficient amount of the native platform token to put at stake

## Builder Lead

`wip`


# Human Resources

It starts with people.

## Introduction

The human resources subsystem is an [Working Groups](/system/working-groups#operations-working-groups), meaning it has no special on-chain features being what basic working group features exist, described in [Working Groups](/system/working-groups).&#x20;

## Notion Space&#x20;

The community maintains a distinct Notion space which holds more dynamic information on the activities of the HR working group.

{% embed url="<https://joystream.notion.site/Human-Resources-ab782adffbdd4be497654f9e48309e2a>" %}


# Marketers

Telling the story and sharing the vision.

## Introduction

The marketing working group is responsible for promoting the DAO's objectives, projects, and initiatives to the wider community. Leveraging digital channels and content strategies, they craft compelling narratives and create engaging campaigns to attract new members, retain existing ones, and enhance the DAO's brand presence. Additionally, they monitor the effectiveness of their efforts, adjusting their tactics to maximize outreach and foster growth in a decentralized ecosystem.

The marketer subsystem is an [Working Groups](/system/working-groups#operations-working-groups), meaning it has no special on-chain features being what basic working group features exist, described in [Working Groups](/system/working-groups).&#x20;

## Notion Space&#x20;

The community maintains a distinct Notion space which holds more dynamic information on the activities of the marketer working group.

{% embed url="<https://joystream.notion.site/Marketer-e9c39c5089694ddd9a64087c831216f3>" %}


# Applications

A working group trailblazing path for app builders.

## Introduction

The applications working group manage and operate video streaming applications and platforms for the organization. These team members work together to create, curate, and distribute multimedia content that aligns with the DAO's goals and values. They also ensure seamless delivery of  video-on-demand services, while fostering partnerships and engaging with community members. This department continuously explores new technologies and opportunities to enhance the DAO's streaming capabilities and user experience.

The applications subsystem is an [Working Groups](/system/working-groups#operations-working-groups), meaning it has no special on-chain features being what basic working group features exist, described in [Working Groups](/system/working-groups).&#x20;

## Notion Space&#x20;

The community maintains a distinct Notion space which holds more dynamic information on the activities of the applications working group.

{% embed url="<https://joystream.notion.site/Apps-05bec9dc2a5949aeb063ad510ad3eb92>" %}


# Security

Security related resources and information.

## Vulnerability Reporting

To report issues in a responsible manner, please send to "<mark style="color:red;">**security" on domain "joystream.org"**</mark>, and encrypt the message, this is however ***not*** a support channel.

Your report should include the following:

* your name
* description of the vulnerability
* attack scenario (if any)
* components
* reproduction
* other details

Try to include as much information in your report as you can, including a description of the vulnerability, its potential impact, and steps for reproducing it. Be sure to use a descriptive subject line.

You'll receive a response to your email within 5 business days indicating the next steps in handling your report. We encourage finders to use encrypted communication channels to protect the confidentiality of vulnerability reports. You can encrypt your report using our public key.&#x20;

After the initial reply to your report, our team will endeavor to keep you informed of the progress being made towards a fix. These updates will be sent at least every five business days.

Thank you for taking the time to responsibly disclose any vulnerabilities you find.

### Responsible Investigation and Reporting

Responsible investigation and reporting includes, but isn't limited to, the following:

* Don't violate the privacy of other users, destroy data, etc.
* Don’t defraud or harm Parity Technologies Ltd or its users during your research; you should make a good faith effort to not interrupt or degrade our services.
* Don't target our physical security measures, or attempt to use social engineering, spam, distributed denial of service (DDOS) attacks, etc.
* Initially report the bug only to us and not to anyone else.
* Give us a reasonable amount of time to fix the bug before disclosing it to anyone else, and give us adequate written warning before disclosing it to anyone else.
* In general, please investigate and report bugs in a way that makes a reasonable, good faith effort not to be disruptive or harmful to us or our users. Otherwise your actions might be interpreted as an attack rather than an effort to be helpful.

## Secure Communication

The following GPG keys may be used to communicate sensitive information to relevant developers, in particular concerning the runtime and blockchain, but also other critical infrastructure and applications. These recipients will relay the message securely to whomever is the maintainer at any given time.

<table><thead><tr><th width="184">Joystream Hande</th><th>Fingerprint</th></tr></thead><tbody><tr><td><code>mokhtar</code></td><td><code>2DD5D822FC22DB196E262440ADA7C97DDD6781D8</code></td></tr></tbody></table>

You can import a key by running the following command with that individual’s fingerprint: `gpg --keyserver hkps://keys.openpgp.org --recv-keys "<fingerprint>"` Ensure that you put quotes around fingerprints containing spaces.

## Security Audits

Two runtime audits have been conducted so far

* **Quarkslab:** <https://github.com/Joystream/audits/tree/main/Quarkslab-22-05-982-REP>
* **SRLabs:** <https://github.com/Joystream/audits/tree/main/SRL-Jsgenesis_baseline_security_assurance_joystream-2021>


# Launch Process

Outlining the launch phases for the Carthage testnet and mainnet.

## Introduction

Launching a new blockchain secured by a new PoS validator set, which comes to consensus using a time sensitive BFT protocol, is a vulnerable process which easily can end up with liveness failure as a result of networking or operational malfunctioning. For this reason, it should be happen in a gradual process, starting very centralised  and small, and opening up to a broader set of stakeholders while block production is monitored. It also has to be a fair process which does not bias in favor of early or arbitrary participants. Lastly, there must be some room for small scale coordination to handle any unforunforeseenseen catastrophic errors which may occur early, and with minimal if any side-effects. The process for launching the Joystream blockchain was designed based on these requirements.

## Carthage and Mainnet

This process will be used for the upcoming launch of the `Carthage`  network, where runtime is feature frozen - and genesis block is largely locked in, and only unforseen circumstances will result in further changes to the process described herein.

## Overview

The launch of the final joystream testnet - `Carthage`, intended as the final test of the runtime and our intended deployment plan for the Joystream mainnet, will be rolled out as outlined in the table below:

<table><thead><tr><th width="123">Stage</th><th width="130">Duration</th><th width="136">Sudo</th><th width="130">Consensus</th><th width="152">Calls Allowed</th><th width="160">Main Jsgenesis Actions</th><th width="116">Staking Rewarded</th><th width="127">#Validators</th></tr></thead><tbody><tr><td><strong>Frozen</strong></td><td>~24h</td><td>Yes <br>(Single Key)</td><td>PoA</td><td><code>sudo,</code><br><code>staking,</code><br><code>session,</code><br><code>multisig*</code></td><td>Bootstrap <br>-> Thawn</td><td>No</td><td>9-12</td></tr><tr><td><strong>Thawn</strong></td><td>~6days</td><td>Yes<br>(Multisig)</td><td>PoS</td><td><code>sudo,</code><br><code>staking,</code><br><code>session,</code><br><code>multisig*</code></td><td>Set #Validators<br>-> Supervised</td><td>Yes</td><td>12-24</td></tr><tr><td><strong>Supervised</strong></td><td>Weeks</td><td>Yes<br>(Multisig)</td><td>PoS</td><td>Everything<code>**</code></td><td>Set #Validators<br>-> Liberated</td><td>Yes</td><td>24-?</td></tr><tr><td><strong>Liberated</strong></td><td>NA</td><td>No</td><td>PoS</td><td>Everything<code>**</code></td><td>NA</td><td>Yes</td><td>council decides</td></tr></tbody></table>

* `*` All transaction related to the `sudo`, `staking`, `session` and `multisig` modules will not be filtered, regardless of origin. This means:
  * Prospective validators and nominators can prepare and configure their validator (on chain), and/or nominate. There will be no changes to the actual set however during the `Frozen` stage.
  * Any `multisig` transactions can be initiated and approved, but the actual call will be filtered unless the call is:
    * `sudo.*`
    * `staking.*`
    * `session.*`
  * Although any `sudo.*` will get through the filter, unless the caller of the extrinsic is actually `sudo`, the transaction will, of course, still fail.
* `**` Some filters will still apply, but they are only applied to certain features that are in the runtime, but not properly integrated. The main example being the `bounty` module.

## Frozen Phase

### Overview

* **Duration:** `~24hours`
* **Consensus:**`PoA`
* **Purpose:** bootstrapping of `memberships`
* **Actors:**
  * Jsgenesis (as `sudo`)
  * Prospective validators and nominators
* **Filter:** everything except
  * `staking`
  * `session`
  * `sudo`
  * `multisig`

### Purpose

The main reason the chain is deployed with a `PoA` consensus algorithm is to allow a safer deployment, without having to restrict access to the chain spec.

### Community Actions

As soon as the first block is created, anyone (with tokens to cover the fees) can make any transaction they want. However, as stated previously, only transaction to the `staking` or `session` module will be executed. Note that any other action will still incur fees.

#### Validators

Go [here](/system/validation) for a step by step guide to become a validator or nominator.

### Jsgenesis Actions

The actions and transactions to be made by Jsgenesis can be found, in sequence, below.

#### Bootstrapping

As soon as the chain has been deployment, Jsgenesis will run a script that creates all members on the platform as they were at the time of the migration snapshot, meaning:

* `rootAccount`
* `controllerAccount`
* `memberHandle`
* `metadata`
  * The extra data held by the query node
* `isFoundingMember`

As all members are created in sequence, so that members will keep their `memberId`.

#### Set new `sudo` key

As the script that handles the bootstrapping requires `sudo` to bypass the filter, the next step is to change the `sudo` key to a multisig key. The signer keys are stored offline, reducing the risk of loss, hacking and social engineering.

#### Set the `validatorCount`

By default, the chain sets the "ideal" number of validators, namely the `staking.validatorCount` equal to the amount of authorities - which is 9. The (new) `sudo` key will change this to 12.

#### Move to `Thawn`

Once \~24 hours have passed, giving everyone equal opportunity to deploy their nodes and configure their validator keys on chain, `sudo` will make the `staking.forceNewEra()` call, effectively changing the consensus system from PoA to PoS.

## Thawn Phase

### Overview

* **Duration:** `~6days`
* **Consensus:** `PoS`
* **Purpose:** Ensure a safe transition from `PoA` to `PoS`
* **Actors:**
  * Jsgenesis (as `sudo`)
  * Validators and nominators
* **Filter:** everything except
  * `staking`
  * `session`
  * `sudo`
  * `multisig`

### Purpose

To ensure a safe transition from `PoA` to `PoS`, Jsgenesis will gradually increase the size validator set (ie. the maximum amount of validators in each `era`).

The benefits of doing relatively small increases of the size:

* Higher competition for slots -> higher stakes required -> actors will be more careful and diligent
* Lower chances of a single bad faith actor occupying a critical amount of slots
* Easier to reach, assist and recover in case of issues

### Community Actions

With the filter still intact, the main action the community can do is to get involved with validation and nomination.

#### Validators

During this stage, validators and nominators can come and go as they like. They will earn rewards, but are also at the risk of being slashed.

### Jsgenesis Actions

The actions and transactions to be made by Jsgenesis can be found, in sequence, below.

#### Set the `validatorCount`

As we see more and more competition for slots, and that the current validator set are performing well, Jsgenesis will increase the amount of slots allowing new actors to join.

More specifically, some of the metrics we will look at are:

* Are all validators online, and producing blocks when called upon
* Are all validators contributing to finalization
* Are validators well connected to each other, maintaining acceptable latency and the blocktime stays at \~6s.
* Is there a sufficiently "deep" bench to ensure the stake requirements are "acceptable", and that there will still be a queue afterwards.

How many times this will be done depends on the above.

#### Move to `Supervised`

Once \~6 days have passed, and we are now assuming that the validators are performing well, Jsgenesis will do a runtime upgrade to the `Supervised` stage, that effectively removes the transaction filter.

## Supervised Phase

### Overview

* **Duration:** `unknown`
* **Consensus:** `PoS`
* **Purpose:**
  * Remove the transaction filter, thus opening for "normal" operations and governance.
  * Maintain `sudo` power for potential emergencies
* **Actors:**
  * Jsgenesis (as `sudo`)
  * Everyone
* **Filter:** nothing except
  * `bounty`
  * a few calls in proposals and content directory

### Purpose

Although "no" transactions will be blocked by the filter, the first 8 days of the `Supervised` stage will still be rather limited.

New members can register and anyone can transfer tokens, but nothing interesting can happen before the council is set and Leads are hired. Together, they are required to "open" up:

* proposals
* the forum
* content creation, storage and distribution
* hiring of workers
* etc.

Once election, the council can do whatever they want, but Jsgenesis will retain `sudo` power for the time being.

### Community Actions

Pretty much everything!

The "required" actions are listed below:

* Elect a council
* Establish working groups
  * Set budgets
  * Create openings
  * Hire Leads
    * Storage/content Leads enables content creation
    * Distributor Lead enables content distribution
    * Forum Lead enables the forum
    * Hire Workers

After a couple days, we should be in a place where everyone can participate by creating content, build, host infrastructure and earn a little $JOY before mainnet.

### Jsgenesis Actions

The actions and transactions to be made by Jsgenesis can be found, in sequence, below.

#### Set the `validatorCount`

Although the council will, and shall, take over this role fairly soon, it will take at least 3 councils due to the constitutionality.

In the meantime, Jsgenesis have the option to increase or decrease the number based on the metrics listed for `Thawn`.

#### Move to `Liberated`

Although the exact timeline is not clear, on the `Carthage` network Jsgenesis will make their final transaction with `sudo` before the second council is elected. The only change will be to remove `sudo`, thus giving full control to the council.

Hopefully, nothing else will be required.

## Liberated Phase

### Overview

* **Duration:**`forever`
* **Consensus:** `PoS`
* **Purpose:** `Video platform DAO`
* **Actors:** Everyone
* **Filter:** Same as prior


# Sudo Transactions Made

Up until the Liberated phase Jsgenesis needed, and had, `sudo` access in order to bootstrap the chain, transition from Frozen to Thawn and Supervised, increase the Validator set, and if required, perform emergency transaction.&#x20;

The following keys were used for `sudo` purposes

<table data-header-hidden><thead><tr><th width="175"></th><th width="94"></th><th width="530"></th><th width="132"></th><th width="278"></th></tr></thead><tbody><tr><td><strong>Keys ID</strong></td><td><strong>Blocks</strong></td><td><strong>Address</strong></td><td><strong>Sudo TX'es</strong></td><td><strong>Purpose</strong></td></tr><tr><td><strong>Initial Sudo</strong></td><td>0-362</td><td><strong><code>j4S1Q6PbzmLmoaNHeDbF9padntUEGdd95YA7pYGsfjdvxttHi</code></strong></td><td>92</td><td>Bootstrapping memberships, grant FM status</td></tr><tr><td><strong>MS Sudo</strong></td><td>362-</td><td><strong><code>j4RtiWzLf89RmRz8piFNQz79viPtuie19ZBk4iBaN3eSQzUr7</code></strong></td><td>8</td><td>Transition to Thawn + Supervised, set validator count</td></tr><tr><td><strong>Offline Signers</strong><br><strong>(2 of 3)</strong></td><td>362-</td><td><em><code>j4UNAyL891FncnC8trgX4fYGgYK2dJpApjcKg98EuHNXTMG9p,</code></em><br><em><code>j4WfewWMybQ9Snkhkygn5XPYYU8hqizMbGeCKtzUGKNUS63NW,</code></em><br><em><code>j4UuxHh8RTc5UXH7SoCk6PjG8ZXyM2g9TvMBxeSDCuQJGpuoD</code></em></td><td>0,<br>10,<br>9</td><td>Offline keys for initiating and approving the <code>sudo</code> calls</td></tr></tbody></table>

The list below contains a list of all transactions `sudo` has done. It will be updated until the Liberated phase begins, where `sudo` is disabled.

<table data-header-hidden><thead><tr><th width="93"></th><th width="119"></th><th width="116"></th><th width="125"></th><th width="220"></th><th width="229"></th><th width="265"></th><th width="332"></th><th width="380"></th></tr></thead><tbody><tr><td><strong>TX ID</strong></td><td><strong>Key ID</strong></td><td><strong>Init. Block</strong><br><strong>[#]</strong></td><td><strong>Exec. Block</strong><br><strong>[#]</strong></td><td><strong>Exec Date</strong><br><strong>[UTC]</strong></td><td><strong>Sudo Call</strong><br><code>sudo.&#x3C;method></code></td><td><strong>Call</strong><br><code>&#x3C;section></code><br><code>&#x3C;method></code>  <strong>(Args)</strong></td><td><strong>Event/State change (Count)</strong></td><td><strong>Purpose</strong></td></tr><tr><td><strong>0</strong></td><td>Initial Sudo</td><td>-</td><td>49</td><td>09 Dec 2022 20:12:30</td><td><code>sudo</code></td><td><code>members</code><br><code>MemberCreated</code></td><td><code>members.MemberCreated</code> (50)</td><td>Migrate memberships and set FMs</td></tr><tr><td><strong>2−89</strong></td><td>Initial Sudo</td><td>-</td><td>52-316</td><td>09 Dec 2022 20:mm:ss</td><td><code>sudo</code></td><td><code>members</code><br><code>MemberCreated</code></td><td>89x: <code>members.MemberCreated</code> (50)</td><td>Migrate memberships and set FMs</td></tr><tr><td><strong>90</strong></td><td>Initial Sudo</td><td>-</td><td>319</td><td>09 Dec 2022 20:39:30</td><td><code>sudo</code></td><td><code>members</code><br><code>MemberCreated</code></td><td><code>members.MemberCreated</code> (10)</td><td>Migrate memberships and set FMs</td></tr><tr><td><strong>91</strong></td><td>Initial Sudo</td><td>-</td><td>362</td><td>09 Dec 2022 20:43:48</td><td><code>setKey</code></td><td><code>j4S1..ttHi</code></td><td><code>sudo.keyChanged</code> -> MS Sudo</td><td>Improve Sudo key security</td></tr><tr><td><strong>92</strong></td><td>MS Sudo</td><td>43013</td><td>43087</td><td>12 Dec 2022 19:56:24</td><td><code>sudoAs</code></td><td><code>vesting</code><br><code>vestOther</code></td><td><code>vesting.VestingUpdated</code> (60)</td><td>Needed for the Community Validators</td></tr><tr><td><strong>93</strong></td><td>MS Sudo</td><td>43317</td><td>52456</td><td>13 Dec 2022 11:33:18</td><td><code>sudo</code></td><td><code>staking</code><br><code>setValidatorCount</code> (12)</td><td><code>staking.validatorCount</code> to 12</td><td>Safely expand the validator set</td></tr><tr><td><strong>94</strong></td><td>MS Sudo</td><td>43327</td><td>52743</td><td>13 Dec 2022 12:02:00</td><td><code>sudo</code></td><td><code>staking</code><br><code>forceNewEra</code></td><td><code>staking.forceEra</code> from <code>ForceNone</code> to <code>ForceNew</code></td><td>Frozen -> Thawn phase</td></tr><tr><td><strong>95</strong></td><td>MS Sudo</td><td>56130</td><td>69211</td><td>14 Dec 2022 15:28:48</td><td><code>sudo</code></td><td><code>staking</code><br><code>setValidatorCount</code> (16)</td><td><code>staking.validatorCount</code> to 16</td><td>Safely expand the validator set</td></tr><tr><td><strong>96</strong></td><td>MS Sudo</td><td>56179</td><td>140146</td><td>19 Dec 2022 14:30:00</td><td><code>sudoUncheckedWeight</code></td><td><code>system</code><br><code>setCode</code></td><td><code>system.CodeUpdated</code></td><td>Thawn -> Supervised phase</td></tr><tr><td><strong>97</strong></td><td>MS Sudo</td><td>56146</td><td>144397</td><td>19 Dec 2022 21:35:18</td><td><code>sudo</code></td><td><code>staking</code><br><code>setValidatorCount</code> (22)</td><td>Set <code>staking.validatorCount</code> to 22</td><td>Safely expand the validator set</td></tr><tr><td><strong>98</strong></td><td>MS Sudo</td><td>56157</td><td>339401</td><td>02 Jan 2023 10:38:00</td><td><code>sudo</code></td><td><code>staking</code><br><code>setValidatorCount</code> (30)</td><td>Set <code>staking.validatorCount</code> to 30</td><td>Safely expand the validator set</td></tr><tr><td><strong>99</strong></td><td>MS Sudo</td><td>1194333</td><td>1245998</td><td>06 Mar 2023 12:00:24</td><td><code>sudo</code></td><td><code>staking</code><br><code>setInvulnerables</code></td><td><code>staking.invulnerables</code> empty</td><td>Prepare for Liberated phase</td></tr></tbody></table>


