Skip to content
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

Summonerd: Make bids and timeouts configurable #3267

Merged
merged 1 commit into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions tools/summonerd/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/// Configuration for the
#[derive(Clone, Copy)]
pub struct Config {
pub phase1_timeout_secs: u64,
pub phase2_timeout_secs: u64,
pub min_bid_u64: u64,
pub max_strikes: u64,
}

impl Default for Config {
fn default() -> Self {
Self {
phase1_timeout_secs: 12 * 60,
phase2_timeout_secs: 8 * 60,
min_bid_u64: 1,
max_strikes: 3,
}
}
}

impl Config {
pub fn with_phase1_timeout_secs(mut self, x: Option<u64>) -> Self {
if let Some(x) = x {
self.phase1_timeout_secs = x;
}
self
}

pub fn with_phase2_timeout_secs(mut self, x: Option<u64>) -> Self {
if let Some(x) = x {
self.phase2_timeout_secs = x;
}
self
}

pub fn with_min_bid_u64(mut self, x: Option<u64>) -> Self {
if let Some(x) = x {
self.min_bid_u64 = x;
}
self
}

pub fn with_max_strikes(mut self, x: Option<u64>) -> Self {
if let Some(x) = x {
self.max_strikes = x;
}
self
}
}
16 changes: 12 additions & 4 deletions tools/summonerd/src/coordinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,26 @@ use std::time::Duration;

use anyhow::Result;

use crate::{participant::Participant, phase::Phase, queue::ParticipantQueue, storage::Storage};
use crate::{
config::Config, participant::Participant, phase::Phase, queue::ParticipantQueue,
storage::Storage,
};

const QUEUE_SLEEP_TIME_SECS: u64 = 1;

pub struct Coordinator {
config: Config,
storage: Storage,
queue: ParticipantQueue,
}

