Skip to content

Commit 962350b

Browse files
committed
temp
1 parent f581b27 commit 962350b

File tree

2 files changed

+117
-19
lines changed

2 files changed

+117
-19
lines changed

apps/fortuna/src/api.rs

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ mod live;
3232
mod metrics;
3333
mod ready;
3434
mod revelation;
35+
mod explorer;
3536

3637
pub type ChainId = String;
3738

@@ -48,7 +49,9 @@ pub struct ApiMetrics {
4849

4950
#[derive(Clone)]
5051
enum JournalLog {
51-
Observed,
52+
Observed {
53+
tx_hash: TxHash
54+
},
5255
FailedToReveal {
5356
reason: String,
5457
},
@@ -60,6 +63,17 @@ enum JournalLog {
6063
}
6164
}
6265

66+
impl JournalLog {
67+
pub fn get_tx_hash(&self) -> Option<TxHash> {
68+
match self {
69+
JournalLog::Observed { tx_hash } => Some(*tx_hash),
70+
JournalLog::FailedToReveal { .. } => None,
71+
JournalLog::Revealed { tx_hash } => Some(*tx_hash),
72+
JournalLog::Landed { .. } => None,
73+
}
74+
}
75+
}
76+
6377
#[derive(Clone)]
6478
struct TimedJournalLog {
6579
pub timestamp: DateTime<chrono::Utc>,
@@ -77,8 +91,10 @@ type RequestKey = (ChainId, u64);
7791

7892
#[derive(Default)]
7993
struct History {
80-
pub by_time: VecDeque<RequestKey>,
81-
pub by_chain: BTreeMap<RequestKey, RequestJournal>,
94+
pub by_hash: HashMap<TxHash, Vec<RequestKey>>,
95+
pub by_chain_and_time: BTreeMap<(ChainId, DateTime<chrono::Utc>), RequestKey>,
96+
pub by_time: BTreeMap<DateTime<chrono::Utc>, RequestKey>,
97+
pub by_request_key: HashMap<RequestKey, RequestJournal>,
8298
}
8399

84100
impl History {
@@ -88,62 +104,81 @@ impl History {
88104
}
89105

90106
pub fn add(&mut self, (chain_id, sequence): RequestKey, request_journal_log: TimedJournalLog){
91-
// Add to the by_chain map
92107
let mut new_entry = false;
93-
let entry = self.by_chain.entry((chain_id.clone(), sequence)).or_insert_with(|| {
108+
let entry = self.by_request_key.entry((chain_id.clone(), sequence)).or_insert_with(|| {
94109
new_entry = true;
95110
RequestJournal {
96111
chain_id: chain_id.clone(),
97112
sequence,
98113
journal: vec![],
99114
}
100115
});
116+
request_journal_log.log.get_tx_hash().map(|tx_hash| {
117+
self.by_hash
118+
.entry(tx_hash)
119+
.or_insert_with(Vec::new)
120+
.push((chain_id.clone(), sequence));
121+
});
101122
entry.journal.push(request_journal_log);
102123
if new_entry {
103-
self.by_time.push_back((chain_id.clone(), sequence));
124+
let current_time = chrono::Utc::now();
125+
self.by_chain_and_time
126+
.insert((chain_id.clone(), current_time), (chain_id.clone(), sequence));
127+
self.by_time
128+
.insert(current_time, (chain_id.clone(), sequence));
129+
104130
if self.by_time.len() > Self::MAX_HISTORY {
105-
let oldest_key = self.by_time.pop_front().unwrap();
106-
self.by_chain.remove(&oldest_key);
131+
// TODO
107132
}
108133
}
109134
}
110135

111-
pub fn get_request_logs(&self, request_key: &RequestKey) -> Option<&Vec<TimedJournalLog>> {
112-
self.by_chain.get(request_key).map(|entry| &entry.journal)
136+
pub fn get_request_logs(&self, request_key: &RequestKey) -> Option<&RequestJournal> {
137+
self.by_request_key.get(request_key)
113138
}
114139

115-
pub fn get_latest_requests(&self, chain_id: Option<&ChainId>, limit: u64) -> Vec<RequestJournal> {
140+
pub fn get_request_logs_by_tx_hash(&self, tx_hash: TxHash) -> Option<Vec<&RequestJournal>> {
141+
self.by_hash.get(&tx_hash).map(|request_keys| {
142+
request_keys.iter()
143+
.map(|request_key| self.by_request_key.get(request_key).unwrap())
144+
.collect()
145+
})
146+
}
147+
148+
pub fn get_latest_requests(&self, chain_id: Option<&ChainId>, limit: u64,
149+
min_timestamp: Option<DateTime<chrono::Utc>>,
150+
max_timestamp: Option<DateTime<chrono::Utc>>) -> Vec<RequestJournal> {
116151
match chain_id {
117152
Some(chain_id) => {
118-
let range = self.by_chain.range((chain_id.clone(), 0)..(chain_id.clone(), u64::MAX));
153+
let range = self.by_chain_and_time.range((chain_id.clone(), min_timestamp.unwrap_or(DateTime::<chrono::Utc>::MIN_UTC))..(chain_id.clone(), max_timestamp.unwrap_or(DateTime::<chrono::Utc>::MAX_UTC)));
119154
range.rev()
120155
.take(limit as usize)
121-
.map(|(_, entry)| entry.clone())
156+
.map(|(_, request_key)| {
157+
self.by_request_key.get(request_key).unwrap().clone()
158+
})
122159
.collect()
123160

124161
},
125162
None => {
126163
self.by_time
127-
.iter()
164+
.range(min_timestamp.unwrap_or(DateTime::<chrono::Utc>::MIN_UTC)..max_timestamp.unwrap_or(DateTime::<chrono::Utc>::MAX_UTC))
128165
.rev()
129166
.take(limit as usize)
130-
.map(|request_key| {
131-
self.by_chain.get(request_key).unwrap().clone()
167+
.map(|(_time, request_key)| {
168+
self.by_request_key.get(request_key).unwrap().clone()
132169
})
133170
.collect::<Vec<_>>()
134171
},
135172
}
136173
}
137-
138-
139174
}
140175

141176

142177
#[derive(Clone)]
143178
pub struct ApiState {
144179
pub chains: Arc<HashMap<ChainId, BlockchainState>>,
145180

146-
// pub history: Arc<History>
181+
pub history: Arc<RwLock<History>>,
147182

148183
pub metrics_registry: Arc<RwLock<Registry>>,
149184

@@ -170,6 +205,7 @@ impl ApiState {
170205
ApiState {
171206
chains: Arc::new(chains),
172207
metrics: Arc::new(metrics),
208+
history: Arc::new(RwLock::new(History::new())),
173209
metrics_registry,
174210
}
175211
}
@@ -208,6 +244,7 @@ pub enum RestError {
208244
/// The server cannot currently communicate with the blockchain, so is not able to verify
209245
/// which random values have been requested.
210246
TemporarilyUnavailable,
247+
BadFilterParameters(String),
211248
/// A catch-all error for all other types of errors that could occur during processing.
212249
Unknown,
213250
}
@@ -242,6 +279,11 @@ impl IntoResponse for RestError {
242279
"An unknown error occurred processing the request",
243280
)
244281
.into_response(),
282+
RestError::BadFilterParameters(message) => (
283+
StatusCode::BAD_REQUEST,
284+
format!("Invalid filter parameters: {}", message),
285+
)
286+
.into_response(),
245287
}
246288
}
247289
}

apps/fortuna/src/api/explorer.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use axum::extract::{Path, Query, State};
2+
use axum::Json;
3+
use ethers::types::TxHash;
4+
use utoipa::IntoParams;
5+
use crate::api::{BinaryEncoding, ChainId, GetRandomValueResponse, RestError, RevelationPathParams, RevelationQueryParams};
6+
use crate::chain::reader::BlockNumber;
7+
8+
9+
#[derive(Debug, serde::Serialize, serde::Deserialize, IntoParams)]
10+
#[into_params(parameter_in=Query)]
11+
pub struct ExplorerQueryParams {
12+
pub mode: ExplorerQueryParamsMode,
13+
14+
pub min_timestamp: Option<u64>,
15+
pub max_timestamp: Option<u64>,
16+
pub sequence_id: Option<u64>,
17+
#[param(value_type = Option<String>)]
18+
pub tx_hash: Option<TxHash>,
19+
#[param(value_type = Option<String>)]
20+
pub chain_id: Option<ChainId>,
21+
}
22+
#[derive(Debug, serde::Serialize, serde::Deserialize)]
23+
#[serde(rename_all = "kebab-case")]
24+
pub enum ExplorerQueryParamsMode {
25+
TxHash,
26+
ChainAndSequence,
27+
ChainAndTimestamp,
28+
Timestamp,
29+
}
30+
31+
#[utoipa::path(
32+
get,
33+
path = "/v1/explorer/",
34+
responses(
35+
(status = 200, description = "Random value successfully retrieved", body = GetRandomValueResponse),
36+
(status = 403, description = "Random value cannot currently be retrieved", body = String)
37+
),
38+
params(ExplorerQueryParams)
39+
)]
40+
pub async fn get_requests(
41+
State(state): State<crate::api::ApiState>,
42+
Query(query_params): Query<ExplorerQueryParams>,
43+
) -> anyhow::Result<Json<()>, RestError> {
44+
match query_params.mode {
45+
ExplorerQueryParamsMode::TxHash => {
46+
let tx_hash = query_params.tx_hash.ok_or(RestError::BadFilterParameters("tx_hash is required when mode=tx-hash".to_string()))?;
47+
state.history.read().await.get_request_logs_by_tx_hash(tx_hash);
48+
}
49+
ExplorerQueryParamsMode::ChainAndSequence => {}
50+
ExplorerQueryParamsMode::ChainAndTimestamp => {}
51+
ExplorerQueryParamsMode::Timestamp => {}
52+
};
53+
Ok(
54+
Json(()),
55+
)
56+
}

0 commit comments

Comments
 (0)