/home/runner/work/lyquor/lyquor/node/src/pool.rs
Line | Count | Source |
1 | | use std::collections::HashMap; |
2 | | use std::future::Future; |
3 | | use std::sync::Arc; |
4 | | |
5 | | use actix::fut::future::LocalBoxActorFuture; |
6 | | use actix::prelude::*; |
7 | | use tokio::sync::RwLock; |
8 | | use tracing::instrument; |
9 | | |
10 | | use crate::image_resolver::ImageResolver; |
11 | | use crate::utils::{LVMStoreFactory, bartender::DeployInfo}; |
12 | | use lyquor_api::{ |
13 | | actor::{FetchOracleInfo, GetAddressByEd25519, GetEd25519ByAddress, GetEthContractAddr, Stop, recipient_service}, |
14 | | call::CallParams, |
15 | | }; |
16 | | use lyquor_primitives::{Address, ChainPos, LyquidID, NodeID, SequenceBackendID, encode_by_fields}; |
17 | | use lyquor_seq::{StopHostedLyquid, fco::FCO, side_effects::SideEffectStore}; |
18 | | use lyquor_vm::scheduler::{RunOptions, RunSource}; |
19 | | |
20 | | use super::{ |
21 | | Error, GetBartender, GetConsoleBuffer, GetConsoleBufferResult, GetDeployedLyquidList, GetHostedLyquidList, |
22 | | GetIdByContract, GetLatestLyquidDeploymentInfo, GetLyquid, GetLyquidByEthAddr, GetLyquidDeploymentInfo, |
23 | | GetRegisteredLyquidList, ImageRepo, ImageRepoResponse, LVM, LoadLyquid, Lyquid, LyquidConfig, LyquidNumber, |
24 | | LyquidProcess, SetPoolAddr, StopHostedLyquids, UnloadLyquid, |
25 | | }; |
26 | | |
27 | | impl Handler<GetAddressByEd25519> for LyquidPool { |
28 | | type Result = ResponseFuture<Option<Address>>; |
29 | | |
30 | | #[tracing::instrument(level = "trace")] |
31 | 0 | fn handle(&mut self, msg: GetAddressByEd25519, _ctx: &mut Context<Self>) -> Self::Result { |
32 | 0 | Box::pin(self.get_address_by_ed25519(msg.pos, msg.id)) |
33 | 0 | } |
34 | | } |
35 | | |
36 | | impl Handler<GetEd25519ByAddress> for LyquidPool { |
37 | | type Result = ResponseFuture<Option<NodeID>>; |
38 | | |
39 | | #[tracing::instrument(level = "trace")] |
40 | 0 | fn handle(&mut self, msg: GetEd25519ByAddress, _ctx: &mut Context<Self>) -> Self::Result { |
41 | 0 | Box::pin(self.get_ed25519_by_address(msg.pos, msg.address)) |
42 | 0 | } |
43 | | } |
44 | | |
45 | | impl Handler<GetEthContractAddr> for LyquidPool { |
46 | | type Result = ResponseFuture<Option<Address>>; |
47 | | |
48 | | #[tracing::instrument(level = "trace")] |
49 | 0 | fn handle(&mut self, msg: lyquor_api::actor::GetEthContractAddr, _ctx: &mut Context<Self>) -> Self::Result { |
50 | 0 | Box::pin(self.get_eth_contract_address(msg.pos, msg.id)) |
51 | 0 | } |
52 | | } |
53 | | |
54 | | impl Handler<FetchOracleInfo> for LyquidPool { |
55 | | type Result = ResponseFuture<Option<lyquor_primitives::oracle::OracleEpochInfo>>; |
56 | | |
57 | | #[tracing::instrument(level = "trace", skip(self, msg, _ctx))] |
58 | | fn handle(&mut self, msg: FetchOracleInfo, _ctx: &mut Context<Self>) -> Self::Result { |
59 | | let id = match msg.target.target { |
60 | | lyquor_primitives::oracle::OracleServiceTarget::LVM(id) => id, |
61 | | lyquor_primitives::oracle::OracleServiceTarget::EVM { .. } => return Box::pin(std::future::ready(None)), |
62 | | }; |
63 | | let lyquid = self.lyquid(&id); |
64 | 0 | Box::pin(async move { |
65 | 0 | let lyquid = lyquid?; |
66 | 0 | let ln = lyquid.get_lyquid_number(msg.pos).await.ok().flatten()?; |
67 | 0 | let call = lyquid |
68 | 0 | .instance |
69 | 0 | .call_instance_func_decoded( |
70 | 0 | ln, |
71 | 0 | CallParams::builder() |
72 | 0 | .caller(Address::ZERO) |
73 | 0 | .method("__lyquor_oracle_dest_epoch_info".into()) |
74 | 0 | .input(encode_by_fields!(topic: String = msg.topic, full_config: bool = msg.full_config).into()) |
75 | 0 | .build(), |
76 | 0 | RunOptions::new(RunSource::InstanceCall), |
77 | 0 | ) |
78 | 0 | .await; |
79 | 0 | let (epoch, config_hash, change_count, config_raw): ( |
80 | 0 | u64, |
81 | 0 | lyquor_primitives::B256, |
82 | 0 | u32, |
83 | 0 | lyquor_primitives::Bytes, |
84 | 0 | ) = call.await.ok()?; |
85 | 0 | let epoch = u32::try_from(epoch).ok()?; |
86 | 0 | let config = if config_raw.is_empty() { |
87 | 0 | None |
88 | | } else { |
89 | 0 | lyquor_primitives::decode_object(config_raw.as_ref()) |
90 | | }; |
91 | 0 | Some(lyquor_primitives::oracle::OracleEpochInfo { |
92 | 0 | epoch, |
93 | 0 | config_hash: <[u8; 32]>::from(config_hash).into(), |
94 | 0 | change_count, |
95 | 0 | config, |
96 | 0 | }) |
97 | 0 | }) |
98 | | } |
99 | | } |
100 | | |
101 | | impl Handler<GetBartender> for LyquidPool { |
102 | | type Result = Lyquid; |
103 | | |
104 | | #[tracing::instrument(level = "trace")] |
105 | 6 | fn handle(&mut self, _: GetBartender, _ctx: &mut Context<Self>) -> Self::Result { |
106 | 6 | self.bartender.clone() |
107 | 6 | } |
108 | | } |
109 | | |
110 | | impl Handler<GetLyquid> for LyquidPool { |
111 | | type Result = Option<Lyquid>; |
112 | | |
113 | | #[tracing::instrument(level = "trace")] |
114 | 0 | fn handle(&mut self, msg: GetLyquid, _ctx: &mut Context<Self>) -> Self::Result { |
115 | 0 | self.lyquid(&msg.id) |
116 | 0 | } |
117 | | } |
118 | | |
119 | | impl Handler<GetIdByContract> for LyquidPool { |
120 | | type Result = ResponseFuture<Option<LyquidID>>; |
121 | | |
122 | | #[tracing::instrument(level = "trace")] |
123 | 0 | fn handle(&mut self, msg: GetIdByContract, _ctx: &mut Context<Self>) -> Self::Result { |
124 | 0 | Box::pin(self.id_by_contract(msg.addr)) |
125 | 0 | } |
126 | | } |
127 | | |
128 | | impl Handler<GetLyquidByEthAddr> for LyquidPool { |
129 | | type Result = AtomicResponse<Self, Option<(Lyquid, LyquidNumber)>>; |
130 | | |
131 | | #[tracing::instrument(level = "trace")] |
132 | 0 | fn handle(&mut self, msg: GetLyquidByEthAddr, _ctx: &mut Context<Self>) -> Self::Result { |
133 | 0 | AtomicResponse::new(self.lyquid_by_eth_addr(msg.addr, msg.pos)) |
134 | 0 | } |
135 | | } |
136 | | |
137 | | impl Handler<GetRegisteredLyquidList> for LyquidPool { |
138 | | type Result = ResponseFuture<Option<Vec<(LyquidID, Vec<LyquidID>)>>>; |
139 | | |
140 | | #[tracing::instrument(level = "trace")] |
141 | 0 | fn handle(&mut self, _: GetRegisteredLyquidList, _ctx: &mut Context<Self>) -> Self::Result { |
142 | 0 | Box::pin(self.registered_lyquid_list()) |
143 | 0 | } |
144 | | } |
145 | | |
146 | | impl Handler<GetLyquidDeploymentInfo> for LyquidPool { |
147 | | type Result = ResponseFuture<Option<DeployInfo>>; |
148 | | |
149 | | #[tracing::instrument(level = "trace")] |
150 | 0 | fn handle(&mut self, msg: GetLyquidDeploymentInfo, _ctx: &mut Context<Self>) -> Self::Result { |
151 | 0 | Box::pin(self.lyquid_deployment_info(msg.id, msg.idx)) |
152 | 0 | } |
153 | | } |
154 | | |
155 | | impl Handler<GetLatestLyquidDeploymentInfo> for LyquidPool { |
156 | | type Result = ResponseFuture<Option<DeployInfo>>; |
157 | | |
158 | | #[tracing::instrument(level = "trace")] |
159 | 0 | fn handle(&mut self, msg: GetLatestLyquidDeploymentInfo, _ctx: &mut Context<Self>) -> Self::Result { |
160 | 0 | Box::pin(self.latest_lyquid_deployment_info(msg.id)) |
161 | 0 | } |
162 | | } |
163 | | |
164 | | impl Handler<GetHostedLyquidList> for LyquidPool { |
165 | | type Result = Vec<LyquidID>; |
166 | | |
167 | | #[tracing::instrument(level = "trace")] |
168 | 0 | fn handle(&mut self, _: GetHostedLyquidList, _ctx: &mut Context<Self>) -> Self::Result { |
169 | 0 | self.pool.keys().cloned().collect() |
170 | 0 | } |
171 | | } |
172 | | |
173 | | impl Handler<GetDeployedLyquidList> for LyquidPool { |
174 | | type Result = ResponseFuture<Vec<LyquidID>>; |
175 | | |
176 | | #[tracing::instrument(level = "trace")] |
177 | 0 | fn handle(&mut self, _: GetDeployedLyquidList, _ctx: &mut Context<Self>) -> Self::Result { |
178 | 0 | Box::pin(self.deployed_lyquid_list()) |
179 | 0 | } |
180 | | } |
181 | | |
182 | | impl Handler<ImageRepo> for LyquidPool { |
183 | | type Result = ImageRepoResponse; |
184 | | |
185 | | #[tracing::instrument(level = "trace")] |
186 | 0 | fn handle(&mut self, _: ImageRepo, _ctx: &mut Context<Self>) -> Self::Result { |
187 | 0 | ImageRepoResponse(self.setup.image_resolver.image_repo()) |
188 | 0 | } |
189 | | } |
190 | | |
191 | | impl Handler<GetConsoleBuffer> for LyquidPool { |
192 | | type Result = ResponseFuture<Option<GetConsoleBufferResult>>; |
193 | | |
194 | | #[tracing::instrument(level = "trace")] |
195 | 0 | fn handle(&mut self, msg: GetConsoleBuffer, _ctx: &mut Context<Self>) -> Self::Result { |
196 | 0 | match self.pool.get(&msg.id) { |
197 | 0 | Some(lyquid) => { |
198 | 0 | let proc = lyquid.proc.clone(); |
199 | 0 | Box::pin(async move { proc.send(msg).await.ok().flatten() }) |
200 | | } |
201 | 0 | None => Box::pin(std::future::ready(None)), |
202 | | } |
203 | 0 | } |
204 | | } |
205 | | |
206 | | impl Handler<LoadLyquid> for LyquidPool { |
207 | | type Result = AtomicResponse<Self, Result<bool, Error>>; |
208 | | |
209 | | #[tracing::instrument(level = "trace")] |
210 | 0 | fn handle(&mut self, msg: LoadLyquid, ctx: &mut Context<Self>) -> Self::Result { |
211 | 0 | let LoadLyquid { id, deps } = msg; |
212 | | |
213 | 0 | if self.pool.contains_key(&id) { |
214 | | // TODO: This does not handle live Lyquid upgrades. If an already-hosted Lyquid is |
215 | | // redeployed with a new image digest, we return early here and never prefetch that |
216 | | // image before the upgraded slot arrives. |
217 | 0 | return AtomicResponse::new(Box::pin(async move { Ok(true) }.into_actor(self))); |
218 | 0 | } |
219 | | |
220 | 0 | let deploy_info = self.latest_lyquid_deployment_info(id); |
221 | 0 | let image_resolver = self.setup.image_resolver.clone(); |
222 | 0 | let lvm = self.lvm.clone(); |
223 | 0 | let setup = self.setup.clone(); |
224 | 0 | let bartender = Some(lyquor_vm::Bartender { |
225 | 0 | get_address_by_ed25519: recipient_service(ctx.address().recipient()), |
226 | 0 | get_ed25519_by_address: recipient_service(ctx.address().recipient()), |
227 | 0 | get_eth_contract_address: recipient_service(ctx.address().recipient()), |
228 | 0 | sequence_backend_id: self.sequence_backend_id, |
229 | 0 | }); |
230 | 0 | let oracle_info = Some(recipient_service(ctx.address().recipient())); |
231 | | |
232 | 0 | AtomicResponse::new(Box::pin( |
233 | 0 | async move { |
234 | | // Non-bartender lyquids are registered before their first constructor slot, |
235 | | // so bartender metadata is available in time to prefetch the latest image here. |
236 | 0 | if let Some(deploy_info) = deploy_info.await { |
237 | 0 | if let Some(repo_hint) = deploy_info.repo_hint.as_deref() { |
238 | 0 | image_resolver.remember_repository_hint(deploy_info.image_digest, repo_hint); |
239 | 0 | } |
240 | 0 | if let Err(e) = image_resolver.ensure_local(deploy_info.image_digest).await { |
241 | 0 | tracing::debug!("Skipping lyquid image prefetch after resolution failure: {e}"); |
242 | 0 | } |
243 | 0 | } |
244 | 0 | LyquidProcess::start(id, deps, lvm, setup, bartender, oracle_info).await |
245 | 0 | } |
246 | 0 | .into_actor(self) |
247 | 0 | .map(move |result, act, ctx| { |
248 | 0 | let entry = result?; |
249 | 0 | entry.proc.do_send(SetPoolAddr { pool: ctx.address() }); |
250 | 0 | act.pool.insert(id, entry); |
251 | 0 | Ok(false) |
252 | 0 | }), |
253 | | )) |
254 | 0 | } |
255 | | } |
256 | | |
257 | | impl Handler<UnloadLyquid> for LyquidPool { |
258 | | type Result = Result<(), Error>; |
259 | | |
260 | | #[tracing::instrument(level = "trace")] |
261 | 0 | fn handle(&mut self, msg: UnloadLyquid, _ctx: &mut Context<Self>) -> Self::Result { |
262 | 0 | if msg.id == *self.bartender.id() { |
263 | 0 | return Err(Error::CannotUnloadBartender); |
264 | 0 | } |
265 | | |
266 | 0 | let Some(lyquid) = self.pool.remove(&msg.id) else { |
267 | 0 | return Err(Error::LyquidNotHosted); |
268 | | }; |
269 | 0 | lyquid.proc.do_send(Stop); |
270 | 0 | Ok(()) |
271 | 0 | } |
272 | | } |
273 | | |
274 | | impl Handler<StopHostedLyquids> for LyquidPool { |
275 | | type Result = (); |
276 | | |
277 | | #[tracing::instrument(level = "trace")] |
278 | 0 | fn handle(&mut self, msg: StopHostedLyquids, _ctx: &mut Context<Self>) -> Self::Result { |
279 | 0 | for id in msg.ids { |
280 | 0 | if let Some(lyquid) = self.pool.get(&id) { |
281 | 0 | lyquid.proc.do_send(StopHostedLyquid); |
282 | 0 | } |
283 | | } |
284 | 0 | } |
285 | | } |
286 | | |
287 | | impl Handler<Stop> for LyquidPool { |
288 | | type Result = AtomicResponse<Self, ()>; |
289 | | |
290 | | #[tracing::instrument(level = "trace")] |
291 | 6 | fn handle(&mut self, _: Stop, _ctx: &mut Context<Self>) -> Self::Result { |
292 | 6 | let procs = self |
293 | 6 | .pool |
294 | 6 | .iter() |
295 | 6 | .map(|(_, lyquid)| lyquid.proc.clone()) |
296 | 6 | .collect::<Vec<_>>(); |
297 | 6 | AtomicResponse::new(Box::pin( |
298 | 6 | async move { |
299 | 6 | for proc in procs { |
300 | 6 | proc.send(Stop).await.ok(); |
301 | | } |
302 | 6 | } |
303 | 6 | .into_actor(self) |
304 | 6 | .map(|_, _act, ctx| { |
305 | 6 | ctx.stop(); |
306 | 6 | }), |
307 | | )) |
308 | 6 | } |
309 | | } |
310 | | |
311 | | #[derive(Clone)] |
312 | | pub struct LyquidPoolSetup { |
313 | | pub fco: Addr<FCO>, |
314 | | pub store_factory: Arc<dyn LVMStoreFactory>, |
315 | | pub image_resolver: Arc<ImageResolver>, |
316 | | pub side_effect_store: Arc<SideEffectStore>, |
317 | | pub log_hub: crate::lyquid_log::LyquidLogHandle, |
318 | | pub sequence_backend_chain_id: u64, |
319 | | pub sequence_backend_bartender_addr: Address, |
320 | | pub config: LyquidConfig, |
321 | | } |
322 | | |
323 | | /// Collection of all serving Lyquids. |
324 | | pub struct LyquidPool { |
325 | | pool: HashMap<LyquidID, Lyquid>, |
326 | | lvm: Arc<RwLock<LVM>>, |
327 | | bartender: Lyquid, |
328 | | sequence_backend_id: SequenceBackendID, |
329 | | setup: LyquidPoolSetup, |
330 | | } |
331 | | |
332 | | lyquor_primitives::debug_struct_name!(LyquidPool); |
333 | | |
334 | | impl Actor for LyquidPool { |
335 | | type Context = Context<Self>; |
336 | | |
337 | | #[instrument(level = "trace", skip(ctx))] |
338 | 6 | fn started(&mut self, ctx: &mut Self::Context) { |
339 | 6 | let pool = ctx.address(); |
340 | 6 | for lyquid in self.pool.values() { |
341 | 6 | lyquid.proc.do_send(SetPoolAddr { pool: pool.clone() }); |
342 | 6 | } |
343 | 6 | } |
344 | | |
345 | | #[instrument(level = "trace", skip(_ctx))] |
346 | 6 | fn stopped(&mut self, _ctx: &mut Self::Context) {} |
347 | | } |
348 | | |
349 | | impl LyquidPool { |
350 | 6 | pub async fn new(bartender_id: LyquidID, lvm: LVM, setup: LyquidPoolSetup) -> Result<Self, Error> { |
351 | 6 | let lvm = Arc::new(RwLock::new(lvm)); |
352 | 6 | let bartender = LyquidProcess::start(bartender_id, vec![], lvm.clone(), setup.clone(), None, None).await?0 ; |
353 | 6 | let bartender_handle = bartender.clone(); |
354 | 6 | let sequence_backend_id = lyquor_primitives::sequence_backend_id( |
355 | 6 | setup.sequence_backend_chain_id, |
356 | 6 | setup.sequence_backend_bartender_addr, |
357 | | ); |
358 | 6 | let mut pool = HashMap::new(); |
359 | 6 | pool.insert(bartender_id, bartender); |
360 | | |
361 | 6 | Ok(Self { |
362 | 6 | bartender: bartender_handle, |
363 | 6 | pool, |
364 | 6 | lvm, |
365 | 6 | sequence_backend_id, |
366 | 6 | setup, |
367 | 6 | }) |
368 | 6 | } |
369 | | |
370 | 0 | fn lyquid(&self, id: &LyquidID) -> Option<Lyquid> { |
371 | 0 | self.pool.get(id).map(|l| l.clone()) |
372 | 0 | } |
373 | | |
374 | 0 | fn id_by_contract(&self, addr: Address) -> impl Future<Output = Option<LyquidID>> + 'static { |
375 | 0 | let bartender = self.bartender.instance().clone(); |
376 | 0 | async move { |
377 | | // TODO: proper caching to avoid calling bartender every time |
378 | 0 | let call = { |
379 | | // We only need to write-lock the instance shortly to generate the async call task. Do |
380 | | // not make await the call right away because we don't need to hold the Instance lock |
381 | | // to execute the call. |
382 | 0 | let bar_ln = bartender.latest_number().await; |
383 | 0 | bartender |
384 | 0 | .call_instance_func_decoded( |
385 | 0 | bar_ln, |
386 | 0 | CallParams::builder() |
387 | 0 | .caller(Address::ZERO) |
388 | 0 | .method("get_lyquid_id_by_eth_addr".into()) |
389 | 0 | .input(encode_by_fields!(addr: Address = addr).into()) |
390 | 0 | .build(), |
391 | 0 | RunOptions::new(RunSource::Bartender), |
392 | 0 | ) |
393 | 0 | .await |
394 | | }; |
395 | 0 | let id: Option<LyquidID> = call.await.ok()?; |
396 | 0 | id |
397 | 0 | } |
398 | 0 | } |
399 | | |
400 | 0 | fn lyquid_by_eth_addr( |
401 | 0 | &mut self, addr: Address, pos: ChainPos, |
402 | 0 | ) -> LocalBoxActorFuture<Self, Option<(Lyquid, LyquidNumber)>> { |
403 | 0 | let bartender = self.bartender.clone(); |
404 | | // TODO: proper caching to avoid calling bartender every time |
405 | 0 | Box::pin(self.id_by_contract(addr).into_actor(self).then(move |id, act, _ctx| { |
406 | 0 | let lyquid = id.and_then(|id| act.lyquid(&id)); |
407 | 0 | async move { |
408 | 0 | let lyquid = lyquid?; |
409 | | // Now we should check if such contract address is valid given the chain position. Because |
410 | | // the contract could be superseded by a later contract for the same lyquid, if the |
411 | | // contract address at the given chain position does not match the address in effect, we |
412 | | // should not regard this as a valid action. |
413 | 0 | let ln = lyquid.get_lyquid_number(pos).await.ok()??; |
414 | 0 | let contract = Self::bartender_get_eth_addr(bartender, pos, lyquid.id, ln.image).await; |
415 | 0 | if contract? == addr { Some((lyquid, ln)) } else { None } |
416 | 0 | } |
417 | 0 | .into_actor(act) |
418 | 0 | })) |
419 | 0 | } |
420 | | |
421 | 0 | fn registered_lyquid_list(&self) -> impl Future<Output = Option<Vec<(LyquidID, Vec<LyquidID>)>>> + 'static { |
422 | 0 | let bartender = self.bartender.instance().clone(); |
423 | 0 | async move { |
424 | 0 | let call = { |
425 | | // We only need to write-lock the instance shortly to generate the async call task. Do |
426 | | // not make await the call right away because we don't need to hold the Instance lock |
427 | | // to execute the call. |
428 | 0 | let bar_ln = bartender.latest_number().await; |
429 | | // TODO: get_lyquid_list_with_deps() is not scalable even if the node wants to subscribe to all |
430 | | // lyquids. We need to change it to an iterator style invcation. |
431 | 0 | bartender |
432 | 0 | .call_instance_func_decoded( |
433 | 0 | bar_ln, |
434 | 0 | CallParams::builder() |
435 | 0 | .caller(Address::ZERO) |
436 | 0 | .method("get_lyquid_list_with_deps".into()) |
437 | 0 | .input(encode_by_fields!().into()) |
438 | 0 | .build(), |
439 | 0 | RunOptions::new(RunSource::Bartender), |
440 | 0 | ) |
441 | 0 | .await |
442 | | }; |
443 | 0 | let result: Vec<(LyquidID, Vec<LyquidID>)> = call.await.ok()?; |
444 | | // NOTE: we need to explicitly declare result for correct decoding |
445 | 0 | Some(result) |
446 | 0 | } |
447 | 0 | } |
448 | | |
449 | 0 | fn lyquid_deployment_info(&self, id: LyquidID, nth: u32) -> impl Future<Output = Option<DeployInfo>> + 'static { |
450 | 0 | let bartender = self.bartender.instance().clone(); |
451 | 0 | async move { |
452 | 0 | let call = { |
453 | | // We only need to write-lock the instance shortly to generate the async call task. Do |
454 | | // not make await the call right away because we don't need to hold the Instance lock |
455 | | // to execute the call. |
456 | 0 | let bar_ln = bartender.latest_number().await; |
457 | | // TODO: get_lyquid_list() is not scalable even if the node wants to subscribe to all |
458 | | // lyquids. We need to change it to an iterator style invcation. |
459 | 0 | bartender |
460 | 0 | .call_instance_func_decoded( |
461 | 0 | bar_ln, |
462 | 0 | CallParams::builder() |
463 | 0 | .caller(Address::ZERO) |
464 | 0 | .method("get_lyquid_deployment_info".into()) |
465 | 0 | .input(encode_by_fields!(id: LyquidID = id, nth: u32 = nth).into()) |
466 | 0 | .build(), |
467 | 0 | RunOptions::new(RunSource::Bartender), |
468 | 0 | ) |
469 | 0 | .await |
470 | | }; |
471 | 0 | let result: Option<DeployInfo> = call.await.ok()?; |
472 | | // NOTE: we need to explicitly declare result for correct decoding |
473 | 0 | result |
474 | 0 | } |
475 | 0 | } |
476 | | |
477 | 0 | fn latest_lyquid_deployment_info(&self, id: LyquidID) -> impl Future<Output = Option<DeployInfo>> + 'static { |
478 | 0 | let bartender = self.bartender.instance().clone(); |
479 | 0 | async move { |
480 | 0 | let call = { |
481 | 0 | let bar_ln = bartender.latest_number().await; |
482 | 0 | bartender |
483 | 0 | .call_instance_func_decoded( |
484 | 0 | bar_ln, |
485 | 0 | CallParams::builder() |
486 | 0 | .caller(Address::ZERO) |
487 | 0 | .method("get_last_lyquid_deployment_info".into()) |
488 | 0 | .input(encode_by_fields!(id: LyquidID = id).into()) |
489 | 0 | .build(), |
490 | 0 | RunOptions::new(RunSource::Bartender), |
491 | 0 | ) |
492 | 0 | .await |
493 | | }; |
494 | 0 | let result: Option<DeployInfo> = call.await.ok()?; |
495 | 0 | result |
496 | 0 | } |
497 | 0 | } |
498 | | |
499 | 0 | fn deployed_lyquid_list(&self) -> impl Future<Output = Vec<LyquidID>> + 'static { |
500 | 0 | let bartender = self.bartender.instance().clone(); |
501 | | |
502 | 0 | async move { |
503 | 0 | let call = { |
504 | | // We only need to write-lock the instance shortly to generate the async call task. Do |
505 | | // not make await the call right away because we don't need to hold the Instance lock |
506 | | // to execute the call. |
507 | 0 | let bar_ln = bartender.latest_number().await; |
508 | 0 | bartender |
509 | 0 | .call_instance_func_decoded( |
510 | 0 | bar_ln, |
511 | 0 | CallParams::builder() |
512 | 0 | .caller(Address::ZERO) |
513 | 0 | .method("get_lyquid_list".into()) |
514 | 0 | .input(encode_by_fields!().into()) |
515 | 0 | .build(), |
516 | 0 | RunOptions::new(RunSource::Bartender), |
517 | 0 | ) |
518 | 0 | .await |
519 | | }; |
520 | 0 | let resp: Option<Vec<LyquidID>> = call.await.ok(); |
521 | 0 | resp.unwrap_or_default() |
522 | 0 | } |
523 | 0 | } |
524 | | |
525 | 0 | fn get_eth_contract_address(&self, pos: ChainPos, id: LyquidID) -> impl Future<Output = Option<Address>> + 'static { |
526 | 0 | let lyquid = self.lyquid(&id); |
527 | 0 | let bartender = self.bartender.clone(); |
528 | 0 | async move { |
529 | 0 | let lyquid = lyquid?; |
530 | 0 | let ln = lyquid.get_lyquid_number(pos).await.ok().flatten()?; |
531 | 0 | Self::bartender_get_eth_addr(bartender, pos, id, ln.image).await |
532 | 0 | } |
533 | 0 | } |
534 | | |
535 | 0 | async fn bartender_get_eth_addr(bartender: Lyquid, pos: ChainPos, id: LyquidID, ln_image: u32) -> Option<Address> { |
536 | 0 | let call = { |
537 | 0 | let bar_ln = bartender.get_lyquid_number(pos).await.ok().flatten()?; |
538 | 0 | bartender |
539 | 0 | .instance |
540 | 0 | .call_instance_func_decoded( |
541 | 0 | bar_ln, |
542 | 0 | CallParams::builder() |
543 | 0 | .caller(Address::ZERO) |
544 | 0 | .method("get_eth_addr".into()) |
545 | 0 | .input(encode_by_fields!(id: LyquidID = id, ln_image: u32 = ln_image).into()) |
546 | 0 | .build(), |
547 | 0 | RunOptions::new(RunSource::Bartender), |
548 | 0 | ) |
549 | 0 | .await |
550 | | }; |
551 | 0 | let result: Option<Address> = call.await.ok()?; |
552 | 0 | result |
553 | 0 | } |
554 | | |
555 | 0 | fn get_address_by_ed25519(&self, pos: ChainPos, id: NodeID) -> impl Future<Output = Option<Address>> + 'static { |
556 | 0 | let bartender = self.bartender.clone(); |
557 | 0 | async move { |
558 | 0 | let call = { |
559 | 0 | let bar_ln = bartender.get_lyquid_number(pos).await.ok().flatten()?; |
560 | 0 | bartender |
561 | 0 | .instance |
562 | 0 | .call_instance_func_decoded( |
563 | 0 | bar_ln, |
564 | 0 | CallParams::builder() |
565 | 0 | .caller(Address::ZERO) |
566 | 0 | .method("get_address_by_ed25519".into()) |
567 | 0 | .input(encode_by_fields!(id: NodeID).into()) |
568 | 0 | .build(), |
569 | 0 | RunOptions::new(RunSource::Bartender), |
570 | 0 | ) |
571 | 0 | .await |
572 | | }; |
573 | 0 | let result: Option<Address> = call.await.ok()?; |
574 | 0 | result |
575 | 0 | } |
576 | 0 | } |
577 | | |
578 | 0 | fn get_ed25519_by_address( |
579 | 0 | &self, pos: ChainPos, address: Address, |
580 | 0 | ) -> impl Future<Output = Option<NodeID>> + 'static { |
581 | 0 | let bartender = self.bartender.clone(); |
582 | 0 | async move { |
583 | 0 | let call = { |
584 | 0 | let bar_ln = bartender.get_lyquid_number(pos).await.ok().flatten()?; |
585 | 0 | bartender |
586 | 0 | .instance |
587 | 0 | .call_instance_func_decoded( |
588 | 0 | bar_ln, |
589 | 0 | CallParams::builder() |
590 | 0 | .caller(Address::ZERO) |
591 | 0 | .method("get_ed25519_by_address".into()) |
592 | 0 | .input(encode_by_fields!(address: Address).into()) |
593 | 0 | .build(), |
594 | 0 | RunOptions::new(RunSource::Bartender), |
595 | 0 | ) |
596 | 0 | .await |
597 | | }; |
598 | 0 | let result: Option<NodeID> = call.await.ok()?; |
599 | 0 | result |
600 | 0 | } |
601 | 0 | } |
602 | | } |