Skip to content

Commit

Permalink
project voting data api (#224)
Browse files Browse the repository at this point in the history
* add back sudo to rococo

* runtime api

* runtime-api-wiring

* some dummy log statements

* attempt 1 to add try-runtime

* second attempt

* add to the cli

* fix warn issue

* on initialise, remove on runtime upgrade proposasl

* add the new storage version migration

* pick runtime

* new migration for proposals

* fmt

* fix v3-v4 migration test

* fix 2

* fix brief migration test

* clippy

* fmt

* new MilestoneVote implementation

* fix implementation

* get project individual votes

* remove total votes api

* fmt

* ummm

* new format for Individual votes

* boom baby, and a fmt

* clippy

* fix build

* fmt

* bump spec version

* 1

* individual votes impl with storage

* fix buggy impl

* define immutability, update on vote

* use individual votes for oninit and submit and close voting + update migration

* fixing build

* seperated test files, tested ImmutableVotes

* fix runtime api, reorg proposals

* fmt

* fmt

* clippy

* migration, err handling

* fix migration

* fmt

* fix

* clippy

* update executive and spec version

* fmt

* remove todos

* remove wrong comment

* fix failing test

* fmt

* run on gcp

* bump to rebuild

* use self hosted runner

* already installed on runner

* attempt auto scaling runner

* increase disk size

* bump to build again

* dont delete at the end of the job

* remove unused directories

* bump

* change region

* split the jobs

* add -y flag

* test menually installing rust

* -y flag

* more -y

* remove double install of protobugf

* -y

* trial 1

* fix

* fix2

* fix3

* bump up

* bump

* change zone

* bump

* lol -y

* bump

* bump 2

* set home directory

* try to run as user

* init commit

* commit 2

* test

* test 3

* attempt 4

* bump

* bump

* bump

* bump

* bump

* reduce image size

* bump

* bump

* bump again

* add clippy

* build essentials

* clang

* refactor

* typo

* typo 2

* make zone configurable

---------

Co-authored-by: samelamin <hussam.elamin@gmail.com>
(cherry picked from commit 84dd997)

# Conflicts:
#	Cargo.lock
#	pallets/proposals/src/lib.rs
  • Loading branch information
f-gate authored and samelamin committed Oct 12, 2023
1 parent 5f644ab commit abd14e4
Show file tree
Hide file tree
Showing 18 changed files with 1,602 additions and 1,234 deletions.
2,229 changes: 1,081 additions & 1,148 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion node/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ where
C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>,
C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Nonce>,
C::Api: BlockBuilder<Block>,
C::Api: pallet_proposals_rpc::ProposalsRuntimeApi<Block, AccountId>,
C::Api: pallet_proposals_rpc::ProposalsRuntimeApi<Block, AccountId, Balance>,
P: TransactionPool + Sync + Send + 'static,
{
use pallet_proposals_rpc::{Proposals, ProposalsApiServer};
Expand Down
1 change: 1 addition & 0 deletions pallets/fellowship/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"]
codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [
"derive",
] }
serde = { version = "1.0.101", features = ["derive"] }
scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false, optional = true }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false }
Expand Down
1 change: 1 addition & 0 deletions pallets/proposals/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ orml-xtokens = { git = "https://github.com/open-web3-stack/open-runtime-module-l
# Local Dependencies
common-traits = { path = "../../libs/common-traits", default-features = false }
common-types = { path = "../../libs/common-types", default-features = false }
common-runtime = { path = "../../runtime/common", default-features = false }
pallet-deposits = { path = "../deposits", default-features = false }

[dev-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions pallets/proposals/rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features =

jsonrpsee = { version = "0.16.2", features = ["server", "macros"] }


# Substrate packages
sp-api = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43" }
sp-rpc = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43" }
sp-blockchain = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43" }
sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43" }
frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43" }
frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43" }
pallet-proposals-rpc-runtime-api = {path = "./runtime-api" }
pallet-proposals-rpc-runtime-api = { default-features = false, path = "./runtime-api" }
sp-std = {default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43"}


[features]
Expand Down
3 changes: 3 additions & 0 deletions pallets/proposals/rpc/runtime-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ targets = ["x86_64-unknown-linux-gnu"]
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [
"derive",
] }
serde = { version = "1.0.101", features = ["derive"], default-features = false}
scale-info = { version = "2.0.1", default-features = false, features = ["derive"] }

sp-api = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43"}
sp-std = {default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43"}
sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43" }
frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43" }
frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43" }
Expand Down
7 changes: 6 additions & 1 deletion pallets/proposals/rpc/runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
#![cfg_attr(not(feature = "std"), no_std)]

use sp_std::vec::Vec;

sp_api::decl_runtime_apis! {
pub trait ProposalsApi<AccountId> where AccountId: codec::Codec {
pub trait ProposalsApi<AccountId, Balance>
where AccountId: codec::Codec + Ord,
{
fn get_project_account_by_id(project_id: u32) -> AccountId;
fn get_all_project_data(project_id: u32) -> Option<(Vec<u8>, Vec<u8>)>;
}
}
27 changes: 23 additions & 4 deletions pallets/proposals/rpc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
use codec::Codec;

use jsonrpsee::{
core::{Error as JsonRpseeError, RpcResult},
proc_macros::rpc,
types::error::{CallError, ErrorObject},
};
pub use pallet_proposals_rpc_runtime_api::ProposalsApi as ProposalsRuntimeApi;

use sp_blockchain::HeaderBackend;
use sp_runtime::traits::Block as BlockT;
use sp_std::vec::Vec;

use std::fmt::Display;
use std::sync::Arc;

// Runtime api return type.

#[rpc(client, server)]
pub trait ProposalsApi<BlockHash, AccountId> {
pub trait ProposalsApi<BlockHash, AccountId: Ord, Balance>
where
AccountId: Ord,
{
#[method(name = "proposals_getProjectKitty")]
fn project_account_id(&self, project_id: u32) -> RpcResult<AccountId>;
#[method(name = "proposals_getAllProjectData")]
fn all_project_data(&self, project_id: u32) -> RpcResult<Option<(Vec<u8>, Vec<u8>)>>;
}

pub struct Proposals<C, B> {
Expand Down Expand Up @@ -48,14 +59,15 @@ impl From<Error> for i32 {
}
}

impl<C, B, AccountId> ProposalsApiServer<<B as BlockT>::Hash, AccountId> for Proposals<C, B>
impl<C, B, AccountId, Balance> ProposalsApiServer<<B as BlockT>::Hash, AccountId, Balance>
for Proposals<C, B>
where
C: sp_api::ProvideRuntimeApi<B>,
C: HeaderBackend<B>,
C: Send + Sync + 'static,
C::Api: ProposalsRuntimeApi<B, AccountId>,
C::Api: ProposalsRuntimeApi<B, AccountId, Balance>,
B: BlockT,
AccountId: Clone + Display + Codec + Send + 'static,
AccountId: Clone + Display + Codec + Send + 'static + Ord,
{
fn project_account_id(&self, project_id: u32) -> RpcResult<AccountId> {
let api = self.client.runtime_api();
Expand All @@ -64,6 +76,13 @@ where
api.get_project_account_by_id(at, project_id)
.map_err(runtime_error_into_rpc_err)
}
fn all_project_data(&self, project_id: u32) -> RpcResult<Option<(Vec<u8>, Vec<u8>)>> {
let api = self.client.runtime_api();
let at = self.client.info().best_hash;

api.get_all_project_data(at, project_id)
.map_err(runtime_error_into_rpc_err)
}
}

/// Converts a runtime trap into an RPC error.
Expand Down
72 changes: 72 additions & 0 deletions pallets/proposals/src/impls/immutable_votes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use crate::*;
impl<T: Config> ImmutableIndividualVotes<T> {
/// Create a new set of individual votes bound to a set of milestone keys.
/// Instantiates the votes as defaults.
#[allow(clippy::type_complexity)]
pub(crate) fn new(
milestone_keys: BoundedVec<MilestoneKey, T::MaxMilestonesPerProject>,
) -> Self {
let mut outer_votes: BoundedBTreeMap<
MilestoneKey,
BoundedBTreeMap<AccountIdOf<T>, bool, T::MaximumContributorsPerProject>,
T::MaxMilestonesPerProject,
> = BoundedBTreeMap::new();

for milestone_key in milestone_keys.iter() {
let inner_votes: BoundedBTreeMap<
AccountIdOf<T>,
bool,
T::MaximumContributorsPerProject,
> = BoundedBTreeMap::new();
// outer_votes and milestone_keys are bounded by the same binding so this will never fail.
outer_votes
.try_insert(milestone_key.to_owned(), inner_votes)
.expect("milestone_keys and outer_votes have been bound by the same binding; qed");
}

Self { inner: outer_votes }
}

/// Insert the vote from an individual on a milestone.
pub(crate) fn insert_individual_vote(
&mut self,
milestone_key: MilestoneKey,
account_id: &AccountIdOf<T>,
vote: bool,
) -> Result<(), DispatchError> {
if let Some(votes) = self.inner.get_mut(&milestone_key) {
if let Some(_existing_vote) = votes.get_mut(account_id) {
return Err(Error::<T>::VotesAreImmutable.into());
} else {
votes
.try_insert(account_id.clone(), vote)
.map_err(|_| Error::<T>::TooManyContributions)?;
}
} else {
return Err(Error::<T>::IndividualVoteNotFound.into());
}

Ok(())
}

/// Clear the votes for a given milestone.
/// Used when a milestone is submitted.
/// Skips if the milestone is not found.
pub(crate) fn clear_milestone_votes(&mut self, milestone_key: MilestoneKey) {
if let Some(btree) = self.inner.get_mut(&milestone_key) {
*btree = Default::default()
}
}

/// Take a mutable reference to the inner individual votes item.
#[allow(dead_code)]
pub(crate) fn as_mut(&mut self) -> &mut IndividualVotes<T> {
&mut self.inner
}
}

impl<T: Config> AsRef<IndividualVotes<T>> for ImmutableIndividualVotes<T> {
fn as_ref(&self) -> &IndividualVotes<T> {
&self.inner
}
}
5 changes: 5 additions & 0 deletions pallets/proposals/src/impls/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod immutable_votes;
pub mod pallet_impls;

pub use immutable_votes::*;
pub use pallet_impls::*;
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,24 @@ impl<T: Config> Pallet<T> {
.map_err(|_| Error::<T>::Overflow)?;
Ok::<(), DispatchError>(())
})?;
UserHasVoted::<T>::remove((project_key, RoundType::VotingRound, milestone_key));

<MilestoneVotes<T>>::insert(project_key, milestone_key, Vote::default());
IndividualVoteStore::<T>::try_mutate(project_key, |maybe_votes| {
if let Some(individual_votes) = maybe_votes {
individual_votes.clear_milestone_votes(milestone_key);
} else {
return Err(Error::<T>::IndividualVoteNotFound.into());
};
Ok::<(), DispatchError>(())
})?;

MilestoneVotes::<T>::try_mutate(project_key, |vote_btree| {
vote_btree
.try_insert(milestone_key, Vote::default())
.map_err(|_| Error::<T>::TooManyMilestoneVotes)?;

Ok::<(), DispatchError>(())
})?;

Self::deposit_event(Event::MilestoneSubmitted(who, project_key, milestone_key));
Self::deposit_event(Event::VotingRoundCreated(project_key));
Ok(().into())
Expand All @@ -70,23 +85,22 @@ impl<T: Config> Pallet<T> {
let now = frame_system::Pallet::<T>::block_number();
let user_has_voted_key = (project_key, RoundType::VotingRound, milestone_key);

UserHasVoted::<T>::try_mutate(user_has_voted_key, |votes| {
ensure!(!votes.contains_key(&who), Error::<T>::VotesAreImmutable);
votes
.try_insert(who.clone(), approve_milestone)
.map_err(|_| Error::<T>::Overflow)?;
IndividualVoteStore::<T>::try_mutate(project_key, |maybe_individual_votes| {
if let Some(individual_votes) = maybe_individual_votes {
individual_votes.insert_individual_vote(milestone_key, &who, approve_milestone)?;
}
Ok::<(), DispatchError>(())
})?;

let vote: Vote<BalanceOf<T>> =
MilestoneVotes::<T>::try_mutate(project_key, milestone_key, |vote| {
if let Some(v) = vote {
MilestoneVotes::<T>::try_mutate(project_key, |vote_btree| {
if let Some(vote) = vote_btree.get_mut(&milestone_key) {
if approve_milestone {
v.yay = v.yay.saturating_add(contribution_amount);
vote.yay = vote.yay.saturating_add(contribution_amount);
} else {
v.nay = v.nay.saturating_add(contribution_amount);
vote.nay = vote.nay.saturating_add(contribution_amount);
}
Ok::<Vote<BalanceOf<T>>, DispatchError>(v.clone())
Ok::<Vote<BalanceOf<T>>, DispatchError>(vote.clone())
} else {
Err(Error::<T>::VotingRoundNotStarted.into())
}
Expand All @@ -102,6 +116,7 @@ impl<T: Config> Pallet<T> {
user_has_voted_key,
who.clone(),
)?;

Self::deposit_event(Event::VoteSubmitted(
who,
project_key,
Expand Down Expand Up @@ -426,14 +441,14 @@ impl<T: Config> Pallet<T> {
}
});

Self::close_voting_round(project_key, user_has_voted_key)?;

Self::deposit_event(Event::MilestoneApproved(
who,
project_key,
user_has_voted_key.2,
<frame_system::Pallet<T>>::block_number(),
));

Self::close_voting_round(project_key, user_has_voted_key)?;
}

if vote.nay >= funding_threshold {
Expand All @@ -457,7 +472,15 @@ impl<T: Config> Pallet<T> {
// Prevent hook from calling.
RoundsExpiring::<T>::remove(exp_block);
// Allow future votes to occur on this milestone
UserHasVoted::<T>::remove(user_has_voted_key);
IndividualVoteStore::<T>::try_mutate(project_key, |maybe_individual_votes| {
if let Some(individual_votes) = maybe_individual_votes {
individual_votes.clear_milestone_votes(user_has_voted_key.2);
} else {
return Err(Error::<T>::IndividualVoteNotFound.into());
}
Ok::<(), DispatchError>(())
})?;

Ok(())
}
}
Loading

0 comments on commit abd14e4

Please sign in to comment.