Skip to content

Commit 38e6519

Browse files
rafal-chxgreenx
andauthored
Add GraphQL extension to provide current STF and CP versions (#2715)
## Linked Issues/PRs Closes #2596 ## Description This PR adds new GraphQL extensions to provide: - current STF version - current consensus parameters version Example response: ``` { "data": { "nodeInfo": { "nodeVersion": "0.41.6" } }, "extensions": { "current_consensus_parameters_version": 0, "current_fuel_block_height": 0, "current_stf_version": 21 } } ``` ### Additional housekeeping changes: - all graphql extensions have been moved into a subdirectory ## TODO - [x] Add tests to check that correct versions are returned when STF or CP are bumped - [x] Rework how we retrieve the current STF version ## Checklist - [x] Breaking changes are clearly marked as such in the PR description and changelog - [x] New behavior is reflected in tests ### Before requesting review - [x] I have reviewed the code myself - [ ] I have created follow-up issues caused by this PR and linked them here ### After merging, notify other teams - [X] [Rust SDK](https://github.com/FuelLabs/fuels-rs/) --------- Co-authored-by: green <[email protected]>
1 parent 50e9657 commit 38e6519

35 files changed

+696
-187
lines changed

.changes/changed/2715.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Each GraphQL response contains `current_consensus_parameters_version` and `current_stf_version` in the `extensions` section.

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ codegen-units = 1
4747
lto = "fat"
4848
# The difference in performance for "fat" and "thin" is small,
4949
# but "thin" LTO is much faster to compile.
50-
# If you play with benchamrks or flamegraph, it is better to use "thin"
51-
# To speedup iterations between compialtion.
50+
# If you play with benchmarks or flamegraphs, it is better to use "thin"
51+
# To speedup iterations between compilation.
5252
#lto = "thin"
5353
panic = "unwind"
5454

crates/client/src/client.rs

Lines changed: 107 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ use cynic::{
5252
QueryBuilder,
5353
};
5454
use fuel_core_types::{
55+
blockchain::header::{
56+
ConsensusParametersVersion,
57+
StateTransitionBytecodeVersion,
58+
},
5559
fuel_asm::{
5660
Instruction,
5761
Word,
@@ -210,13 +214,36 @@ impl Clone for ConsistencyPolicy {
210214
}
211215
}
212216

217+
#[derive(Debug, Default)]
218+
struct ChainStateInfo {
219+
current_stf_version: Arc<Mutex<Option<StateTransitionBytecodeVersion>>>,
220+
current_consensus_parameters_version: Arc<Mutex<Option<ConsensusParametersVersion>>>,
221+
}
222+
223+
impl Clone for ChainStateInfo {
224+
fn clone(&self) -> Self {
225+
Self {
226+
current_stf_version: Arc::new(Mutex::new(
227+
self.current_stf_version.lock().ok().and_then(|v| *v),
228+
)),
229+
current_consensus_parameters_version: Arc::new(Mutex::new(
230+
self.current_consensus_parameters_version
231+
.lock()
232+
.ok()
233+
.and_then(|v| *v),
234+
)),
235+
}
236+
}
237+
}
238+
213239
#[derive(Debug, Clone)]
214240
pub struct FuelClient {
215241
client: reqwest::Client,
216242
#[cfg(feature = "subscriptions")]
217243
cookie: std::sync::Arc<reqwest::cookie::Jar>,
218244
url: reqwest::Url,
219245
require_height: ConsistencyPolicy,
246+
chain_state_info: ChainStateInfo,
220247
}
221248

222249
impl FromStr for FuelClient {
@@ -247,6 +274,7 @@ impl FromStr for FuelClient {
247274
require_height: ConsistencyPolicy::Auto {
248275
height: Arc::new(Mutex::new(None)),
249276
},
277+
chain_state_info: Default::default(),
250278
})
251279
}
252280

@@ -259,6 +287,7 @@ impl FromStr for FuelClient {
259287
require_height: ConsistencyPolicy::Auto {
260288
height: Arc::new(Mutex::new(None)),
261289
},
290+
chain_state_info: Default::default(),
262291
})
263292
}
264293
}
@@ -322,6 +351,51 @@ impl FuelClient {
322351
}
323352
}
324353

