Skip to content

Commit

Permalink
Initial mining asset (paritytech#131)
Browse files Browse the repository at this point in the history
* Ass mining/asset module

* Impl weight related traits in mining/asset

* Quick impl mining/asset traits

* Add mining-asset into runtime

* Add mining/asset tests

* Add more mining/asset tests

* Split out mining primitive

* Rename to mining-common primitive

* Make the Weight namings more generic

* Add some docs in common

* Make Amount in Weight trait use generics

* Make Weight trait totally generic

* Extract generic weight factors

* Fix tests

* Add test in ci

* .
  • Loading branch information
liuchengxu authored Jul 5, 2020
1 parent ef034a2 commit 18aa916
Show file tree
Hide file tree
Showing 22 changed files with 1,356 additions and 210 deletions.
11 changes: 8 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,14 @@ jobs:
- name: Compile
run: cargo test --no-run

# - name: Test
# FIXME
# run: cargo test --all
- name: Test
# FIXME: test all
run: |
cd xpallets/mining/asset
cargo test -- --nocapture
cd -
cd xpallets/mining/staking
cargo test -- --nocapture
- if: matrix.os == 'ubuntu-latest'
run: cargo clippy
40 changes: 38 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ members = [
"cli",
"runtime",
"primitives",
"primitives/staking",
"primitives/mining/common",
"primitives/mining/staking",
"rpc",

"xpallets/assets",
Expand All @@ -24,6 +25,7 @@ members = [
"xpallets/contracts/common",
"xpallets/contracts/rpc",
"xpallets/contracts/rpc/runtime-api",
"xpallets/mining/asset",
"xpallets/mining/staking",
"xpallets/protocol",
"xpallets/support",
Expand Down
23 changes: 23 additions & 0 deletions primitives/mining/common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "xp-mining-common"
version = "0.1.0"
authors = ["ChainX community <https://www.chainx.org>"]
edition = "2018"

[dependencies]
# Substrate primitives
sp-std = { git = "https://github.com/paritytech/substrate.git", tag = "v2.0.0-rc4", default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate.git", tag = "v2.0.0-rc4", default-features = false }
sp-arithmetic = { git = "https://github.com/paritytech/substrate.git", tag = "v2.0.0-rc4", default-features = false }

# ChainX primitives
chainx-primitives = { path = "../../../primitives", default-features = false }

[features]
default = ["std"]
std = [
"sp-std/std",
"sp-runtime/std",

"chainx-primitives/std",
]
193 changes: 193 additions & 0 deletions primitives/mining/common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
#![cfg_attr(not(feature = "std"), no_std)]

//! Common concepts with regard to the ChainX Mining system, particularly the user-level ones.
//!
//! There are two approaches of mining in ChainX:
//!
//! 1. As a PoS-based blockchain, **Staking** is inherently the fundamental way of mining.
//! In this way, users(stakers) nominate some validators with some balances locked, earning
//! the staking reward.
//!
//! 2. One goal of ChainX is to embrace more the eixsting cryptocurrencies into one ecosystem,
//! therefore **Asset Mining** is introduced for winning more external assets like BTC, ETH, etc.
//! For example, Bitcoin users can deposit their BTC into ChainX, then they'll get the X_BTC
//! in 1:1 and the mining rights in ChainX system accordingly, earning the new minted PCX
//! like the stakers in Staking.
//!
//! Both of these two approaches share one same rule when calculating the individual reward, i.e.,
//! **time-sensitive weight calculation**.
//!
//! ```
//! Amount(Balance) * Duration(BlockNumber) = Weight
//! ```
//!
//! For Staking:
//!
//! ```
//! staked_balance(Balance) * time(BlockNumber) = vote_weight
//! ```
//!
//! All the nominators split the reward of the validator's jackpot according to the proportion of vote weight.
//!
//! For Asset Mining:
//!
//! ```
//! ext_asset_balance(Balance) * time(BlockNumber) = ext_mining_weight
//! ```
//!
//! All asset miners split the reward of asset's jackpot according to the proportion of asset mining weight.
//!

use sp_arithmetic::traits::{BaseArithmetic, SaturatedConversion};
use sp_std::result::Result;

/// Type for calculating the mining weight.
pub type WeightType = u128;

/// The getter and setter methods for the further mining weight processing.
pub trait BaseMiningWeight<Balance, BlockNumber> {
fn amount(&self) -> Balance;
/// Set the new amount.
///
/// Amount management of asset miners is handled by assets module,
/// hence the default implementation is provided here.
fn set_amount(&mut self, _new: Balance) {}

fn last_acum_weight(&self) -> WeightType;
fn set_last_acum_weight(&mut self, s: WeightType);

fn last_acum_weight_update(&self) -> BlockNumber;
fn set_last_acum_weight_update(&mut self, num: BlockNumber);
}

/// Amount changes of miner's state.
///
/// `Zero` happens:
/// 1. stakers performs the `rebond` operation.
/// 2. claim the reward.
#[derive(Clone, Copy, sp_runtime::RuntimeDebug)]
pub enum Delta<Balance> {
Add(Balance),
Sub(Balance),
Zero,
}

/// General logic for state changes of the mining weight operations.
pub trait MiningWeight<Balance: BaseArithmetic + Copy, BlockNumber>:
BaseMiningWeight<Balance, BlockNumber>
{
/// Set the new amount after settling the change of nomination.
fn settle_and_set_amount(&mut self, delta: &Delta<Balance>) {
let new = match *delta {
Delta::Add(x) => self.amount() + x,
Delta::Sub(x) => self.amount() - x,
Delta::Zero => return,
};
self.set_amount(new);
}

/// This action doesn't involve in the change of amount.
///
/// Used for asset mining module.
fn set_state_weight(&mut self, latest_acum_weight: WeightType, current_block: BlockNumber) {
self.set_last_acum_weight(latest_acum_weight);
self.set_last_acum_weight_update(current_block);
}

/// Set new state on bond, unbond and rebond in Staking.
fn set_state(
&mut self,
latest_acum_weight: WeightType,
current_block: BlockNumber,
delta: &Delta<Balance>,
) {
self.set_state_weight(latest_acum_weight, current_block);
self.settle_and_set_amount(delta);
}
}

impl<Balance: BaseArithmetic + Copy, BlockNumber, T: BaseMiningWeight<Balance, BlockNumber>>
MiningWeight<Balance, BlockNumber> for T
{
}

/// Skips the next processing when the latest mining weight is zero.
pub struct ZeroMiningWeightError;

/// General logic for calculating the latest mining weight.
pub trait ComputeMiningWeight<AccountId, BlockNumber: Copy> {
/// The entity that holds the funds of claimers.
type Claimee;
type Error: From<ZeroMiningWeightError>;

fn claimer_weight_factors(_: &AccountId, _: &Self::Claimee, _: BlockNumber) -> WeightFactors;
fn claimee_weight_factors(_: &Self::Claimee, _: BlockNumber) -> WeightFactors;

fn settle_claimer_weight(
who: &AccountId,
target: &Self::Claimee,
current_block: BlockNumber,
) -> WeightType {
Self::_calc_latest_vote_weight(Self::claimer_weight_factors(who, target, current_block))
}

fn settle_claimee_weight(target: &Self::Claimee, current_block: BlockNumber) -> WeightType {
Self::_calc_latest_vote_weight(Self::claimee_weight_factors(target, current_block))
}

fn settle_weight_on_claim(
who: &AccountId,
target: &Self::Claimee,
current_block: BlockNumber,
) -> Result<(WeightType, WeightType), Self::Error> {
let claimer_weight = Self::settle_claimer_weight(who, target, current_block);

if claimer_weight == 0 {
return Err(ZeroMiningWeightError.into());
}

let claimee_weight = Self::settle_claimee_weight(target, current_block);

Ok((claimer_weight, claimee_weight))
}

fn _calc_latest_vote_weight(weight_factors: WeightFactors) -> WeightType {
let (last_acum_weight, amount, duration) = weight_factors;
last_acum_weight + WeightType::from(amount) * WeightType::from(duration)
}
}

/// Weight Formula:
///
/// LatestVoteWeight(WeightType) = last_acum_weight(WeightType) + amount(Balance) * duration(BlockNumber)
///
/// Using u128 for calculating the weights won't run into the overflow issue practically.
pub type WeightFactors = (WeightType, u128, u128);

/// Prepares the factors for calculating the latest mining weight.
pub fn generic_weight_factors<
Balance: BaseArithmetic,
BlockNumber: BaseArithmetic,
W: BaseMiningWeight<Balance, BlockNumber>,
>(
w: W,
current_block: BlockNumber,
) -> WeightFactors {
(
w.last_acum_weight(),
w.amount().saturated_into(),
(current_block - w.last_acum_weight_update()).saturated_into(),
)
}

/// Claims the reward for participating in the mining.
pub trait Claim<AccountId> {
/// Entity of holder of individual miners.
///
/// Validator for Staking, Asset for Asset Mining.
type Claimee;
/// Claim error type.
type Error;

fn claim(claimer: &AccountId, claimee: &Self::Claimee) -> Result<(), Self::Error>;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "xp-staking"
name = "xp-mining-staking"
version = "0.1.0"
authors = ["ChainX community <https://www.chainx.org>"]
edition = "2018"
Expand All @@ -10,7 +10,8 @@ sp-std = { git = "https://github.com/paritytech/substrate.git", tag = "v2.0.0-rc
sp-runtime = { git = "https://github.com/paritytech/substrate.git", tag = "v2.0.0-rc4", default-features = false }

# ChainX primitives
chainx-primitives = { path = "../../primitives", default-features = false }
chainx-primitives = { path = "../../../primitives", default-features = false }
xp-mining-common = { path = "../common", default-features = false }

[features]
default = ["std"]
Expand All @@ -19,4 +20,5 @@ std = [
"sp-runtime/std",

"chainx-primitives/std",
"xp-mining-common/std",
]
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
//! A crate which contains primitives that are useful for implementation that uses staking
//! approaches in general. Definitions related to sessions, slashing, etc go here.

mod vote_weight;

pub use vote_weight::*;

use chainx_primitives::AssetId;
use sp_std::prelude::Vec;

Expand All @@ -16,9 +12,6 @@ pub type SessionIndex = u32;
/// Simple index type with which we can count unbonded entries.
pub type UnbondedIndex = u32;

/// Type for calculating staker's vote weight.
pub type VoteWeight = u128;

/// Type for measuring the non-validator entity's mining power.
pub type MiningPower = u128;

Expand Down
Loading

0 comments on commit 18aa916

Please sign in to comment.