Offchain Phragmén BREAKING. (#4517)

* Initial skeleton for offchain phragmen

* Basic compact encoding decoding for results

* add compact files

* Bring back Self::ensure_storage_upgraded();

* Make staking use compact stuff.

* First seemingly working version of reduce, full of todos

* Everything phragmen related works again.

* Signing made easier, still issues.

* Signing from offchain compile fine 😎

* make compact work with staked asssignment

* Evaluation basics are in place.

* Move reduce into crate. Document stuff

* move reduce into no_std

* Add files

* Remove other std deps. Runtime compiles

* Seemingly it is al stable; cycle implemented but not integrated.

* Add fuzzing code.

* Cleanup reduce a bit more.

* a metric ton of tests for staking; wip 🔨

* Implement a lot more of the tests.

* wip getting the unsigned stuff to work

* A bit gleanup for unsigned debug

* Clean and finalize compact code.

* Document reduce.

* Still problems with signing

* We officaly duct taped the transaction submission stuff. 🤓

* Deadlock with keys again

* Runtime builds

* Unsigned test works 🙌

* Some cleanups

* Make all the tests compile and stuff

* Minor cleanup

* fix more merge stuff

* Most tests work again.

* a very nasty bug in reduce

* Fix all integrations

* Fix more todos

* Revamp everything and everything

* Remove bogus test

* Some review grumbles.

* Some fixes

* Fix doc test

* loop for submission

* Fix cli, keyring etc.

* some cleanup

* Fix staking tests again

* fix per-things; bring patches from benchmarking

* better score prediction

* Add fuzzer, more patches.

* Some fixes

* More docs

* Remove unused generics

* Remove max-nominator footgun

* Better fuzzer

* Disable it 

* Bump.

* Another round of self-review

* Refactor a lot

* More major fixes in perThing

* Add new fuzz file

* Update lock

* fix fuzzing code.

* Fix nominator retain test

* Add slashing check

* Update frame/staking/src/tests.rs

Co-Authored-By: Joshy Orndorff <JoshOrndorff@users.noreply.github.com>

* Some formatting nits

* Review comments.

* Fix cargo file

* Almost all tests work again

* Update frame/staking/src/tests.rs

Co-Authored-By: thiolliere <gui.thiolliere@gmail.com>

* Fix review comments

* More review stuff

* Some nits

* Fix new staking / session / babe relation

* Update primitives/phragmen/src/lib.rs

Co-Authored-By: thiolliere <gui.thiolliere@gmail.com>

* Update primitives/phragmen/src/lib.rs

Co-Authored-By: thiolliere <gui.thiolliere@gmail.com>

* Update primitives/phragmen/compact/src/lib.rs

Co-Authored-By: thiolliere <gui.thiolliere@gmail.com>

* Some doc updates to slashing

* Fix derive

* Remove imports

* Remove unimplemented tests

* nits

* Remove dbg

* Better fuzzing params

* Remove unused pref map

* Deferred Slashing/Offence for offchain Phragmen  (#5151)

* Some boilerplate

* Add test

* One more test

* Review comments

* Fix build

* review comments

* fix more

* fix build

* Some cleanups and self-reviews

* More minor self reviews

* Final nits

* Some merge fixes.

* opt comment

* Fix build

* Fix build again.

* Update frame/staking/fuzz/fuzz_targets/submit_solution.rs

Co-Authored-By: Gavin Wood <gavin@parity.io>

* Update frame/staking/src/slashing.rs

Co-Authored-By: Gavin Wood <gavin@parity.io>

* Update frame/staking/src/offchain_election.rs

Co-Authored-By: Gavin Wood <gavin@parity.io>

* Fix review comments

* fix test

* === 🔑 Revamp without staking key.

* final round of changes.

* Fix cargo-deny

* Update frame/staking/src/lib.rs

Co-Authored-By: Gavin Wood <gavin@parity.io>

Co-authored-by: Joshy Orndorff <JoshOrndorff@users.noreply.github.com>
Co-authored-by: thiolliere <gui.thiolliere@gmail.com>
Co-authored-by: Gavin Wood <gavin@parity.io>
This commit is contained in:
Kian Paimani
2020-03-26 15:37:40 +01:00
committed by GitHub
parent 2a67e6c437
commit 970c5f94f2
64 changed files with 11953 additions and 892 deletions
+4
View File
@@ -0,0 +1,4 @@
target
corpus
artifacts
File diff suppressed because it is too large Load Diff
+35
View File
@@ -0,0 +1,35 @@
[package]
name = "pallet-staking-fuzz"
version = "0.0.0"
authors = ["Automatically generated"]
publish = false
edition = "2018"
[package.metadata]
cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.3"
codec = { package = "parity-scale-codec", version = "1.2.0", default-features = false, features = ["derive"] }
pallet-staking = { version = "2.0.0-alpha.2", path = "..", features = ["testing-utils"] }
pallet-staking-reward-curve = { version = "2.0.0-alpha.2", path = "../reward-curve" }
pallet-session = { version = "2.0.0-alpha.2", path = "../../session" }
pallet-indices = { version = "2.0.0-alpha.2", path = "../../indices" }
pallet-balances = { version = "2.0.0-alpha.2", path = "../../balances" }
pallet-timestamp = { version = "2.0.0-alpha.2", path = "../../timestamp" }
frame-system = { version = "2.0.0-alpha.2", path = "../../system" }
frame-support = { version = "2.0.0-alpha.2", path = "../../support" }
sp-std = { version = "2.0.0-alpha.2", path = "../../../primitives/std" }
sp-io ={ version = "2.0.0-alpha.2", path = "../../../primitives/io" }
sp-core = { version = "2.0.0-alpha.2", path = "../../../primitives/core" }
sp-phragmen = { version = "2.0.0-alpha.2", path = "../../../primitives/phragmen" }
sp-runtime = { version = "2.0.0-alpha.2", path = "../../../primitives/runtime" }
rand = "0.7.3"
# Prevent this from interfering with workspaces
[workspace]
members = ["."]
[[bin]]
name = "submit_solution"
path = "fuzz_targets/submit_solution.rs"
@@ -0,0 +1,182 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Mock file for staking fuzzing.
use sp_runtime::traits::{Convert, SaturatedConversion};
use frame_support::{impl_outer_origin, impl_outer_dispatch, parameter_types};
type AccountId = u64;
type AccountIndex = u32;
type BlockNumber = u64;
type Balance = u64;
type System = frame_system::Module<Test>;
type Balances = pallet_balances::Module<Test>;
type Staking = pallet_staking::Module<Test>;
type Indices = pallet_indices::Module<Test>;
type Session = pallet_session::Module<Test>;
impl_outer_origin! {
pub enum Origin for Test where system = frame_system {}
}
impl_outer_dispatch! {
pub enum Call for Test where origin: Origin {
staking::Staking,
}
}
pub struct CurrencyToVoteHandler;
impl Convert<u64, u64> for CurrencyToVoteHandler {
fn convert(x: u64) -> u64 {
x
}
}
impl Convert<u128, u64> for CurrencyToVoteHandler {
fn convert(x: u128) -> u64 {
x.saturated_into()
}
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct Test;
impl frame_system::Trait for Test {
type Origin = Origin;
type Index = AccountIndex;
type BlockNumber = BlockNumber;
type Call = Call;
type Hash = sp_core::H256;
type Hashing = ::sp_runtime::traits::BlakeTwo256;
type AccountId = AccountId;
type Lookup = Indices;
type Header = sp_runtime::testing::Header;
type Event = ();
type BlockHashCount = ();
type MaximumBlockWeight = ();
type AvailableBlockRatio = ();
type MaximumBlockLength = ();
type Version = ();
type ModuleToIndex = ();
type AccountData = pallet_balances::AccountData<u64>;
type OnNewAccount = ();
type OnKilledAccount = (Balances,);
}
parameter_types! {
pub const ExistentialDeposit: Balance = 10;
}
impl pallet_balances::Trait for Test {
type Balance = Balance;
type Event = ();
type DustRemoval = ();
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
}
impl pallet_indices::Trait for Test {
type AccountIndex = AccountIndex;
type Event = ();
type Currency = Balances;
type Deposit = ();
}
parameter_types! {
pub const MinimumPeriod: u64 = 5;
}
impl pallet_timestamp::Trait for Test {
type Moment = u64;
type OnTimestampSet = ();
type MinimumPeriod = MinimumPeriod;
}
impl pallet_session::historical::Trait for Test {
type FullIdentification = pallet_staking::Exposure<AccountId, Balance>;
type FullIdentificationOf = pallet_staking::ExposureOf<Test>;
}
sp_runtime::impl_opaque_keys! {
pub struct SessionKeys {
pub foo: sp_runtime::testing::UintAuthorityId,
}
}
pub struct TestSessionHandler;
impl pallet_session::SessionHandler<AccountId> for TestSessionHandler {
const KEY_TYPE_IDS: &'static [sp_runtime::KeyTypeId] = &[];
fn on_genesis_session<Ks: sp_runtime::traits::OpaqueKeys>(_validators: &[(AccountId, Ks)]) {}
fn on_new_session<Ks: sp_runtime::traits::OpaqueKeys>(
_: bool,
_: &[(AccountId, Ks)],
_: &[(AccountId, Ks)],
) {}
fn on_disabled(_: usize) {}
}
impl pallet_session::Trait for Test {
type SessionManager = pallet_session::historical::NoteHistoricalRoot<Test, Staking>;
type Keys = SessionKeys;
type ShouldEndSession = pallet_session::PeriodicSessions<(), ()>;
type NextSessionRotation = pallet_session::PeriodicSessions<(), ()>;
type SessionHandler = TestSessionHandler;
type Event = ();
type ValidatorId = AccountId;
type ValidatorIdOf = pallet_staking::StashOf<Test>;
type DisabledValidatorsThreshold = ();
}
pallet_staking_reward_curve::build! {
const I_NPOS: sp_runtime::curve::PiecewiseLinear<'static> = curve!(
min_inflation: 0_025_000,
max_inflation: 0_100_000,
ideal_stake: 0_500_000,
falloff: 0_050_000,
max_piece_count: 40,
test_precision: 0_005_000,
);
}
parameter_types! {
pub const RewardCurve: &'static sp_runtime::curve::PiecewiseLinear<'static> = &I_NPOS;
pub const MaxNominatorRewardedPerValidator: u32 = 64;
}
pub type Extrinsic = sp_runtime::testing::TestXt<Call, ()>;
type SubmitTransaction = frame_system::offchain::TransactionSubmitter<
sp_runtime::testing::UintAuthorityId,
Test,
Extrinsic,
>;
impl pallet_staking::Trait for Test {
type Currency = Balances;
type Time = pallet_timestamp::Module<Self>;
type CurrencyToVote = CurrencyToVoteHandler;
type RewardRemainder = ();
type Event = ();
type Slash = ();
type Reward = ();
type SessionsPerEra = ();
type SlashDeferDuration = ();
type SlashCancelOrigin = frame_system::EnsureRoot<Self::AccountId>;
type BondingDuration = ();
type SessionInterface = Self;
type RewardCurve = RewardCurve;
type NextNewSession = Session;
type ElectionLookahead = ();
type Call = Call;
type SubmitTransaction = SubmitTransaction;
type KeyType = sp_runtime::testing::UintAuthorityId;
type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator;
}
@@ -0,0 +1,130 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Fuzzing for staking pallet.
#![no_main]
use libfuzzer_sys::fuzz_target;
use mock::Test;
use pallet_staking::testing_utils::{
self, USER, get_seq_phragmen_solution, get_weak_solution, setup_chain_stakers,
set_validator_count, signed_account,
};
use frame_support::assert_ok;
use sp_runtime::{traits::Dispatchable, DispatchError};
mod mock;
#[repr(u32)]
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
enum Mode {
/// Initial submission. This will be rather cheap.
InitialSubmission,
/// A better submission that will replace the previous ones. This is the most expensive.
StrongerSubmission,
/// A weak submission that will be rejected. This will be rather cheap.
WeakerSubmission,
}
pub fn new_test_ext() -> Result<sp_io::TestExternalities, std::string::String> {
frame_system::GenesisConfig::default().build_storage::<mock::Test>().map(Into::into)
}
fuzz_target!(|do_reduce: bool| {
let ext = new_test_ext();
let mode: Mode = unsafe { std::mem::transmute(testing_utils::random(0, 2)) };
let num_validators = testing_utils::random(50, 500);
let num_nominators = testing_utils::random(200, 2000);
let edge_per_voter = testing_utils::random(1, 16);
let to_elect = testing_utils::random(10, num_validators);
println!("+++ instance with params {} / {} / {} / {:?} / {}",
num_nominators,
num_validators,
edge_per_voter,
mode,
to_elect,
);
ext.unwrap_or_default().execute_with(|| {
// initial setup
set_validator_count::<Test>(to_elect);
setup_chain_stakers::<Test>(
num_validators,
num_nominators,
edge_per_voter,
);
println!("++ Chain setup done.");
// stuff to submit
let (winners, compact, score) = match mode {
Mode::InitialSubmission => {
/* No need to setup anything */
get_seq_phragmen_solution::<Test>(do_reduce)
},
Mode::StrongerSubmission => {
let (winners, compact, score) = get_weak_solution::<Test>(false);
assert_ok!(
<pallet_staking::Module<Test>>::submit_election_solution(
signed_account::<Test>(USER),
winners,
compact,
score,
)
);
get_seq_phragmen_solution::<Test>(do_reduce)
},
Mode::WeakerSubmission => {
let (winners, compact, score) = get_seq_phragmen_solution::<Test>(do_reduce);
assert_ok!(
<pallet_staking::Module<Test>>::submit_election_solution(
signed_account::<Test>(USER),
winners,
compact,
score,
)
);
get_weak_solution::<Test>(false)
}
};
println!("++ Submission ready.");
// must have chosen correct number of winners.
assert_eq!(winners.len() as u32, <pallet_staking::Module<Test>>::validator_count());
// final call and origin
let call = pallet_staking::Call::<Test>::submit_election_solution(
winners,
compact,
score,
);
let caller = signed_account::<Test>(USER);
// actually submit
match mode {
Mode::WeakerSubmission => {
assert_eq!(
call.dispatch(caller.into()).unwrap_err(),
DispatchError::Module { index: 0, error: 11, message: Some("PhragmenWeakSubmission") },
);
},
_ => assert_ok!(call.dispatch(caller.into())),
};
})
});