354+
fn update_chain_state_info<R, E>(&self, response: &FuelGraphQlResponse<R, E>) {
355+
if let Some(current_sft_version) = response
356+
.extensions
357+
.as_ref()
358+
.and_then(|e| e.current_stf_version)
359+
{
360+
if let Ok(mut c) = self.chain_state_info.current_stf_version.lock() {
361+
*c = Some(current_sft_version);
362+
}
363+
}
364+
365+
if let Some(current_consensus_parameters_version) = response
366+
.extensions
367+
.as_ref()
368+
.and_then(|e| e.current_consensus_parameters_version)
369+
{
370+
if let Ok(mut c) = self
371+
.chain_state_info
372+
.current_consensus_parameters_version
373+
.lock()
374+
{
375+
*c = Some(current_consensus_parameters_version);
376+
}
377+
}
378+
379+
let inner_required_height = match &self.require_height {
380+
ConsistencyPolicy::Auto { height } => Some(height.clone()),
381+
ConsistencyPolicy::Manual { .. } => None,
382+
};
383+
384+
if let Some(inner_required_height) = inner_required_height {
385+
if let Some(current_fuel_block_height) = response
386+
.extensions
387+
.as_ref()
388+
.and_then(|e| e.current_fuel_block_height)
389+
{
390+
let mut lock = inner_required_height.lock().expect("Mutex poisoned");
391+
392+
if current_fuel_block_height >= lock.unwrap_or_default() {
393+
*lock = Some(current_fuel_block_height);
394+
}
395+
}
396+
}
397+
}
398+
325399
/// Send the GraphQL query to the client.
326400
pub async fn query<ResponseData, Vars>(
327401
&self,
@@ -340,34 +414,14 @@ impl FuelClient {
340414
.await
341415
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
342416

343-
let inner_required_height = match &self.require_height {
344-
ConsistencyPolicy::Auto { height } => Some(height.clone()),
345-
_ => None,
346-
};
347-
348-
Self::decode_response(response, inner_required_height)
417+
self.decode_response(response)
349418
}
350419

351-
fn decode_response<R, E>(
352-
response: FuelGraphQlResponse<R, E>,
353-
inner_required_height: Option<Arc<Mutex<Option<BlockHeight>>>>,
354-
) -> io::Result<R>
420+
fn decode_response<R, E>(&self, response: FuelGraphQlResponse<R, E>) -> io::Result<R>
355421
where
356422
R: serde::de::DeserializeOwned + 'static,
357423
{
358-
if let Some(inner_required_height) = inner_required_height {
359-
if let Some(current_fuel_block_height) = response
360-
.extensions
361-
.as_ref()
362-
.and_then(|e| e.current_fuel_block_height)
363-
{
364-
let mut lock = inner_required_height.lock().expect("Mutex poisoned");
365-
366-
if current_fuel_block_height >= lock.unwrap_or_default() {
367-
*lock = Some(current_fuel_block_height);
368-
}
369-
}
370-
}
424+
self.update_chain_state_info(&response);
371425

372426
if let Some(failed) = response
373427
.extensions
@@ -398,7 +452,7 @@ impl FuelClient {
398452
async fn subscribe<ResponseData, Vars>(
399453
&self,
400454
q: StreamingOperation<ResponseData, Vars>,
401-
) -> io::Result<impl futures::Stream<Item = io::Result<ResponseData>>>
455+
) -> io::Result<impl futures::Stream<Item = io::Result<ResponseData>> + '_>
402456
where
403457
Vars: serde::Serialize,
404458
ResponseData: serde::de::DeserializeOwned + 'static,
@@ -471,25 +525,19 @@ impl FuelClient {
471525

472526
let mut last = None;
473527

474-
let inner_required_height = match &self.require_height {
475-
ConsistencyPolicy::Auto { height } => Some(height.clone()),
476-
_ => None,
477-
};
478-
479528
let stream = es::Client::stream(&client)
480-
.zip(futures::stream::repeat(inner_required_height))
481-
.take_while(|(result, _)| {
529+
.take_while(|result| {
482530
futures::future::ready(!matches!(result, Err(es::Error::Eof)))
483531
})
484-
.filter_map(move |(result, inner_required_height)| {
532+
.filter_map(move |result| {
485533
tracing::debug!("Got result: {result:?}");
486534
let r = match result {
487535
Ok(es::SSE::Event(es::Event { data, .. })) => {
488536
match serde_json::from_str::<FuelGraphQlResponse<ResponseData>>(
489537
&data,
490538
) {
491539
Ok(resp) => {
492-
match Self::decode_response(resp, inner_required_height) {
540+
match self.decode_response(resp) {
493541
Ok(resp) => {
494542
match last.replace(data) {
495543
// Remove duplicates
@@ -527,6 +575,24 @@ impl FuelClient {
527575
Ok(stream)
528576
}
529577

578+
pub fn latest_stf_version(&self) -> Option<StateTransitionBytecodeVersion> {
579+
self.chain_state_info
580+
.current_stf_version
581+
.lock()
582+
.ok()
583+
.and_then(|value| *value)
584+
}
585+
586+
pub fn latest_consensus_parameters_version(
587+
&self,
588+
) -> Option<ConsensusParametersVersion> {
589+
self.chain_state_info
590+
.current_consensus_parameters_version
591+
.lock()
592+
.ok()
593+
.and_then(|value| *value)
594+
}
595+
530596
pub async fn health(&self) -> io::Result<bool> {
531597
let query = schema::Health::build(());
532598
self.query(query).await.map(|r| r.health)
@@ -764,10 +830,10 @@ impl FuelClient {
764830
/// Compared to the `submit_and_await_commit`, the stream also contains
765831
/// `SubmittedStatus` as an intermediate state.
766832
#[cfg(feature = "subscriptions")]
767-
pub async fn submit_and_await_status(
768-
&self,
769-
tx: &Transaction,
770-
) -> io::Result<impl Stream<Item = io::Result<TransactionStatus>>> {
833+
pub async fn submit_and_await_status<'a>(
834+
&'a self,
835+
tx: &'a Transaction,
836+
) -> io::Result<impl Stream<Item = io::Result<TransactionStatus>> + 'a> {
771837
use cynic::SubscriptionBuilder;
772838
let tx = tx.clone().to_bytes();
773839
let s = schema::tx::SubmitAndAwaitStatusSubscription::build(TxArg {
@@ -926,10 +992,10 @@ impl FuelClient {
926992
#[tracing::instrument(skip(self), level = "debug")]
927993
#[cfg(feature = "subscriptions")]
928994
/// Subscribe to the status of a transaction
929-
pub async fn subscribe_transaction_status(
930-
&self,
931-
id: &TxId,
932-
) -> io::Result<impl futures::Stream<Item = io::Result<TransactionStatus>>> {
995+
pub async fn subscribe_transaction_status<'a>(
996+
&'a self,
997+
id: &'a TxId,
998+
) -> io::Result<impl futures::Stream<Item = io::Result<TransactionStatus>> + 'a> {
933999
use cynic::SubscriptionBuilder;
9341000
let tx_id: TransactionId = (*id).into();
9351001
let s = schema::tx::StatusChangeSubscription::build(TxIdArgs { id: tx_id });

crates/client/src/reqwest_ext.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ use cynic::{
33
GraphQlResponse,
44
Operation,
55
};
6-
use fuel_core_types::fuel_types::BlockHeight;
6+
use fuel_core_types::{
7+
blockchain::header::{
8+
ConsensusParametersVersion,
9+
StateTransitionBytecodeVersion,
10+
},
11+
fuel_types::BlockHeight,
12+
};
713
use std::{
814
future::Future,
915
marker::PhantomData,
@@ -20,6 +26,8 @@ pub struct ExtensionsResponse {
2026
pub required_fuel_block_height: Option<BlockHeight>,
2127
pub current_fuel_block_height: Option<BlockHeight>,
2228
pub fuel_block_height_precondition_failed: Option<bool>,
29+
pub current_stf_version: Option<StateTransitionBytecodeVersion>,
30+
pub current_consensus_parameters_version: Option<ConsensusParametersVersion>,
2331
}
2432

2533
#[derive(Debug, serde::Serialize)]

crates/fuel-core/src/graphql_api.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,10 @@ pub mod api_service;
1212
pub(crate) mod block_height_subscription;
1313
pub mod da_compression;
1414
pub mod database;
15+
pub(crate) mod extensions;
1516
pub(crate) mod indexation;
16-
pub(crate) mod metrics_extension;
1717
pub mod ports;
18-
pub(crate) mod required_fuel_block_height_extension;
1918
pub mod storage;
20-
pub(crate) mod validation_extension;
2119
pub mod worker_service;
2220

2321
#[derive(Clone, Debug)]

crates/fuel-core/src/graphql_api/api_service.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::{
22
fuel_core_graphql_api::{
3-
metrics_extension::MetricsExtension,
43
ports::{
54
BlockProducerPort,
65
ConsensusModulePort,
@@ -11,12 +10,16 @@ use crate::{
1110
P2pPort,
1211
TxPoolPort,
1312
},
14-
validation_extension::ValidationExtension,
1513
Config,
1614
},
1715
graphql_api::{
1816
self,
19-
required_fuel_block_height_extension::RequiredFuelBlockHeightExtension,
17+
extensions::{
18+
chain_state_info::ChainStateInfoExtension,
19+
metrics::MetricsExtension,
20+
required_fuel_block_height::RequiredFuelBlockHeightExtension,
21+
validation::ValidationExtension,
22+
},
2023
},
2124
schema::{
2225
CoreSchema,
@@ -294,8 +297,9 @@ where
294297
.extension(RequiredFuelBlockHeightExtension::new(
295298
required_fuel_block_height_tolerance,
296299
required_fuel_block_height_timeout,
297-
block_height_subscriber,
300+
block_height_subscriber.clone(),
298301
))
302+
.extension(ChainStateInfoExtension::new(block_height_subscriber))
299303
.finish();
300304

301305
let graphql_endpoint = "/v1/graphql";

crates/fuel-core/src/graphql_api/block_height_subscription.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ impl Handler {
3333
// get all sending endpoint corresponding to subscribers that are waiting for a block height
3434
// that is at most `block_height`.
3535
let to_notify = inner_map.tx_handles.split_off(&Reverse(block_height));
36-
inner_map.latest_seen_block_height = block_height;
36+
inner_map.current_block_height = block_height;
3737

3838
to_notify.into_values().flatten()
3939
};
@@ -59,7 +59,7 @@ impl Subscriber {
5959
let future = {
6060
let mut inner_map = self.inner.write();
6161

62-
if inner_map.latest_seen_block_height >= block_height {
62+
if inner_map.current_block_height >= block_height {
6363
return Ok(());
6464
}
6565

@@ -77,22 +77,22 @@ impl Subscriber {
7777
})
7878
}
7979

80-
pub fn latest_seen_block_height(&self) -> BlockHeight {
81-
self.inner.read().latest_seen_block_height
80+
pub fn current_block_height(&self) -> BlockHeight {
81+
self.inner.read().current_block_height
8282
}
8383
}
8484

8585
#[derive(Debug, Default)]
8686
struct HandlersMapInner {
8787
tx_handles: BTreeMap<Reverse<BlockHeight>, Vec<oneshot::Sender<()>>>,
88-
latest_seen_block_height: BlockHeight,
88+
current_block_height: BlockHeight,
8989
}
9090

9191
impl HandlersMapInner {
92-
fn new(latest_seen_block_height: BlockHeight) -> Self {
92+
fn new(current_block_height: BlockHeight) -> Self {
9393
Self {
9494
tx_handles: BTreeMap::new(),
95-
latest_seen_block_height,
95+
current_block_height,
9696
}
9797
}
9898
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pub(crate) mod chain_state_info;
2+
pub(crate) mod metrics;
3+
pub(crate) mod required_fuel_block_height;
4+
pub(crate) mod validation;

0 commit comments

Comments
 (0)