fuel_core_shared_sequencer/
service.rs1use crate::{
4 Client,
5 Config,
6 http_api::AccountMetadata,
7 ports::{
8 BlocksProvider,
9 Signer,
10 },
11};
12use async_trait::async_trait;
13use core::time::Duration;
14use fuel_core_services::{
15 EmptyShared,
16 RunnableService,
17 RunnableTask,
18 ServiceRunner,
19 StateWatcher,
20 TaskNextAction,
21 stream::BoxStream,
22};
23use fuel_core_types::services::{
24 block_importer::SharedImportResult,
25 shared_sequencer::{
26 SSBlob,
27 SSBlobs,
28 },
29};
30use futures::StreamExt;
31use std::sync::Arc;
32
33pub struct NonInitializedTask<S> {
35 config: Config,
36 signer: Arc<S>,
37 blocks_events: BoxStream<SharedImportResult>,
38}
39
40pub struct Task<S> {
42 shared_sequencer_client: Option<Client>,
44 config: Config,
45 signer: Arc<S>,
46 account_metadata: Option<AccountMetadata>,
47 prev_order: Option<u64>,
48 blobs: Arc<tokio::sync::Mutex<SSBlobs>>,
49 interval: tokio::time::Interval,
50}
51
52impl<S> NonInitializedTask<S> {
53 fn new(
55 config: Config,
56 blocks_events: BoxStream<SharedImportResult>,
57 signer: Arc<S>,
58 ) -> anyhow::Result<Self> {
59 if config.enabled && config.endpoints.is_none() {
60 return Err(anyhow::anyhow!(
61 "Shared sequencer is enabled but no endpoints are set"
62 ));
63 }
64
65 Ok(Self {
66 config,
67 blocks_events,
68 signer,
69 })
70 }
71}
72
73#[async_trait]
74impl<S> RunnableService for NonInitializedTask<S>
75where
76 S: Signer + 'static,
77{
78 const NAME: &'static str = "SharedSequencer";
79
80 type SharedData = EmptyShared;
81 type Task = Task<S>;
82 type TaskParams = ();
83
84 fn shared_data(&self) -> Self::SharedData {
85 EmptyShared
86 }
87
88 async fn into_task(
89 mut self,
90 state: &StateWatcher,
91 _: Self::TaskParams,
92 ) -> anyhow::Result<Self::Task> {
93 let shared_sequencer_client = match &self.config.endpoints {
94 Some(endpoints) => {
95 let mut state = state.clone();
96 let init = Client::new(
97 endpoints.clone(),
98 self.config.topic,
99 self.config.http_request_timeout,
100 self.config.http_connect_timeout,
101 );
102
103 let ss = tokio::select! {
104 biased;
105 _ = state.wait_stopping_or_stopped() => {
106 return Err(anyhow::anyhow!(
107 "Shutdown requested during SharedSequencer initialization"
108 ));
109 }
110 res = init => res?,
111 };
112
113 if self.signer.is_available() {
114 let cosmos_public_address =
115 ss.sender_account_id(self.signer.as_ref())?;
116
117 tracing::info!(
118 "Shared sequencer uses account ID: {}",
119 cosmos_public_address
120 );
121 }
122
123 Some(ss)
124 }
125 _ => None,
126 };
127
128 let blobs = Arc::new(tokio::sync::Mutex::new(SSBlobs::new()));
129
130 if self.config.enabled {
131 let mut block_events = self.blocks_events;
132
133 tokio::task::spawn({
134 let blobs = blobs.clone();
135 async move {
136 while let Some(block) = block_events.next().await {
137 let blob = SSBlob {
138 block_height: *block.sealed_block.entity.header().height(),
139 block_id: block.sealed_block.entity.id(),
140 };
141 blobs.lock().await.push(blob);
142 }
143 }
144 });
145 }
146
147 Ok(Task {
148 interval: tokio::time::interval(self.config.block_posting_frequency),
149 shared_sequencer_client,
150 config: self.config,
151 signer: self.signer,
152 account_metadata: None,
153 prev_order: None,
154 blobs,
155 })
156 }
157}
158
159impl<S> Task<S>
160where
161 S: Signer,
162{
163 async fn ensure_account_metadata(&mut self) -> anyhow::Result<()> {
165 if self.account_metadata.is_some() {
166 return Ok(());
167 }
168 let ss = self
169 .shared_sequencer_client
170 .as_ref()
171 .expect("Shared sequencer client is not set");
172 self.account_metadata = Some(ss.get_account_meta(self.signer.as_ref()).await?);
173 Ok(())
174 }
175
176 async fn ensure_prev_order(&mut self) -> anyhow::Result<()> {
178 if self.prev_order.is_some() {
179 return Ok(());
180 }
181 let ss = self
182 .shared_sequencer_client
183 .as_ref()
184 .expect("Shared sequencer client is not set");
185 self.prev_order = ss.get_topic().await?.map(|f| f.order);
186 Ok(())
187 }
188}
189
190impl<S> RunnableTask for Task<S>
191where
192 S: Signer + 'static,
193{
194 async fn run(&mut self, watcher: &mut StateWatcher) -> TaskNextAction {
195 if !self.config.enabled {
196 let _ = watcher.while_started().await;
197 return TaskNextAction::Stop
198 }
199
200 tokio::select! {
201 biased;
202 _ = watcher.while_started() => return TaskNextAction::Stop,
203 res = self.ensure_account_metadata() => {
204 if let Err(err) = res {
205 tokio::select! {
209 biased;
210 _ = watcher.while_started() => return TaskNextAction::Stop,
211 _ = tokio::time::sleep(Duration::from_secs(1)) => {}
212 }
213 return TaskNextAction::ErrorContinue(err)
214 }
215 }
216 }
217
218 tokio::select! {
219 biased;
220 _ = watcher.while_started() => return TaskNextAction::Stop,
221 res = self.ensure_prev_order() => {
222 if let Err(err) = res {
223 return TaskNextAction::ErrorContinue(err)
224 }
225 }
226 }
227
228 tokio::select! {
229 biased;
230 _ = watcher.while_started() => {
231 TaskNextAction::Stop
232 },
233 _ = self.interval.tick() => {
234 let blobs = {
235 let mut lock = self.blobs.lock().await;
236 core::mem::take(&mut *lock)
237 };
238 if blobs.is_empty() {
239 tokio::select! {
240 biased;
241 _ = watcher.while_started() => return TaskNextAction::Stop,
242 _ = tokio::time::sleep(Duration::from_secs(1)) => {}
243 }
244 return TaskNextAction::Continue;
245 };
246
247 let mut account = self.account_metadata.take().expect("Account metadata is not set");
248 let next_order = self.prev_order.map(|prev| prev.wrapping_add(1)).unwrap_or(0);
249 let ss = self.shared_sequencer_client
250 .as_ref().expect("Shared sequencer client is not set");
251 let blobs_bytes = postcard::to_allocvec(&blobs).expect("Failed to serialize SSBlob");
252
253 let send = ss.send(self.signer.as_ref(), account, next_order, blobs_bytes);
254 tokio::select! {
255 biased;
256 _ = watcher.while_started() => return TaskNextAction::Stop,
257 res = send => {
258 if let Err(err) = res {
259 return TaskNextAction::ErrorContinue(err);
260 }
261 }
262 }
263
264 tracing::info!("Posted block to shared sequencer {blobs:?}");
265 account.sequence = account.sequence.saturating_add(1);
266 self.prev_order = Some(next_order);
267 self.account_metadata = Some(account);
268 TaskNextAction::Continue
269 },
270 }
271 }
272
273 async fn shutdown(self) -> anyhow::Result<()> {
274 Ok(())
277 }
278}
279
280pub fn new_service<B, S>(
282 block_provider: B,
283 config: Config,
284 signer: Arc<S>,
285) -> anyhow::Result<ServiceRunner<NonInitializedTask<S>>>
286where
287 B: BlocksProvider,
288 S: Signer,
289{
290 let blocks_events = block_provider.subscribe();
291 Ok(ServiceRunner::new(NonInitializedTask::new(
292 config,
293 blocks_events,
294 signer,
295 )?))
296}