diff --git a/CHANGELOG.md b/CHANGELOG.md index 85f6b0c20..b5d85401c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [D] New `pallet-ddc-validator` which implements DDC CDN nodes validation and rewarding. You can enable DDC validation providing `--enable-ddc-validation` argument and `--dac-url` argument to specify DAC endpoint. It will only work on the nodes with validation and offchain workers enabled as well. - [D] Several calls for `pallet-ddc-staking` to distribute rewards. +- [D] Third kind of account in DDC Staking for DDC nodes (along with stash and controller). - [D] DDC cluster managers access control list in `pallet-ddc-staking` managed by governance. - [Zombienet](https://github.com/paritytech/zombienet) configurations to test block building and spawn a network for DDC validation debugging. diff --git a/pallets/ddc-staking/src/benchmarking.rs b/pallets/ddc-staking/src/benchmarking.rs index e7a35e87f..70dde5733 100644 --- a/pallets/ddc-staking/src/benchmarking.rs +++ b/pallets/ddc-staking/src/benchmarking.rs @@ -21,19 +21,22 @@ benchmarks! { let controller = create_funded_user::("controller", USER_SEED, 100); let controller_lookup: ::Source = T::Lookup::unlookup(controller.clone()); + let node = create_funded_user::("node", USER_SEED, 100); + let node_lookup: ::Source = T::Lookup::unlookup(node.clone()); let amount = T::Currency::minimum_balance() * 10u32.into(); whitelist_account!(stash); - }: _(RawOrigin::Signed(stash.clone()), controller_lookup, amount) + }: _(RawOrigin::Signed(stash.clone()), controller_lookup, node_lookup, amount) verify { assert!(Bonded::::contains_key(stash)); assert!(Ledger::::contains_key(controller)); + assert!(Nodes::::contains_key(node)); } unbond { // clean up any existing state. clear_storages_and_edges::(); - let (stash, controller) = create_stash_controller::(0, 100)?; + let (stash, controller, _) = create_stash_controller_node::(0, 100)?; let ledger = Ledger::::get(&controller).ok_or("ledger not created before")?; let original_bonded: BalanceOf = ledger.active; let amount = T::Currency::minimum_balance() * 5u32.into(); // Half of total @@ -47,7 +50,7 @@ benchmarks! { } withdraw_unbonded { - let (stash, controller) = create_stash_controller::(0, 100)?; + let (stash, controller, _) = create_stash_controller_node::(0, 100)?; let amount = T::Currency::minimum_balance() * 5u32.into(); // Half of total DdcStaking::::unbond(RawOrigin::Signed(controller.clone()).into(), amount)?; CurrentEra::::put(EraIndex::max_value()); @@ -62,7 +65,7 @@ benchmarks! { } store { - let (stash, controller) = create_stash_controller_with_balance::(0, T::DefaultStorageBondSize::get())?; + let (stash, controller, _) = create_stash_controller_node_with_balance::(0, T::DefaultStorageBondSize::get())?; whitelist_account!(controller); }: _(RawOrigin::Signed(controller), 1) @@ -71,7 +74,7 @@ benchmarks! { } serve { - let (stash, controller) = create_stash_controller_with_balance::(0, T::DefaultEdgeBondSize::get())?; + let (stash, controller, _) = create_stash_controller_node_with_balance::(0, T::DefaultEdgeBondSize::get())?; whitelist_account!(controller); }: _(RawOrigin::Signed(controller), 1) @@ -83,7 +86,7 @@ benchmarks! { // clean up any existing state. clear_storages_and_edges::(); - let (edge_stash, edge_controller) = create_stash_controller_with_balance::(0, T::DefaultEdgeBondSize::get())?; + let (edge_stash, edge_controller, _) = create_stash_controller_node_with_balance::(0, T::DefaultEdgeBondSize::get())?; DdcStaking::::serve(RawOrigin::Signed(edge_controller.clone()).into(), 1)?; assert!(Edges::::contains_key(&edge_stash)); CurrentEra::::put(1); @@ -97,7 +100,7 @@ benchmarks! { } set_controller { - let (stash, _) = create_stash_controller::(USER_SEED, 100)?; + let (stash, _, _) = create_stash_controller_node::(USER_SEED, 100)?; let new_controller = create_funded_user::("new_controller", USER_SEED, 100); let new_controller_lookup = T::Lookup::unlookup(new_controller.clone()); whitelist_account!(stash); @@ -106,6 +109,16 @@ benchmarks! { assert!(Ledger::::contains_key(&new_controller)); } + set_node { + let (stash, _, _) = create_stash_controller_node::(USER_SEED, 100)?; + let new_node = create_funded_user::("new_node", USER_SEED, 100); + let new_node_lookup = T::Lookup::unlookup(new_node.clone()); + whitelist_account!(stash); + }: _(RawOrigin::Signed(stash), new_node_lookup) + verify { + assert!(Nodes::::contains_key(&new_node)); + } + allow_cluster_manager { let new_cluster_manager = create_funded_user::("cluster_manager", USER_SEED, 100); let new_cluster_manager_lookup = T::Lookup::unlookup(new_cluster_manager.clone()); diff --git a/pallets/ddc-staking/src/lib.rs b/pallets/ddc-staking/src/lib.rs index fa05a1ade..b54dbc1a6 100644 --- a/pallets/ddc-staking/src/lib.rs +++ b/pallets/ddc-staking/src/lib.rs @@ -271,7 +271,7 @@ pub mod pallet { pub type Edges = StorageMap<_, Twox64Concat, T::AccountId, ClusterId>; /// The map of (wannabe) storage network participants stash keys to the DDC cluster ID they wish - /// to participate into.. + /// to participate into. #[pallet::storage] #[pallet::getter(fn storages)] pub type Storages = StorageMap<_, Twox64Concat, T::AccountId, ClusterId>; @@ -334,10 +334,15 @@ pub mod pallet { #[pallet::getter(fn cluster_managers)] pub type ClusterManagers = StorageValue<_, Vec, ValueQuery>; + /// Map from DDC node ID to the node operator stash account. + #[pallet::storage] + #[pallet::getter(fn nodes)] + pub type Nodes = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>; + #[pallet::genesis_config] pub struct GenesisConfig { - pub edges: Vec<(T::AccountId, T::AccountId, BalanceOf, ClusterId)>, - pub storages: Vec<(T::AccountId, T::AccountId, BalanceOf, ClusterId)>, + pub edges: Vec<(T::AccountId, T::AccountId, T::AccountId, BalanceOf, ClusterId)>, + pub storages: Vec<(T::AccountId, T::AccountId, T::AccountId, BalanceOf, ClusterId)>, pub settings: Vec<(ClusterId, BalanceOf, EraIndex, BalanceOf, EraIndex)>, } @@ -376,7 +381,7 @@ pub mod pallet { } // Add initial CDN participants - for &(ref stash, ref controller, balance, cluster) in &self.edges { + for &(ref stash, ref controller, ref node, balance, cluster) in &self.edges { assert!( T::Currency::free_balance(&stash) >= balance, "Stash do not have enough balance to participate in CDN." @@ -384,6 +389,7 @@ pub mod pallet { assert_ok!(Pallet::::bond( T::RuntimeOrigin::from(Some(stash.clone()).into()), T::Lookup::unlookup(controller.clone()), + T::Lookup::unlookup(node.clone()), balance, )); assert_ok!(Pallet::::serve( @@ -393,7 +399,7 @@ pub mod pallet { } // Add initial storage network participants - for &(ref stash, ref controller, balance, cluster) in &self.storages { + for &(ref stash, ref controller, ref node, balance, cluster) in &self.storages { assert!( T::Currency::free_balance(&stash) >= balance, "Stash do not have enough balance to participate in storage network." @@ -401,6 +407,7 @@ pub mod pallet { assert_ok!(Pallet::::bond( T::RuntimeOrigin::from(Some(stash.clone()).into()), T::Lookup::unlookup(controller.clone()), + T::Lookup::unlookup(node.clone()), balance, )); assert_ok!(Pallet::::store( @@ -442,7 +449,7 @@ pub mod pallet { NotStash, /// Stash is already bonded. AlreadyBonded, - /// Controller is already paired. + /// Controller or node is already paired. AlreadyPaired, /// Cannot have a storage network or CDN participant, with the size less than defined by /// governance (see `BondSize`). If unbonding is the intention, `chill` first to remove @@ -500,6 +507,7 @@ pub mod pallet { pub fn bond( origin: OriginFor, controller: ::Source, + node: ::Source, #[pallet::compact] value: BalanceOf, ) -> DispatchResult { let stash = ensure_signed(origin)?; @@ -519,8 +527,17 @@ pub mod pallet { Err(Error::::InsufficientBond)? } + let node = T::Lookup::lookup(node)?; + + // Reject a bond with a known DDC node. + if Nodes::::contains_key(&node) { + Err(Error::::AlreadyPaired)? + } + frame_system::Pallet::::inc_consumers(&stash).map_err(|_| Error::::BadState)?; + Nodes::::insert(&node, &stash); + // You're auto-bonded forever, here. We might improve this by only bonding when // you actually store/serve and remove once you unbond __everything__. >::insert(&stash, &controller); @@ -906,6 +923,33 @@ pub mod pallet { Ok(()) } + + /// (Re-)set the DDC node of a node operator stash account. Requires to chill first. + /// + /// The dispatch origin for this call must be _Signed_ by the stash, not the controller. + #[pallet::weight(T::WeightInfo::set_node())] + pub fn set_node( + origin: OriginFor, + new_node: ::Source, + ) -> DispatchResult { + let stash = ensure_signed(origin)?; + + let new_node = T::Lookup::lookup(new_node)?; + + if let Some(existing_node_stash) = Nodes::::get(&new_node) { + if existing_node_stash != stash { + Err(Error::::AlreadyPaired)? + } + } + + // Ensure only one node per stash during the DDC era. + ensure!(!>::contains_key(&stash), Error::::AlreadyInRole); + ensure!(!>::contains_key(&stash), Error::::AlreadyInRole); + + >::insert(new_node, stash); + + Ok(()) + } } impl Pallet { @@ -1021,6 +1065,10 @@ pub mod pallet { >::remove(stash); >::remove(&controller); + if let Some((node, _)) = >::iter().find(|(_, v)| v == stash) { + >::remove(node); + } + Self::do_remove_storage(stash); Self::do_remove_edge(stash); diff --git a/pallets/ddc-staking/src/mock.rs b/pallets/ddc-staking/src/mock.rs index c82675b73..3d0dae597 100644 --- a/pallets/ddc-staking/src/mock.rs +++ b/pallets/ddc-staking/src/mock.rs @@ -195,17 +195,17 @@ impl ExtBuilder { let mut edges = vec![]; if self.has_edges { edges = vec![ - // (stash, controller, stake, cluster) - (11, 10, 100, 1), - (21, 20, 100, 1), + // (stash, controller, node, stake, cluster) + (11, 10, 12, 100, 1), + (21, 20, 22, 100, 1), ]; } let mut storages = vec![]; if self.has_storages { storages = vec![ - // (stash, controller, stake, cluster) - (31, 30, 100, 1), - (41, 40, 100, 1), + // (stash, controller, node, stake, cluster) + (31, 30, 32, 100, 1), + (41, 40, 42, 100, 1), ]; } diff --git a/pallets/ddc-staking/src/testing_utils.rs b/pallets/ddc-staking/src/testing_utils.rs index 6d31a4749..4898d7313 100644 --- a/pallets/ddc-staking/src/testing_utils.rs +++ b/pallets/ddc-staking/src/testing_utils.rs @@ -43,29 +43,43 @@ pub fn create_funded_user_with_balance( } /// Create a stash and controller pair. -pub fn create_stash_controller( +pub fn create_stash_controller_node( n: u32, balance_factor: u32, -) -> Result<(T::AccountId, T::AccountId), &'static str> { +) -> Result<(T::AccountId, T::AccountId, T::AccountId), &'static str> { let stash = create_funded_user::("stash", n, balance_factor); let controller = create_funded_user::("controller", n, balance_factor); let controller_lookup: ::Source = T::Lookup::unlookup(controller.clone()); + let node = create_funded_user::("node", n, balance_factor); + let node_lookup: ::Source = T::Lookup::unlookup(node.clone()); let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into(); - DdcStaking::::bond(RawOrigin::Signed(stash.clone()).into(), controller_lookup, amount)?; - return Ok((stash, controller)) + DdcStaking::::bond( + RawOrigin::Signed(stash.clone()).into(), + controller_lookup, + node_lookup, + amount, + )?; + return Ok((stash, controller, node)) } /// Create a stash and controller pair with fixed balance. -pub fn create_stash_controller_with_balance( +pub fn create_stash_controller_node_with_balance( n: u32, balance: crate::BalanceOf, -) -> Result<(T::AccountId, T::AccountId), &'static str> { +) -> Result<(T::AccountId, T::AccountId, T::AccountId), &'static str> { let stash = create_funded_user_with_balance::("stash", n, balance); let controller = create_funded_user_with_balance::("controller", n, balance); let controller_lookup: ::Source = T::Lookup::unlookup(controller.clone()); + let node = create_funded_user_with_balance::("node", n, balance); + let node_lookup: ::Source = T::Lookup::unlookup(node.clone()); - DdcStaking::::bond(RawOrigin::Signed(stash.clone()).into(), controller_lookup, balance)?; - Ok((stash, controller)) + DdcStaking::::bond( + RawOrigin::Signed(stash.clone()).into(), + controller_lookup, + node_lookup, + balance, + )?; + Ok((stash, controller, node)) } diff --git a/pallets/ddc-staking/src/tests.rs b/pallets/ddc-staking/src/tests.rs index 6d024cf64..17fe47caa 100644 --- a/pallets/ddc-staking/src/tests.rs +++ b/pallets/ddc-staking/src/tests.rs @@ -109,12 +109,12 @@ fn staking_should_work() { let _ = Balances::make_free_balance_be(&i, 2000); } - // Add new CDN participant, account 3 controlled by 4. - assert_ok!(DdcStaking::bond(RuntimeOrigin::signed(3), 4, 1500)); + // Add new CDN participant, account 3 controlled by 4 with node 5. + assert_ok!(DdcStaking::bond(RuntimeOrigin::signed(3), 4, 5, 1500)); assert_ok!(DdcStaking::serve(RuntimeOrigin::signed(4), 1)); - // Account 4 controls the stash from account 3, which is 1500 units and 3 is a CDN - // participant. + // Account 4 controls the stash from account 3, which is 1500 units, 3 is a CDN + // participant, 5 is a DDC node. assert_eq!(DdcStaking::bonded(&3), Some(4)); assert_eq!( DdcStaking::ledger(&4), @@ -127,6 +127,7 @@ fn staking_should_work() { }) ); assert_eq!(DdcStaking::edges(3), Some(1)); + assert_eq!(DdcStaking::nodes(5), Some(3)); // Set `CurrentEra`. Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP); diff --git a/pallets/ddc-staking/src/weights.rs b/pallets/ddc-staking/src/weights.rs index acb858060..3b69615ab 100644 --- a/pallets/ddc-staking/src/weights.rs +++ b/pallets/ddc-staking/src/weights.rs @@ -1,7 +1,7 @@ //! Autogenerated weights for pallet_ddc_staking //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-09-01, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-10-10, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `e14`, CPU: `11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -35,6 +35,7 @@ pub trait WeightInfo { fn serve() -> Weight; fn chill() -> Weight; fn set_controller() -> Weight; + fn set_node() -> Weight; fn allow_cluster_manager() -> Weight; fn disallow_cluster_manager() -> Weight; } @@ -44,11 +45,12 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: DdcStaking Bonded (r:1 w:1) // Storage: DdcStaking Ledger (r:1 w:1) + // Storage: DdcStaking Nodes (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn bond() -> Weight { - Weight::from_ref_time(49_113_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + Weight::from_ref_time(55_007_000 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(4 as u64)) } // Storage: DdcStaking Ledger (r:1 w:1) // Storage: DdcStaking Edges (r:1 w:0) @@ -105,6 +107,12 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } + // Storage: DdcStaking Nodes (r:1 w:1) + fn set_node() -> Weight { + Weight::from_ref_time(21_779_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } // Storage: DdcStaking ClusterManagers (r:1 w:1) fn allow_cluster_manager() -> Weight { Weight::from_ref_time(11_727_000 as u64) @@ -123,11 +131,12 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { // Storage: DdcStaking Bonded (r:1 w:1) // Storage: DdcStaking Ledger (r:1 w:1) + // Storage: DdcStaking Nodes (r:1 w:1) // Storage: Balances Locks (r:1 w:1) fn bond() -> Weight { - Weight::from_ref_time(49_113_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + Weight::from_ref_time(55_007_000 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(4 as u64)) } // Storage: DdcStaking Ledger (r:1 w:1) // Storage: DdcStaking Edges (r:1 w:0) @@ -184,6 +193,12 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) } + // Storage: DdcStaking Nodes (r:1 w:1) + fn set_node() -> Weight { + Weight::from_ref_time(21_779_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } // Storage: DdcStaking ClusterManagers (r:1 w:1) fn allow_cluster_manager() -> Weight { Weight::from_ref_time(11_727_000 as u64) diff --git a/pallets/ddc-validator/src/lib.rs b/pallets/ddc-validator/src/lib.rs index 117eefe73..5d3b91005 100644 --- a/pallets/ddc-validator/src/lib.rs +++ b/pallets/ddc-validator/src/lib.rs @@ -812,13 +812,20 @@ pub mod pallet { for assigned_edge in assigned_edges.iter() { log::debug!("assigned edge: {:?}", assigned_edge); + let Some((node, _)) = >::iter().find(|(n, s)| assigned_edge == s) else { + log::debug!("no known node for: {:?}", assigned_edge); + continue; + }; + + log::debug!("assigned edge node: {:?}", assigned_edge); + // form url for each node let edge_url = format!( "{}{}{}/$.{}", data_url, "ddc:dac:aggregation:nodes:", current_ddc_era - 1, - utils::account_to_string::(assigned_edge.clone()) + utils::account_to_string::(node) ); log::debug!("edge url: {:?}", edge_url); diff --git a/pallets/ddc-validator/src/mock.rs b/pallets/ddc-validator/src/mock.rs index 00497301c..92b3f3b26 100644 --- a/pallets/ddc-validator/src/mock.rs +++ b/pallets/ddc-validator/src/mock.rs @@ -317,6 +317,8 @@ pub fn new_test_ext() -> sp_io::TestExternalities { (AccountId::from([0x1; 32]), 1000), // edge controller (AccountId::from([0x11; 32]), 1000), + // edge node + (AccountId::from([0x21; 32]), 1000), // validator1 stash; has to be equal to the OCW key in the current implementation ( AccountId::from([ @@ -366,7 +368,13 @@ pub fn new_test_ext() -> sp_io::TestExternalities { let _ = pallet_staking::GenesisConfig:: { stakers, ..Default::default() } .assimilate_storage(&mut storage); - let edges = vec![(AccountId::from([0x1; 32]), AccountId::from([0x11; 32]), 100, 1)]; + let edges = vec![( + AccountId::from([0x1; 32]), + AccountId::from([0x11; 32]), + AccountId::from([0x21; 32]), + 100, + 1, + )]; let storages = vec![]; let _ = pallet_ddc_staking::GenesisConfig:: { edges, storages, ..Default::default() } .assimilate_storage(&mut storage); diff --git a/pallets/ddc-validator/src/tests.rs b/pallets/ddc-validator/src/tests.rs index fedd3f713..5c8056efd 100644 --- a/pallets/ddc-validator/src/tests.rs +++ b/pallets/ddc-validator/src/tests.rs @@ -38,8 +38,11 @@ fn it_sets_validation_decision_with_one_validator_in_quorum() { t.register_extension(TransactionPoolExt::new(pool)); let era_to_validate: EraIndex = 3; - let cdn_node_to_validate = AccountId::from([0x1; 32]); - let cdn_node_to_validate_str = utils::account_to_string::(cdn_node_to_validate.clone()); + let edge_stash_to_validate = AccountId::from([0x1; 32]); + let edge_stash_to_validate_str = + utils::account_to_string::(edge_stash_to_validate.clone()); + let edge_node_to_validate = AccountId::from([0x21; 32]); + let edge_node_to_validate_str = utils::account_to_string::(edge_node_to_validate.clone()); let validator_1_stash = AccountId::from([ 0xd2, 0xbf, 0x4b, 0x84, 0x4d, 0xfe, 0xfd, 0x67, 0x72, 0xa8, 0x84, 0x3e, 0x66, 0x9f, 0x94, 0x34, 0x08, 0x96, 0x6a, 0x97, 0x7e, 0x3a, 0xe2, 0xaf, 0x1d, 0xd7, 0x8e, 0x0f, 0x55, 0xf4, @@ -67,7 +70,7 @@ fn it_sets_validation_decision_with_one_validator_in_quorum() { expect_request( &format!( "{}/JSON.GET/ddc:dac:aggregation:nodes:{}/$.{}", - DEFAULT_DATA_PROVIDER_URL, era_to_validate, cdn_node_to_validate_str + DEFAULT_DATA_PROVIDER_URL, era_to_validate, edge_node_to_validate_str ), include_bytes!("./mock-data/set-1/aggregated-node-data-for-era.json"), ); @@ -116,7 +119,7 @@ fn it_sets_validation_decision_with_one_validator_in_quorum() { "{}/FCALL/save_validation_result_by_node/1/{}:{}:{}/{}", DEFAULT_DATA_PROVIDER_URL, OCW_PUB_KEY_STR, - cdn_node_to_validate_str, + edge_stash_to_validate_str, era_to_validate, url_encoded_result_json, ), @@ -195,9 +198,9 @@ fn it_sets_validation_decision_with_one_validator_in_quorum() { tx.call, crate::mock::RuntimeCall::DdcValidator(crate::Call::set_validation_decision { era: era_to_validate, - cdn_node: cdn_node_to_validate.clone(), + cdn_node: edge_stash_to_validate.clone(), validation_decision: ValidationDecision { - edge: cdn_node_to_validate_str, + edge: edge_stash_to_validate_str, result: true, payload: utils::hash(&serialized_decisions), totals: DacTotalAggregates { @@ -213,7 +216,7 @@ fn it_sets_validation_decision_with_one_validator_in_quorum() { let tx = transactions.pop().unwrap(); let tx = Extrinsic::decode(&mut &*tx).unwrap(); - let stakers_points = vec![(cdn_node_to_validate, common_decision.totals.sent)]; + let stakers_points = vec![(edge_stash_to_validate, common_decision.totals.sent)]; assert!(tx.signature.is_some()); assert_eq!( diff --git a/runtime/cere-dev/src/lib.rs b/runtime/cere-dev/src/lib.rs index ff5778e9e..3a9719d7c 100644 --- a/runtime/cere-dev/src/lib.rs +++ b/runtime/cere-dev/src/lib.rs @@ -130,7 +130,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 48005, + spec_version: 48006, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 5,