impl Coordinator {
pub fn new(storage: Storage, queue: ParticipantQueue) -> Self {
Self { storage, queue }
pub fn new(config: Config, storage: Storage, queue: ParticipantQueue) -> Self {
Self {
config,
storage,
queue,
}
}

pub async fn run<P: Phase + 'static>(mut self) -> Result<()> {
Expand Down Expand Up @@ -46,7 +54,7 @@ impl Coordinator {
async fn contribute<P: Phase>(&mut self, contributor: Participant) -> Result<()> {
let address = contributor.address();
match tokio::time::timeout(
Duration::from_secs(P::CONTRIBUTION_TIME_SECS),
Duration::from_secs(P::contribution_time(self.config)),
self.contribute_inner::<P>(contributor),
)
.await
Expand Down
36 changes: 31 additions & 5 deletions tools/summonerd/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod config;
mod coordinator;
mod participant;
mod penumbra_knower;
Expand Down Expand Up @@ -36,6 +37,7 @@ use tracing::Instrument;
use tracing_subscriber::{prelude::*, EnvFilter};
use url::Url;

use crate::config::Config;
use crate::phase::Phase1;
use crate::phase::Phase2;
use crate::phase::PhaseMarker;
Expand Down Expand Up @@ -131,6 +133,14 @@ enum Command {
#[clap(long, display_order = 902, default_value = "127.0.0.1:8080")]
/// The address to bind the gRPC and web servers to.
bind_addr: SocketAddr,
#[clap(long, display_order = 1000)]
phase1_timeout_secs: Option<u64>,
#[clap(long, display_order = 1001)]
phase2_timeout_secs: Option<u64>,
#[clap(long, display_order = 1002)]
min_bid_u64: Option<u64>,
#[clap(long, display_order = 1002)]
max_strikes: Option<u64>,
},
/// Export the output of the ceremony
Export {
Expand Down Expand Up @@ -158,13 +168,23 @@ impl Opt {
fvk,
node,
bind_addr,
phase1_timeout_secs,
phase2_timeout_secs,
min_bid_u64,
max_strikes,
} => {
let config = Config::default()
.with_phase1_timeout_secs(phase1_timeout_secs)
.with_phase2_timeout_secs(phase2_timeout_secs)
.with_min_bid_u64(min_bid_u64)
.with_max_strikes(max_strikes);
let marker = match phase {
1 => PhaseMarker::P1,
2 => PhaseMarker::P2,
_ => anyhow::bail!("Phase must be 1 or 2."),
};
let storage = Storage::load_or_initialize(ceremony_db(&storage_dir)).await?;
let storage =
Storage::load_or_initialize(config, ceremony_db(&storage_dir)).await?;
// Check if we've transitioned, for a nice error message
if marker == PhaseMarker::P2
&& storage.transition_extra_information().await?.is_none()
Expand All @@ -175,7 +195,7 @@ impl Opt {
PenumbraKnower::load_or_initialize(storage_dir.join("penumbra.db"), &fvk, node)
.await?;
let queue = ParticipantQueue::new();
let coordinator = Coordinator::new(storage.clone(), queue.clone());
let coordinator = Coordinator::new(config, storage.clone(), queue.clone());
let coordinator_span = tracing::error_span!("coordinator");
let coordinator_handle = match marker {
PhaseMarker::P1 => {
Expand Down Expand Up @@ -226,13 +246,17 @@ impl Opt {
// This is assumed to be valid as it's the starting point for the ceremony.
let phase_1_root = phase_1_raw_root.assume_valid();

let mut storage = Storage::load_or_initialize(ceremony_db(&storage_dir)).await?;
let mut storage =
Storage::load_or_initialize(Config::default(), ceremony_db(&storage_dir))
.await?;
storage.set_root(phase_1_root).await?;

Ok(())
}
Command::Transition { storage_dir } => {
let mut storage = Storage::load_or_initialize(ceremony_db(&storage_dir)).await?;
let mut storage =
Storage::load_or_initialize(Config::default(), ceremony_db(&storage_dir))
.await?;

let phase1_crs = match storage.phase1_current_crs().await? {
Some(x) => x,
Expand All @@ -247,7 +271,9 @@ impl Opt {
storage_dir,
target_dir,
} => {
let storage = Storage::load_or_initialize(ceremony_db(&storage_dir)).await?;
let storage =
Storage::load_or_initialize(Config::default(), ceremony_db(&storage_dir))
.await?;
// Grab phase1 output
let phase1_crs = match storage.phase1_current_crs().await? {
Some(x) => x,
Expand Down
18 changes: 11 additions & 7 deletions tools/summonerd/src/phase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use penumbra_proto::tools::summoning::v1alpha1::{
participate_request::Contribution as PBContribution, CeremonyCrs,
};

use crate::storage::Storage;
use crate::{config::Config, storage::Storage};

/// A simple marker for which phase we're in, which some code can depend on at runtime.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand All @@ -36,10 +36,8 @@ pub trait Phase {
/// The constant value for the marker we use, for runtime dispatch.
const MARKER: PhaseMarker;

/// The amount of time we should wait for a contribution.
///
/// This varies since one phase is more expensive than the other.
const CONTRIBUTION_TIME_SECS: u64;
/// Get the contribution time from a config
fn contribution_time(config: Config) -> u64;

/// Serialize the CRS value, in a potentially failing way.
fn serialize_crs(data: Self::CRS) -> Result<CeremonyCrs>;
Expand Down Expand Up @@ -80,7 +78,10 @@ impl Phase for Phase1 {
type RawContribution = Phase1RawCeremonyContribution;
type Contribution = Phase1CeremonyContribution;
const MARKER: PhaseMarker = PhaseMarker::P1;
const CONTRIBUTION_TIME_SECS: u64 = 20 * 60;

fn contribution_time(config: Config) -> u64 {
config.phase1_timeout_secs
}

fn serialize_crs(data: Self::CRS) -> Result<CeremonyCrs> {
data.try_into()
Expand Down Expand Up @@ -128,7 +129,10 @@ impl Phase for Phase2 {
type RawContribution = Phase2RawCeremonyContribution;
type Contribution = Phase2CeremonyContribution;
const MARKER: PhaseMarker = PhaseMarker::P2;
const CONTRIBUTION_TIME_SECS: u64 = 10 * 60;

fn contribution_time(config: Config) -> u64 {
config.phase2_timeout_secs
}

fn serialize_crs(data: Self::CRS) -> Result<CeremonyCrs> {
data.try_into()
Expand Down
29 changes: 17 additions & 12 deletions tools/summonerd/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ use r2d2_sqlite::{
};
use tokio::task::spawn_blocking;

use crate::{penumbra_knower::PenumbraKnower, phase::PhaseMarker};

const MIN_BID_AMOUNT_U64: u64 = 1u64;
const MAX_STRIKES: u64 = 3u64;
use crate::{config::Config, penumbra_knower::PenumbraKnower, phase::PhaseMarker};

/// The current time as a unix timestamp.
///
Expand All @@ -53,21 +50,28 @@ pub enum ContributionAllowed {

#[derive(Clone)]
pub struct Storage {
config: Config,
pool: r2d2::Pool<SqliteConnectionManager>,
}

impl Storage {
/// If the database at `storage_path` exists, [`Self::load`] it, otherwise, [`Self::initialize`] it.
pub async fn load_or_initialize(storage_path: impl AsRef<Utf8Path>) -> anyhow::Result<Self> {
pub async fn load_or_initialize(
config: Config,
storage_path: impl AsRef<Utf8Path>,
) -> anyhow::Result<Self> {
if storage_path.as_ref().exists() {
return Self::load(storage_path).await;
return Self::load(config, storage_path).await;
}

Self::initialize(storage_path).await
Self::initialize(config, storage_path).await
}

/// Initialize creates the database, but does not insert anything into it.
async fn initialize(storage_path: impl AsRef<Utf8Path>) -> anyhow::Result<Self> {
async fn initialize(
config: Config,
storage_path: impl AsRef<Utf8Path>,
) -> anyhow::Result<Self> {
// Connect to the database (or create it)
let pool = Self::connect(storage_path)?;

Expand All @@ -81,13 +85,14 @@ impl Storage {

tx.commit()?;

Ok(Storage { pool })
Ok(Storage { config, pool })
})
.await?
}

async fn load(path: impl AsRef<Utf8Path>) -> anyhow::Result<Self> {
async fn load(config: Config, path: impl AsRef<Utf8Path>) -> anyhow::Result<Self> {
let storage = Self {
config,
pool: Self::connect(path)?,
};

Expand Down Expand Up @@ -196,7 +201,7 @@ impl Storage {
// - Hasn't already contributed
// - Not banned
let amount = knower.total_amount_sent_to_me(&address).await?;
if amount < Amount::from(MIN_BID_AMOUNT_U64) {
if amount < Amount::from(self.config.min_bid_u64) {
return Ok(ContributionAllowed::DidntBidEnough(amount));
}
let has_contributed = {
Expand All @@ -213,7 +218,7 @@ impl Storage {
if has_contributed {
return Ok(ContributionAllowed::AlreadyContributed);
}
if self.get_strikes(address).await? >= MAX_STRIKES {
if self.get_strikes(address).await? >= self.config.max_strikes {
return Ok(ContributionAllowed::Banned);
}
Ok(ContributionAllowed::Yes(amount))
Expand Down
Loading