Skip to content

Commit

Permalink
feat: add --dry-run option
Browse files Browse the repository at this point in the history
Adding a dev-friendly "dry-run" mode, that prevents write operations:

  1. sending penumbra transactions
  2. posting messages in discord

This change makes it a lot easier to iterate on Galileo code locally.
  • Loading branch information
conorsch committed Nov 21, 2023
1 parent 9ede543 commit 1d84fa4
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 4 deletions.
10 changes: 10 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# run local instance of galileo, in dry-run mode, meaning no funds will be sent.
# assumes you've imported the galileo seed phrase locally
dev:
cargo run --release -- serve --dry-run 1penumbra --data-dir ~/.local/share/pcli-galileo

# scan discord history for faucet drips, collect in local csv, for inclusion in allocations.
history:
cargo run --release -- history \
--channel https://discord.com/channels/824484045370818580/915710851917439060 \
| tee discord-history.csv
26 changes: 23 additions & 3 deletions src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,19 @@ pub struct Handler {
/// times we've told the user about the rate limit (so that eventually we can stop replying if
/// they keep asking).
send_history: Arc<Mutex<SendHistory>>,
/// Whether read-only mode is enabled. If false, Penumbra transactions will be sent,
/// and Discord replies will be posted. If true, those events will be logged, but not actually
/// performed.
dry_run: bool,
}

impl Handler {
pub fn new(rate_limit: Duration, reply_limit: usize) -> Self {
pub fn new(rate_limit: Duration, reply_limit: usize, dry_run: bool) -> Self {
Handler {
rate_limit,
reply_limit,
send_history: Arc::new(Mutex::new(SendHistory::new())),
dry_run,
}
}
/// Check whether the bot can proceed with honoring this request,
Expand Down Expand Up @@ -231,7 +236,13 @@ impl SendHistory {

#[async_trait]
impl EventHandler for Handler {
#[instrument(skip(self, ctx))]
// The `Message` struct is very large, and clutters up debug logging, so we'll
// extract specific fields to make spans readable.
#[instrument(skip_all, fields(
user_name = %message.author.name,
user_id = %message.author.id,
message_id = %message.id,
))]
/// Respond to faucet request. For first-time requests, will spend a bit.
/// If the faucet request duplicates a previous request within the timeout
/// window, then Galileo will respond instructing the user to wait longer.
Expand Down Expand Up @@ -281,7 +292,11 @@ impl EventHandler for Handler {
"Please wait for another {} before requesting more tokens. Thanks!",
format_remaining_time(last_fulfilled, self.rate_limit)
);
reply(&ctx, &message, response).await;
if !self.dry_run {
reply(&ctx, &message, response).await;
} else {
tracing::debug!("dry_run: would reply with rate-limit response",);
}

// Setting the notified count to zero "un-rate-limits" an entry, which we do when a
// request fails, so we don't have to traverse the entire list:
Expand All @@ -299,6 +314,11 @@ impl EventHandler for Handler {
.unwrap()
.record_request(message_info.user_id, &penumbra_addresses);

if self.dry_run {
tracing::debug!("dry_run: would send tx");
return;
}

// Send the message to the queue, to be processed asynchronously
tracing::trace!("sending message to worker queue");
ctx.data
Expand Down
8 changes: 7 additions & 1 deletion src/opt/serve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ pub struct Serve {
/// Batch size for responding to catch-up backlog.
#[clap(long, default_value = "25")]
catch_up_batch_size: usize,
/// Disable transaction sending and Discord notifications. Useful for debugging.
#[clap(long)]
dry_run: bool,
/// The amounts to send for each response, written as typed values 1.87penumbra, 12cubes, etc.
values: Vec<Value>,
}
Expand Down Expand Up @@ -144,7 +147,7 @@ impl Serve {
let (send_requests, responder) = Responder::new(service, self.max_addresses, self.values);

// Create a watcher for Discord messages, which will manage spends and replies.
let handler = Handler::new(self.rate_limit, self.reply_limit);
let handler = Handler::new(self.rate_limit, self.reply_limit, self.dry_run);

tracing::debug!("configuring discord client");
// Make a new client using a token set by an environment variable, with our handlers
Expand Down Expand Up @@ -214,6 +217,9 @@ impl Serve {
}
/// Perform sanity checks on CLI args prior to running.
async fn preflight_checks(&self) -> anyhow::Result<()> {
if self.dry_run {
tracing::info!("dry-run mode is enabled, won't send transactions or post messages");
}
if self.values.is_empty() {
anyhow::bail!("at least one value must be provided");
} else if self.values.iter().any(|v| v.amount.value().is_zero()) {
Expand Down

0 comments on commit 1d84fa4

Please sign in to comment.