-
Notifications
You must be signed in to change notification settings - Fork 261
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
Staking support #99
Staking support #99
Changes from 93 commits
6bdf5e8
10667e3
d4a085a
c08e22a
93c347c
fef1260
3d6e2ee
c0b1904
8721237
a28fb20
cfabfe8
ff967e9
716dd13
88ac3cb
122260f
ffbffd4
1b4dc07
ebfdc17
aaf190e
5ff1b17
58da217
ab9612b
193104c
7868f2c
c4942c4
c6ade8b
36b124d
bc19991
9fd2949
e3e30cf
ba4f90c
06aa7e2
49b3b1e
3314ea1
57e2390
067c6f9
83c6c61
5bdeee4
7587469
06bc4ab
6ccfc9c
d4d7b1d
c63b74c
4d8ccab
90e3b9b
2bea59f
a95fd8a
f15c5f6
d3df9ea
ff8e37f
fe20eb5
e80ddd4
94ce5ce
ae676c9
e829a46
6bf960f
528b218
7bad6c6
da36675
d5b1138
28533f6
c330e68
59decaf
93fafcd
8bcde31
b03b28b
2744127
3efe16f
dd15f22
773d43e
d3865a6
0084f87
33f1710
9f34f76
1df5a64
c622641
b253a7a
1394bb9
9a86802
0e7bc2d
92bcd26
95c8900
b3687af
3020f32
303c5f4
2e74c1e
f0a5f61
e0b41af
0516987
dffbba6
0860bad
ec96a42
01869a7
b54b1b9
68ff645
9c6c663
bc8bc36
dd4dce9
1eb67d9
d63661f
369cb88
24848b3
e2862d8
8a79571
f3da309
18e8029
f7cda4a
15c0206
d659e91
52960b7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,9 @@ A library to **sub**mit e**xt**rinsics to a [substrate](https://github.com/parit | |
|
||
See [examples](./examples). | ||
|
||
If you use `#[derive(Call)]` without `#[module]` in the same module, you will get errors | ||
complaining about an undefined method with a name starting with `with_`. | ||
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. Please open an issue for this, or provide some context here 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. It took me a long time to figure this out, so I added this for the benefit of others. 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. Do you think this info – which I agree is useful – should move or be copied to the docs for 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. I've raised an issue for it to improve the error message: #170. But probably we should remove from the readme since it doesn't make sense without any context. |
||
|
||
**Alternatives** | ||
|
||
[substrate-api-client](https://github.com/scs/substrate-api-client) provides similar functionality. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// Copyright 2019-2020 Parity Technologies (UK) Ltd. | ||
// This file is part of substrate-subxt. | ||
// | ||
// subxt is free software: you can redistribute it and/or modify | ||
DemiMarie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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. | ||
// | ||
// subxt is distributed in the hope that it will be useful, | ||
DemiMarie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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-subxt. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
//! Session support | ||
use crate::frame::system::{ | ||
System, | ||
SystemEventsDecoder as _, | ||
}; | ||
use codec::Encode; | ||
use frame_support::Parameter; | ||
use sp_runtime::traits::{ | ||
Member, | ||
OpaqueKeys, | ||
}; | ||
use std::{ | ||
fmt::Debug, | ||
marker::PhantomData, | ||
}; | ||
use substrate_subxt_proc_macro::Store; | ||
|
||
/// Impls `Default::default` for some types that have a `_runtime` field of type | ||
/// `PhantomData` as their only field. | ||
macro_rules! default_impl { | ||
($name:ident) => { | ||
impl<T: Session> Default for $name<T> { | ||
fn default() -> Self { | ||
Self { | ||
_runtime: PhantomData, | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
|
||
/// The trait needed for this module. | ||
#[module] | ||
pub trait Session: System { | ||
/// The validator account identifier type for the runtime. | ||
type ValidatorId: Parameter + Debug + Ord + Default + Send + Sync + 'static; | ||
|
||
/// The keys. | ||
type Keys: OpaqueKeys + Member + Parameter + Default; | ||
} | ||
|
||
/// The current set of validators. | ||
#[derive(Encode, Store, Debug)] | ||
pub struct ValidatorsStore<T: Session> { | ||
#[store(returns = Vec<<T as Session>::ValidatorId>)] | ||
/// Marker for the runtime | ||
pub _runtime: PhantomData<T>, | ||
} | ||
|
||
default_impl!(ValidatorsStore); | ||
|
||
/// Set the session keys for a validator. | ||
#[derive(Encode, Call, Debug)] | ||
pub struct SetKeysCall<T: Session> { | ||
/// The keys | ||
pub keys: T::Keys, | ||
/// The proof. This is not currently used and can be set to an empty vector. | ||
pub proof: Vec<u8>, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,252 @@ | ||
// Copyright 2019-2020 Parity Technologies (UK) Ltd. | ||
// This file is part of substrate-subxt. | ||
// | ||
// subxt 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. | ||
// | ||
// subxt 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-subxt. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
//! Implements support for the pallet_staking module. | ||
|
||
use super::balances::{ | ||
Balances, | ||
BalancesEventsDecoder as _, | ||
}; | ||
use codec::{ | ||
Decode, | ||
Encode, | ||
}; | ||
|
||
use std::{ | ||
collections::BTreeMap, | ||
fmt::Debug, | ||
marker::PhantomData, | ||
}; | ||
|
||
pub use pallet_staking::{ | ||
ActiveEraInfo, | ||
EraIndex, | ||
Exposure, | ||
Nominations, | ||
RewardDestination, | ||
RewardPoint, | ||
StakingLedger, | ||
ValidatorPrefs, | ||
}; | ||
|
||
/// Rewards for the last `HISTORY_DEPTH` eras. | ||
/// If reward hasn't been set or has been removed then 0 reward is returned. | ||
#[derive(Clone, Encode, Decode, Debug, Store)] | ||
pub struct ErasRewardPointsStore<T: Staking> { | ||
#[store(returns = EraRewardPoints<T::AccountId>)] | ||
/// Era index | ||
pub index: EraIndex, | ||
/// Marker for the runtime | ||
pub _phantom: PhantomData<T>, | ||
} | ||
|
||
/// Preference of what happens regarding validation. | ||
#[derive(Clone, Encode, Decode, Debug, Call)] | ||
pub struct SetPayeeCall<T: Staking> { | ||
/// The payee | ||
pub payee: RewardDestination, | ||
/// Marker for the runtime | ||
pub _runtime: PhantomData<T>, | ||
} | ||
|
||
/// The subset of the `frame::Trait` that a client must implement. | ||
#[module] | ||
pub trait Staking: Balances {} | ||
|
||
/// Number of eras to keep in history. | ||
/// | ||
/// Information is kept for eras in `[current_era - history_depth; current_era]`. | ||
/// | ||
/// Must be more than the number of eras delayed by session otherwise. | ||
/// I.e. active era must always be in history. | ||
/// I.e. `active_era > current_era - history_depth` must be guaranteed. | ||
#[derive(Encode, Decode, Copy, Clone, Debug, Default, Store)] | ||
pub struct HistoryDepthStore<T: Staking> { | ||
#[store(returns = u32)] | ||
/// Marker for the runtime | ||
pub _runtime: PhantomData<T>, | ||
} | ||
|
||
/// Map from all locked "stash" accounts to the controller account. | ||
#[derive(Encode, Copy, Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd, Store)] | ||
pub struct BondedStore<T: Staking> { | ||
#[store(returns = Option<T::AccountId>)] | ||
/// Tٗhe stash account | ||
pub stash: T::AccountId, | ||
} | ||
|
||
/// Map from all (unlocked) "controller" accounts to the info regarding the staking. | ||
#[derive(Encode, Copy, Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd, Store)] | ||
pub struct LedgerStore<T: Staking> { | ||
#[store(returns = Option<StakingLedger<T::AccountId, T::Balance>>)] | ||
/// The controller account | ||
pub controller: T::AccountId, | ||
} | ||
|
||
/// Where the reward payment should be made. Keyed by stash. | ||
#[derive(Encode, Copy, Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd, Store)] | ||
pub struct PayeeStore<T: Staking> { | ||
#[store(returns = RewardDestination)] | ||
/// Tٗhe stash account | ||
pub stash: T::AccountId, | ||
} | ||
|
||
/// The map from (wannabe) validator stash key to the preferences of that validator. | ||
#[derive(Encode, Copy, Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd, Store)] | ||
pub struct ValidatorsStore<T: Staking> { | ||
#[store(returns = ValidatorPrefs)] | ||
/// Tٗhe stash account | ||
pub stash: T::AccountId, | ||
} | ||
|
||
/// The map from nominator stash key to the set of stash keys of all validators to nominate. | ||
#[derive(Encode, Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Store)] | ||
pub struct NominatorsStore<T: Staking> { | ||
#[store(returns = Option<Nominations<T::AccountId>>)] | ||
/// Tٗhe stash account | ||
pub stash: T::AccountId, | ||
} | ||
|
||
/// The current era index. | ||
/// | ||
/// This is the latest planned era, depending on how the Session pallet queues the validator | ||
/// set, it might be active or not. | ||
#[derive(Encode, Copy, Clone, Debug, Store)] | ||
pub struct CurrentEraStore<T: Staking> { | ||
#[store(returns = Option<EraIndex>)] | ||
/// Marker for the runtime | ||
pub _runtime: PhantomData<T>, | ||
} | ||
|
||
/// Reward points of an era. Used to split era total payout between validators. | ||
/// | ||
/// This points will be used to reward validators and their respective nominators. | ||
#[derive(PartialEq, Encode, Decode, Default, Debug)] | ||
pub struct EraRewardPoints<AccountId: Ord> { | ||
/// Total number of points. Equals the sum of reward points for each validator. | ||
pub total: RewardPoint, | ||
/// The reward points earned by a given validator. | ||
pub individual: BTreeMap<AccountId, RewardPoint>, | ||
} | ||
|
||
/// Declare no desire to either validate or nominate. | ||
/// | ||
/// Effective at the beginning of the next era. | ||
/// | ||
/// The dispatch origin for this call must be _Signed_ by the controller, not the stash. | ||
/// Can only be called when [`EraElectionStatus`] is `Closed`. | ||
#[derive(Debug, Call, Encode)] | ||
pub struct ChillCall<T: Staking> { | ||
/// Runtime marker | ||
pub _runtime: PhantomData<T>, | ||
} | ||
|
||
impl<T: Staking> Default for ChillCall<T> { | ||
fn default() -> Self { | ||
Self { | ||
_runtime: PhantomData, | ||
} | ||
} | ||
} | ||
impl<T: Staking> Clone for ChillCall<T> { | ||
fn clone(&self) -> Self { | ||
Self { | ||
_runtime: self._runtime, | ||
} | ||
} | ||
} | ||
impl<T: Staking> Copy for ChillCall<T> {} | ||
|
||
/// Declare the desire to validate for the origin controller. | ||
/// | ||
/// Effective at the beginning of the next era. | ||
/// | ||
/// The dispatch origin for this call must be _Signed_ by the controller, not the stash. | ||
/// Can only be called when [`EraElectionStatus`] is `Closed`. | ||
#[derive(Clone, Debug, PartialEq, Call, Encode)] | ||
pub struct ValidateCall<T: Staking> { | ||
/// Runtime marker | ||
pub _runtime: PhantomData<T>, | ||
/// Validation preferences | ||
pub prefs: ValidatorPrefs, | ||
} | ||
|
||
/// Declare the desire to nominate `targets` for the origin controller. | ||
/// | ||
/// Effective at the beginning of the next era. | ||
/// | ||
/// The dispatch origin for this call must be _Signed_ by the controller, not the stash. | ||
/// Can only be called when [`EraElectionStatus`] is `Closed`. | ||
#[derive(Call, Encode, Debug)] | ||
pub struct NominateCall<T: Staking> { | ||
/// The targets that are being nominated | ||
pub targets: Vec<T::Address>, | ||
} | ||
|
||
#[cfg(all(test, feature = "integration-tests"))] | ||
mod tests { | ||
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. Please clean up the tests before asking for a review. This is PoC-level stuff here. :) 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. The #[cfg(test)]
#[cfg(feature = "integration-tests")] or added 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. I think he is referring to looking how it is handled already by other tests. Even better would be to add the staking module to the test runtime we use for tests 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. I tried adding the staking module, but it would up pulling in a huge portion of Kusama. I think we would be better off using an actual Kusama node instead. 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. Is there a reason to use a feature gate instead of #[ignore]? 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. No, fixed. 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. I also fixed the test suite to retry on temporary errors. 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. No I was referring to the test code itself: it contains a bunch of my lame attempts at figuring out how to test this. It's not something we want to actually review. :/ I think @ascjones added the @demi the retry thing doesn't actually work for me. If you use EDIT: I think the problem is that if you run a single node the chain isn't progressing so when we submit several extrinsics they end up failing in random order with "Extrinsic Usurped" or "Priority is too low: (1155024648500 vs 1155024648500)". Edit-2: the contracts tests are run with the 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.
Yeah usually that is probably because the nonce needs incrementing. I came up with a rudimentary solution here which appears to work consistently: https://github.com/paritytech/substrate-subxt/blob/aj-contract-call/src/frame/contracts.rs#L175
I agree. I assume these tests would work against That said for this PR I am happy for the tests to be run manually against a local node of whatever flavour. |
||
use super::*; | ||
use crate::{ | ||
extrinsic::PairSigner, | ||
runtimes::KusamaRuntime as RT, | ||
ClientBuilder, | ||
}; | ||
// use sp_core::{sr25519::Pair, Pair as _}; | ||
use crate::{ | ||
extrinsic::Signer, | ||
frame::{ | ||
balances::*, | ||
system::*, | ||
}, | ||
}; | ||
use sp_keyring::AccountKeyring; | ||
|
||
#[async_std::test] | ||
async fn test_nominate() { | ||
env_logger::try_init().ok(); | ||
let alice = PairSigner::<RT, _>::new(AccountKeyring::Alice.pair()); | ||
let bob = PairSigner::<RT, _>::new(AccountKeyring::Bob.pair()); | ||
|
||
let client = ClientBuilder::<RT>::new().build().await.unwrap(); | ||
let current_era = client.current_era(None).await.unwrap(); | ||
println!("Current era: {:?}", current_era); | ||
let hd = client.history_depth(None).await.unwrap(); | ||
println!("History depth: {:?}", hd); | ||
let total_issuance = client.total_issuance(None).await.unwrap(); | ||
println!("total issuance: {:?}", total_issuance); | ||
let alice_account = client.account(&alice.account_id(), None).await.unwrap(); | ||
println!("Alice's account info: {:?}", alice_account); | ||
let o = client | ||
.nominate(&alice, vec![bob.account_id().clone()]) | ||
.await | ||
.unwrap(); | ||
println!("Nom nom: {:?}", o); | ||
let o = client | ||
.validate(&bob, ValidatorPrefs::default()) | ||
.await | ||
.unwrap(); | ||
println!("Validator result: {:?}", o); | ||
for &i in &[RewardDestination::Controller] { | ||
for &j in &[&bob, &alice] { | ||
println!( | ||
"Transaction result: {:?}", | ||
client.set_payee(j, i).await.unwrap() | ||
); | ||
client.chill(j).await.unwrap(); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,6 +101,8 @@ pub trait System { | |
+ MaybeSerialize | ||
+ Debug | ||
+ MaybeDisplay | ||
+ Encode | ||
+ Decode | ||
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. I think these are redundant, they come with 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. q: |
||
+ Ord | ||
+ Default; | ||
|
||
|
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.
Why is kusama the default feature?