-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: interface and data structures for Collator Power Pallet #53
Changes from 3 commits
c032385
9fec6c3
0138910
567c77f
8859d5f
e823ccb
416bb0b
4c77261
4eb3dae
650a2e9
4d20ff5
debcfae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
[package] | ||
authors.workspace = true | ||
description = "manages collators' power used in the selection process of a collator node" | ||
edition.workspace = true | ||
homepage.workspace = true | ||
license-file.workspace = true | ||
name = "pallet-collator-power" | ||
publish = false | ||
repository.workspace = true | ||
version = "0.0.0" | ||
|
||
[lints] | ||
workspace = true | ||
|
||
[package.metadata.docs.rs] | ||
targets = ["x86_64-unknown-linux-gnu"] | ||
|
||
[dependencies] | ||
codec = { workspace = true, default-features = false, features = ["derive"] } | ||
scale-info = { workspace = true, default-features = false, features = ["derive"] } | ||
|
||
# frame deps | ||
frame-benchmarking = { workspace = true, default-features = false, optional = true } | ||
frame-support = { workspace = true, default-features = false } | ||
frame-system = { workspace = true, default-features = false } | ||
|
||
[dev-dependencies] | ||
sp-core = { workspace = true, default-features = false } | ||
sp-io = { workspace = true } | ||
sp-runtime = { workspace = true, default-features = false } | ||
|
||
[features] | ||
default = ["std"] | ||
runtime-benchmarks = [ | ||
"frame-benchmarking/runtime-benchmarks", | ||
"frame-support/runtime-benchmarks", | ||
"frame-system/runtime-benchmarks", | ||
"sp-runtime/runtime-benchmarks", | ||
] | ||
std = [ | ||
"codec/std", | ||
"frame-benchmarking?/std", | ||
"frame-support/std", | ||
"frame-system/std", | ||
"scale-info/std", | ||
"sp-core/std", | ||
"sp-io/std", | ||
"sp-runtime/std", | ||
] | ||
try-runtime = ["frame-support/try-runtime", "frame-system/try-runtime", "sp-runtime/try-runtime"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# Overall Power Pallet Flow | ||
|
||
## Glossary | ||
- **extrinsic** - state transition function on a pallet, essentially a signed transaction which requires an account with some tokens to be executed as it costs fees. | ||
- **Miner** - [Storage Provider][5] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In our solution, where storage providing and block production is done by separate roles/entities using |
||
- **collateral** - amount of tokens staked by a miner (via `SPP`) to be able to provide storage services | ||
- **PoSt** - [Proof of Storage][3] | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- `SPP` - Storage Provider Pallet | ||
- `CPP` - Collator Power Pallet | ||
- `CSP` - Collator Selection Pallet | ||
- **session** - a [session][4] is a period of time that has a constant set of validators. | ||
|
||
## Overview | ||
|
||
**Collators** are entities selected to produce **blocks** which are then finalized by relay chain's **validators**. | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
To participate in **block production**, a **Collator** needs to: | ||
- stake a ***certain*** (yet to be determined) amount of tokens | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- be backed by **Miners'** **Storage Power**. | ||
Collators' staking is a requirement for participation in the process, the actual selection is based on **Storage Power**. | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The collators are selected based on **Storage Power** by `CSP` in an randonmized auction algorithm. | ||
The more **Storage Power** has been staked on a **Collator** by the **Miner**, the more likely their chances to be selected for block production. | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
This pallet works as a proxy between `SPP` and `CSP` to make collator choices. | ||
It stores how much **Storage Power** a **Miner** has and how much was delegated by **Miners** to **Collators**. | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Both `SPP` and `CSP` are [tightly coupled][2] to this pallet. | ||
|
||
Trade Offs [?]: | ||
|
||
**Collators** are separately tracked by `CSP` and this pallet gives back the `staked_power` to a **Miner** when a **Collator** disappears. | ||
This is an intentional design decision, this could be also tracked in this pallet, however I do think it'd make this Pallet too complex. | ||
As Collators also need to be staked and require their own registration logic. | ||
|
||
## Data Structures | ||
|
||
```rust | ||
struct MinerClaim { | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// Indicates how much power a Miner has | ||
raw_bytes_power: T::StoragePower; | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
staked_power: Map<T::CollatorId, T::StoragePower> | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
``` | ||
|
||
## Use Cases | ||
|
||
### Registration | ||
|
||
#### Useful links | ||
- [Creating Storage Miner in Lotus][1] | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### Assumptions | ||
- `create_miner(miner: T::MinerId)` is an **extrinsic**. It's called by `Storage Provider Node` on a bootstrap. We can trust it, as `T::MinerId` is essentialy an account ID. The transaction needs to be signed, for it to be signed it needs to come from an account. For it to come from an account, the account has to have an **existential deposit** and it costs money. That's how it's DOS-resistant. | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### Flow: | ||
1. **Miner** calls `create_miner(miner: T::MinerId)` | ||
2. `CPP` initializes `Map<MinerId, MinerClaim>`. | ||
|
||
### Manipulating Power | ||
|
||
#### Assumptions | ||
- `SPP` only calls `CPP.update_miner_power` function after: | ||
* Miner has been registered in `Collator Power` via `create_miner` function call | ||
* Miner has submitted `PreCommit` sector with a certain (calculated by `SPP`) amount **Collateral** required | ||
* Miner has proven sectors with **PoSt** via `ProveCommit` of `SPP`. | ||
- `update_miner_power` is ***NOT** an **extrinsic**. It can only be called from `SPP` via [tight coupling][2]. | ||
- The reason is that we can't trust that a **Miner** will call extrinsic and update it on their own. `SPP` logic will perform those updates, e.g: after (not)receiving **PoSt**, receiving **pledge collaterals**. | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#### Flow | ||
1. `SPP` calls `CPP.update_miner_power(miner: T::MinerId, deltaStorageBytes: T::StoragePower)` | ||
2. If Storage Power was decresed: `CPP` decreases all delegated power (to **Collators**) | ||
- essentially means 'Slashing Miner and the Power they delegated' | ||
3. If Storage Power was increased: `CPP` does nothing. | ||
4. `CPP` performs bookeeping, updating `MinerClaim` | ||
|
||
### Delegating Power | ||
|
||
#### Assumptions | ||
- It's an **extrinsic**, can be called by a **Miner**. | ||
|
||
#### Flow | ||
1. **Miner** calls `CPP.delegate_power(miner: T::MinerId, collator: T::CollatorId, amount: T::StoragePower)` | ||
2. `CPP` saves delegated **Storage Power** in **Miner's** claims (`staked_power`). | ||
3. In the next **session**, the saved Power is picked up by `CSP`, by calling `CPP.get_collator_power(collator: T::CollatorId) -> T::StoragePower`. | ||
|
||
#### Slashing Collator (?) | ||
|
||
TODO: I don't have this piece of the puzzle yet. I mean... What happens if a **Miner** staked some power on a **Collator** and it misbehaved? Do we **slash** the **Miner's** staked tokens/or storage power, and if so, how? | ||
|
||
[1]: https://github.com/filecoin-project/lotus/blob/9851d35a3811e5339560fb706926bf63a846edae/cmd/lotus-miner/init.go#L638 | ||
[2]: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_pallet_coupling/index.html#tight-coupling-pallets | ||
[3]: https://spec.filecoin.io/#section-algorithms.pos | ||
[4]: https://paritytech.github.io/polkadot-sdk/master/pallet_session/index. | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
[5]: https://github.com/eigerco/polka-disk/blob/main/doc/research/lotus/lotus-overview.md#Roles |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Collator Power Pallet | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
//! # Collator Power Pallet | ||
//! | ||
//! # Overview | ||
//! | ||
//! The Collator Power Pallet provides functions for: | ||
//! - ... | ||
|
||
#![cfg_attr(not(feature = "std"), no_std)] | ||
|
||
pub use pallet::*; | ||
|
||
#[frame_support::pallet(dev_mode)] | ||
pub mod pallet { | ||
use codec::{Decode, Encode}; | ||
use frame_support::{ | ||
dispatch::DispatchResultWithPostInfo, | ||
pallet_prelude::*, | ||
sp_runtime::RuntimeDebug, | ||
}; | ||
use frame_system::{pallet_prelude::*}; | ||
use scale_info::TypeInfo; | ||
|
||
#[pallet::config] | ||
pub trait Config: frame_system::Config { | ||
/// Because this pallet emits events, it depends on the runtime's definition of an event. | ||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; | ||
|
||
/// Unit of Storage Power of a Miner | ||
/// E.g. `u128`, used as `number of bytes` for a given SP. | ||
type StoragePower: Parameter + Member + Clone + MaxEncodedLen; | ||
|
||
/// A stable ID for a Collator | ||
type CollatorId: Parameter + Member + Ord + MaxEncodedLen; | ||
|
||
/// A stable ID for a Miner | ||
type MinerId: Parameter + Member + Ord + MaxEncodedLen; | ||
} | ||
|
||
#[derive( | ||
Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, Default, TypeInfo, MaxEncodedLen, | ||
)] | ||
pub struct MinerClaim<CollatorId: Ord, StoragePower> { | ||
/// Number of bytes stored by a Miner | ||
raw_bytes_power: StoragePower, | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// Stores how much currency was staked on a particular collator | ||
staked_power: BoundedBTreeMap<CollatorId, StoragePower, ConstU32<10>> | ||
} | ||
|
||
#[pallet::pallet] | ||
pub struct Pallet<T>(_); | ||
th7nder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#[pallet::storage] | ||
#[pallet::getter(fn storage_provider_claims)] | ||
pub type MinerClaims<T: Config> = | ||
StorageMap<_, _, T::MinerId, MinerClaim<T::CollatorId, T::StoragePower>>; | ||
|
||
#[pallet::event] | ||
// #[pallet::generate_deposit(pub(super) fn deposit_event)] | ||
pub enum Event<T: Config> { | ||
/// Indicates that a new Miner has been registered. | ||
/// Newly created Miner does not have any Power. | ||
/// Power is updated after the Miner proves it has storage available. | ||
MinerRegistered(T::AccountId), | ||
} | ||
|
||
#[pallet::error] | ||
pub enum Error<T> { | ||
/// If there is an entry in claims map, connected to the AccountId that tries to be registered as a Miner. | ||
MinerAlreadyRegistered, | ||
} | ||
|
||
/// Extrinsics exposed by the pallet | ||
#[pallet::call] | ||
impl<T: Config> Pallet<T> { | ||
/// After Miner proved a sector, calls this method to update the bookkeeping about available power. | ||
pub fn update_storage_power( | ||
_storage_provider: OriginFor<T>, | ||
_raw_delta_bytes: T::StoragePower, | ||
) -> DispatchResultWithPostInfo { | ||
todo!() | ||
} | ||
} | ||
|
||
/// Functions exposed by the pallet | ||
/// e.g. `pallet-collator-selection` used them to make decision about the next block producer | ||
impl<T: Config> Pallet<T> { | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we are using mono repo for our work, the glossary could be made in a separate, higher-level document to keep terminology consistent across the project.