From cb887c9583710e67824e933bf74d925b97e636d7 Mon Sep 17 00:00:00 2001
From: MujkicA <32431923+MujkicA@users.noreply.github.com>
Date: Tue, 27 Feb 2024 14:58:38 +0100
Subject: [PATCH 01/11] fix: cmp ordering of dispense tracker entries (#53)
Fixes comparison between entries of the dispense tracker
---
src/dispense_tracker.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dispense_tracker.rs b/src/dispense_tracker.rs
index 1307573..ecd2025 100644
--- a/src/dispense_tracker.rs
+++ b/src/dispense_tracker.rs
@@ -13,7 +13,7 @@ pub struct Entry {
impl Ord for Entry {
fn cmp(&self, other: &Self) -> Ordering {
- other.timestamp.cmp(&self.timestamp)
+ self.timestamp.cmp(&other.timestamp)
}
}
From 0d9391a2785158b32533446741bfeee9492415c9 Mon Sep 17 00:00:00 2001
From: Sophie Dankel <47993817+sdankel@users.noreply.github.com>
Date: Tue, 5 Mar 2024 14:34:45 -0800
Subject: [PATCH 02/11] Add agreement checkboxes (#58)
---
static/index.html | 31 +++++++++++++++++++++++++++++--
1 file changed, 29 insertions(+), 2 deletions(-)
diff --git a/static/index.html b/static/index.html
index f76ec24..01fe4aa 100644
--- a/static/index.html
+++ b/static/index.html
@@ -138,6 +138,14 @@
color: #949494;
}
+ .agreements {
+ text-align: left;
+ margin-top: 25px;
+ line-height: 1.4em;
+ color: #555;
+ font-size: 14px;
+ }
+
.queued {
display: flex;
flex-direction: column;
@@ -220,8 +228,16 @@
Waiting until more tokens are available
+
+
+
+
+
+
+
+
-
+
Test Ether sent to the wallet
@@ -243,6 +259,17 @@
Test Ether sent to the wallet
}
}
+ let agreements = document.getElementById('agreements');
+ function hasAgreed() {
+ let inputs = agreements.getElementsByTagName('input');
+ let inputsList = Array.prototype.slice.call(inputs);
+ return inputsList.every(i => i.checked);
+ }
+
+ agreements.onchange = function() {
+ form.querySelector("input[type=submit]").disabled = !hasAgreed();
+ };
+
function hasCaptcha() {
return !!document.getElementsByClassName("captcha-container")[0];
}
@@ -260,7 +287,7 @@
Test Ether sent to the wallet
document.getElementsByClassName("captcha-container")[0].classList.remove("hidden");
}
document.getElementsByClassName("queued")[0].classList.add("hidden");
- form.querySelector("input[type=submit]").disabled = false;
+ form.querySelector("input[type=submit]").disabled = !hasAgreed();
}
function give_me_coins(form) {
From ee618bc6c4badffec7e7cb13baf71072925f05ce Mon Sep 17 00:00:00 2001
From: Green Baneling
Date: Thu, 7 Mar 2024 11:17:34 +0100
Subject: [PATCH 03/11] Skip arguments in the logs (#59)
---
src/routes.rs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/routes.rs b/src/routes.rs
index 5bd91ce..47af2e9 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -64,7 +64,7 @@ pub async fn main(Extension(config): Extension) -> Html {
Html(render_page(public_node_url, captcha_key))
}
-#[tracing::instrument(skip(wallet))]
+#[tracing::instrument(skip_all)]
pub async fn health(Extension(wallet): Extension) -> Response {
// ping client for health
let client = wallet
@@ -202,7 +202,7 @@ async fn submit_tx_with_timeout(
Ok(())
}
-#[tracing::instrument(skip(wallet, config))]
+#[tracing::instrument(skip_all)]
pub async fn dispense_tokens(
Json(input): Json,
Extension(wallet): Extension,
@@ -352,7 +352,7 @@ pub async fn dispense_tokens(
})
}
-#[tracing::instrument(skip(config))]
+#[tracing::instrument(skip_all)]
pub async fn dispense_info(
Extension(config): Extension,
) -> Result {
From 7c261fa84d479301402d86ca790232ebd42ef08e Mon Sep 17 00:00:00 2001
From: MujkicA <32431923+MujkicA@users.noreply.github.com>
Date: Sat, 16 Mar 2024 08:37:19 +0100
Subject: [PATCH 04/11] Limit retries on dispense (#63)
* limit retries and add better logging
* make in progress explicit
* Minimize chance of miss behaviour
---------
Co-authored-by: xgreenx
---
src/dispense_tracker.rs | 62 ++++++++++++++----------------
src/lib.rs | 2 +-
src/main.rs | 4 +-
src/models.rs | 10 +++++
src/routes.rs | 83 +++++++++++++++++++++++++++--------------
5 files changed, 94 insertions(+), 67 deletions(-)
diff --git a/src/dispense_tracker.rs b/src/dispense_tracker.rs
index ecd2025..4ec62d2 100644
--- a/src/dispense_tracker.rs
+++ b/src/dispense_tracker.rs
@@ -1,45 +1,30 @@
-use std::{
- cmp::Ordering,
- collections::{BinaryHeap, HashMap, HashSet},
-};
+use std::collections::BTreeMap;
+use std::collections::{HashMap, HashSet};
+use std::time::{SystemTime, UNIX_EPOCH};
use fuel_types::Address;
-#[derive(Debug, Eq, PartialEq)]
-pub struct Entry {
- address: Address,
- timestamp: u64,
-}
-
-impl Ord for Entry {
- fn cmp(&self, other: &Self) -> Ordering {
- self.timestamp.cmp(&other.timestamp)
- }
-}
-
-impl PartialOrd for Entry {
- fn partial_cmp(&self, other: &Self) -> Option {
- Some(self.cmp(other))
- }
-}
-
pub trait Clock: std::fmt::Debug + Send + Sync {
fn now(&self) -> u64;
}
#[derive(Debug)]
-pub struct TokioTime {}
+pub struct StdTime {}
-impl Clock for TokioTime {
+impl Clock for StdTime {
fn now(&self) -> u64 {
- tokio::time::Instant::now().elapsed().as_secs()
+ let start = SystemTime::now();
+ let since_the_epoch = start
+ .duration_since(UNIX_EPOCH)
+ .expect("Time went backwards");
+ since_the_epoch.as_secs()
}
}
#[derive(Debug)]
pub struct DispenseTracker {
tracked: HashMap,
- queue: BinaryHeap,
+ queue: BTreeMap>,
in_progress: HashSet,
clock: Box,
}
@@ -48,9 +33,9 @@ impl Default for DispenseTracker {
fn default() -> Self {
Self {
tracked: HashMap::default(),
- queue: BinaryHeap::default(),
+ queue: Default::default(),
in_progress: HashSet::default(),
- clock: Box::new(TokioTime {}),
+ clock: Box::new(StdTime {}),
}
}
}
@@ -59,7 +44,7 @@ impl DispenseTracker {
pub fn new(clock: impl Clock + 'static) -> Self {
Self {
tracked: HashMap::new(),
- queue: BinaryHeap::new(),
+ queue: Default::default(),
in_progress: HashSet::new(),
clock: Box::new(clock),
}
@@ -70,7 +55,7 @@ impl DispenseTracker {
let timestamp = self.clock.now();
self.tracked.insert(address, timestamp);
- self.queue.push(Entry { address, timestamp });
+ self.queue.entry(timestamp).or_default().push(address);
}
pub fn mark_in_progress(&mut self, address: Address) {
@@ -84,10 +69,13 @@ impl DispenseTracker {
pub fn evict_expired_entries(&mut self, eviction_duration: u64) {
let now = self.clock.now();
- while let Some(oldest_entry) = self.queue.peek() {
- if now - oldest_entry.timestamp > eviction_duration {
- let removed_entry = self.queue.pop().unwrap();
- self.tracked.remove(&removed_entry.address);
+ while let Some(oldest_entry) = self.queue.first_entry() {
+ if now - oldest_entry.key() > eviction_duration {
+ let (_, addresses) = oldest_entry.remove_entry();
+
+ for address in addresses {
+ self.tracked.remove(&address);
+ }
} else {
break;
}
@@ -95,6 +83,10 @@ impl DispenseTracker {
}
pub fn has_tracked(&self, address: &Address) -> bool {
- self.tracked.get(address).is_some() || self.in_progress.contains(address)
+ self.tracked.get(address).is_some()
+ }
+
+ pub fn is_in_progress(&self, address: &Address) -> bool {
+ self.in_progress.contains(address)
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 019b8bc..3599a1a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -42,7 +42,7 @@ mod dispense_tracker;
mod recaptcha;
mod routes;
-pub use dispense_tracker::{Clock, TokioTime};
+pub use dispense_tracker::{Clock, StdTime};
pub use routes::THE_BIGGEST_AMOUNT;
#[derive(Debug)]
diff --git a/src/main.rs b/src/main.rs
index f89c5e6..8549ea1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,11 +1,11 @@
-use fuel_faucet::{config::Config, start_server, TokioTime};
+use fuel_faucet::{config::Config, start_server, StdTime};
use tracing_subscriber::EnvFilter;
#[tokio::main]
async fn main() {
let config = Config::default();
init_logger(&config);
- let clock = TokioTime {};
+ let clock = StdTime {};
let (_, task) = start_server(config, clock).await;
let _ = task.await.unwrap();
}
diff --git a/src/models.rs b/src/models.rs
index 42b4b33..9255906 100644
--- a/src/models.rs
+++ b/src/models.rs
@@ -1,3 +1,5 @@
+use std::fmt::{self, Display, Formatter};
+
use reqwest::StatusCode;
use serde::{Deserialize, Serialize};
@@ -24,3 +26,11 @@ pub struct DispenseError {
pub status: StatusCode,
pub error: String,
}
+
+impl Display for DispenseError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ write!(f, "{self:?}")
+ }
+}
+
+impl std::error::Error for DispenseError {}
diff --git a/src/routes.rs b/src/routes.rs
index 47af2e9..51aee11 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -135,6 +135,13 @@ fn check_and_mark_dispense_limit(
));
}
+ if tracker.is_in_progress(&address) {
+ return Err(error(
+ "Account is already in the process of receiving assets".to_string(),
+ StatusCode::TOO_MANY_REQUESTS,
+ ));
+ }
+
tracker.mark_in_progress(address);
Ok(())
}
@@ -238,27 +245,37 @@ pub async fn dispense_tokens(
}
check_and_mark_dispense_limit(&dispense_tracker, address, config.dispense_limit_interval)?;
- let cleanup = || {
+
+ struct CleanUpper(Fn)
+ where
+ Fn: FnMut();
+
+ impl Drop for CleanUpper
+ where
+ Fn: FnMut(),
+ {
+ fn drop(&mut self) {
+ self.0();
+ }
+ }
+
+ // We want to remove the address from `in_progress` regardless of the outcome of the transaction.
+ let _cleanup = CleanUpper(|| {
dispense_tracker
.lock()
.unwrap()
.remove_in_progress(&address);
- };
+ });
let provider = wallet.provider().expect("client provider");
- let mut tx_id;
- loop {
+ let mut tx_id = None;
+ for _ in 0..5 {
let mut guard = state.lock().await;
let coin_output = if let Some(previous_coin_output) = &guard.last_output {
*previous_coin_output
} else {
- get_coin_output(&wallet, config.dispense_amount)
- .await
- .map_err(|e| {
- cleanup();
- e
- })?
+ get_coin_output(&wallet, config.dispense_amount).await?
};
let coin_type = CoinType::Coin(Coin {
@@ -296,55 +313,63 @@ pub async fn dispense_tokens(
.fee_checked_from_tx(&network_config.network_info.consensus_parameters)
.expect("Should be able to calculate fee");
- tx_id = script.id(network_config.network_info.consensus_parameters.chain_id);
+ let id = script.id(network_config.network_info.consensus_parameters.chain_id);
let result = tokio::time::timeout(
Duration::from_secs(config.timeout),
provider.send_transaction(script),
)
.await
- .map(|r| {
+ .map_err(|_| {
+ error(
+ format!("Timeout while submitting transaction for address: {address:X}"),
+ StatusCode::INTERNAL_SERVER_ERROR,
+ )
+ })
+ .and_then(|r| {
r.map_err(|e| {
error(
- format!("Failed to submit transaction: {e}"),
+ format!(
+ "Failed to submit transaction for address: {address:X} with error: {}",
+ e
+ ),
StatusCode::INTERNAL_SERVER_ERROR,
)
})
- })
- .map_err(|e| {
- error(
- format!("Timeout while submitting transaction: {e}"),
- StatusCode::INTERNAL_SERVER_ERROR,
- )
});
match result {
- Ok(Ok(_)) => {
+ Ok(_) => {
guard.last_output = Some(CoinOutput {
- utxo_id: UtxoId::new(tx_id, 1),
+ utxo_id: UtxoId::new(id, 1),
owner: coin_output.owner,
amount: coin_output.amount - total_fee.min_fee() - config.dispense_amount,
});
+ tx_id = Some(id);
break;
}
- _ => {
+ Err(e) => {
+ tracing::warn!("{}", e);
guard.last_output = None;
}
};
}
- submit_tx_with_timeout(&client, &tx_id, config.timeout)
- .await
- .map_err(|e| {
- cleanup();
- e
- })?;
+ let Some(tx_id) = tx_id else {
+ return Err(error(
+ "Failed to submit transaction".to_string(),
+ StatusCode::INTERNAL_SERVER_ERROR,
+ ));
+ };
+
+ submit_tx_with_timeout(&client, &tx_id, config.timeout).await?;
info!(
"dispensed {} tokens to {:#x}",
config.dispense_amount, &address
);
- dispense_tracker.lock().unwrap().track(address);
+ let mut tracker = dispense_tracker.lock().unwrap();
+ tracker.track(address);
Ok(DispenseResponse {
status: "Success".to_string(),
From 1ab2314c19d344b3a8739f1e79661f94f7034295 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luiz=20Est=C3=A1cio=20=7C=20stacio=2Eeth?=
Date: Wed, 27 Mar 2024 16:01:35 -0300
Subject: [PATCH 05/11] feat: update tx link (#64)
---
src/models.rs | 1 +
src/routes.rs | 1 +
static/index.html | 7 ++-----
3 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/src/models.rs b/src/models.rs
index 9255906..79c1ffb 100644
--- a/src/models.rs
+++ b/src/models.rs
@@ -19,6 +19,7 @@ pub struct DispenseInput {
pub struct DispenseResponse {
pub status: String,
pub tokens: u64,
+ pub tx_id: String,
}
#[derive(Debug)]
diff --git a/src/routes.rs b/src/routes.rs
index 51aee11..221f091 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -374,6 +374,7 @@ pub async fn dispense_tokens(
Ok(DispenseResponse {
status: "Success".to_string(),
tokens: config.dispense_amount,
+ tx_id: tx_id.to_string(),
})
}
diff --git a/static/index.html b/static/index.html
index 01fe4aa..5e7f087 100644
--- a/static/index.html
+++ b/static/index.html
@@ -247,8 +247,7 @@ Test Ether sent to the wallet
Node url: {{ public_node_url }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Node url: {{ public_node_url }}
-
-
-
-