Skip to content

Commit

Permalink
Service option to exclude polls from being voted on which belong to c…
Browse files Browse the repository at this point in the history
…ertain tracks (#33)

`--exclude-tracks` is a comma separated list of track IDs. Vote requests
for such tracks return with a `TrackNotAllowed` bad request error.

If a track is excluded and there are already vote requests for the
track, then a warning is issued but the requests will be mixed. This is
to honour the `/vote` request having previously returned HTTP OK.
However, further vote requests will not be allowed.
  • Loading branch information
shamsasari authored Aug 5, 2024
1 parent f5195d2 commit aa5f4fa
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 11 deletions.
14 changes: 7 additions & 7 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ resolver = "2"
[workspace.package]
homepage = "https://projectglove.io/"
repository = "https://github.com/projectglove/glove-monorepo/"
version = "0.0.8"
version = "0.0.9"

[workspace.dependencies]
anyhow = "1.0.86"
Expand Down
1 change: 1 addition & 0 deletions service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ pub struct GloveContext {
pub enclave_handle: EnclaveHandle,
pub attestation_bundle: AttestationBundle,
pub network: CallableSubstrateNetwork,
pub exclude_tracks: HashSet<u16>,
pub regular_mix_enabled: bool,
pub state: GloveState,
}
Expand Down
46 changes: 43 additions & 3 deletions service/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
use std::io;
use std::sync::Arc;
use std::time::Duration;

use anyhow::{bail, Context};
use anyhow::{anyhow, bail, Context};
use aws_sdk_dynamodb::error::SdkError;
use aws_sdk_dynamodb::operation::delete_item::DeleteItemError;
use aws_sdk_dynamodb::operation::put_item::PutItemError;
Expand Down Expand Up @@ -86,6 +86,13 @@ struct Args {
#[arg(long, value_enum, verbatim_doc_comment, default_value_t = EnclaveMode::Nitro)]
enclave_mode: EnclaveMode,

/// List of track IDs, seperated by comma, for which polls are not allowed to be voted on.
///
/// See https://wiki.polkadot.network/docs/learn-polkadot-opengov-origins#origins-and-tracks-info
/// for list of track IDs.
#[arg(long, verbatim_doc_comment, value_delimiter = ',')]
exclude_tracks: Vec<u16>,

/// If specified, will mix vote requests at a regular interval.
///
/// This is only useful for testing and development purposes. Otherwise, in production this
Expand Down Expand Up @@ -195,10 +202,13 @@ async fn main() -> anyhow::Result<()> {
enclave_handle,
attestation_bundle,
network,
exclude_tracks: args.exclude_tracks.into_iter().collect(),
regular_mix_enabled: args.regular_mix,
state: GloveState::default()
});

check_excluded_tracks(&glove_context).await?;

let subscan = Subscan::new(glove_context.network.network_name.clone(), args.subscan_api_key);
if args.regular_mix {
warn!("Regular mixing of votes is enabled. This is not suitable for production.");
Expand All @@ -220,6 +230,30 @@ async fn main() -> anyhow::Result<()> {
Ok(())
}

async fn check_excluded_tracks(context: &Arc<GloveContext>) -> anyhow::Result<()> {
let track_infos = context.network.get_tracks()?;
let mut excluded_track_infos = HashMap::new();
for exclude_track_id in &context.exclude_tracks {
let track_info = track_infos.get(&exclude_track_id)
.ok_or_else(|| anyhow!("Excluded track {} not found", exclude_track_id))?;
excluded_track_infos.insert(exclude_track_id, &track_info.name);
}

info!("Excluded tracks: {:?}", excluded_track_infos);

for poll_index in context.storage.get_poll_indices().await? {
let Some(poll_info) = context.network.get_ongoing_poll(poll_index).await? else {
continue;
};
if excluded_track_infos.contains_key(&poll_info.track) {
warn!("Poll {} belongs to track {} which has been excluded. Existing vote requests \
will be mixed, but further requests will not be accepted.", poll_index, poll_info.track);
}
}

Ok(())
}

async fn initialize_enclave(enclave_mode: EnclaveMode) -> io::Result<EnclaveHandle> {
match enclave_mode {
EnclaveMode::Nitro => {
Expand Down Expand Up @@ -395,8 +429,11 @@ async fn vote(
if request.genesis_hash != network.api.genesis_hash() {
return Err(BadVoteRequestError::ChainMismatch.into());
}
if network.get_ongoing_poll(request.poll_index).await?.is_none() {
let Some(poll_info) = network.get_ongoing_poll(request.poll_index).await? else {
return Err(BadVoteRequestError::PollNotOngoing.into());
};
if context.exclude_tracks.contains(&poll_info.track) {
return Err(BadVoteRequestError::TrackNotAllowed.into());
}
if context.state.is_non_glove_voter(request.poll_index, &request.account).await {
return Err(BadVoteRequestError::VotedOutsideGlove.into());
Expand Down Expand Up @@ -670,6 +707,8 @@ enum BadVoteRequestError {
VotedOutsideGlove,
#[error("Poll is not ongoing or does not exist")]
PollNotOngoing,
#[error("Poll belongs to a track which is not allowed for voting")]
TrackNotAllowed,
#[error("Votes for poll has already been mixed and submitted on-chain")]
PollAlreadyMixed,
#[error("Insufficient account balance for vote")]
Expand All @@ -684,6 +723,7 @@ impl IntoResponse for BadVoteRequestError {
BadVoteRequestError::NotMember => "NotMember",
BadVoteRequestError::VotedOutsideGlove => "VotedOutsideGlove",
BadVoteRequestError::PollNotOngoing => "PollNotOngoing",
BadVoteRequestError::TrackNotAllowed => "TrackNotAllowed",
BadVoteRequestError::PollAlreadyMixed => "PollAlreadyMixed",
BadVoteRequestError::InsufficientBalance => "InsufficientBalance"
}.to_string();
Expand Down

0 comments on commit aa5f4fa

Please sign in to comment.