Coverage Report

Created: 2026-04-10 00:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}