iota_bridge/server/
mod.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5#![allow(clippy::inconsistent_digit_grouping)]
6use std::{net::SocketAddr, str::FromStr, sync::Arc};
7
8use axum::{
9    Json, Router,
10    extract::{Path, State},
11    http::StatusCode,
12    routing::get,
13};
14use ethers::types::Address as EthAddress;
15use fastcrypto::{
16    encoding::{Encoding, Hex},
17    traits::ToFromBytes,
18};
19use iota_types::{TypeTag, bridge::BridgeChainId};
20use tracing::{info, instrument};
21
22use crate::{
23    crypto::BridgeAuthorityPublicKeyBytes,
24    error::BridgeError,
25    metrics::BridgeMetrics,
26    server::handler::{BridgeRequestHandler, BridgeRequestHandlerTrait},
27    types::{
28        AddTokensOnEvmAction, AddTokensOnIotaAction, AssetPriceUpdateAction,
29        BlocklistCommitteeAction, BlocklistType, BridgeAction, EmergencyAction,
30        EmergencyActionType, EvmContractUpgradeAction, LimitUpdateAction, SignedBridgeAction,
31    },
32    with_metrics,
33};
34
35pub mod governance_verifier;
36pub mod handler;
37
38#[cfg(test)]
39pub(crate) mod mock_handler;
40
41pub const APPLICATION_JSON: &str = "application/json";
42
43pub const PING_PATH: &str = "/ping";
44
45// Important: for BridgeActions, the paths need to match the ones in
46// bridge_client.rs
47pub const ETH_TO_IOTA_TX_PATH: &str = "/sign/bridge_tx/eth/iota/{tx_hash}/{event_index}";
48pub const IOTA_TO_ETH_TX_PATH: &str = "/sign/bridge_tx/iota/eth/{tx_digest}/{event_index}";
49pub const COMMITTEE_BLOCKLIST_UPDATE_PATH: &str =
50    "/sign/update_committee_blocklist/{chain_id}/{nonce}/{type}/{keys}";
51pub const EMERGENCY_BUTTON_PATH: &str = "/sign/emergency_button/{chain_id}/{nonce}/{type}";
52pub const LIMIT_UPDATE_PATH: &str =
53    "/sign/update_limit/{chain_id}/{nonce}/{sending_chain_id}/{new_usd_limit}";
54pub const ASSET_PRICE_UPDATE_PATH: &str =
55    "/sign/update_asset_price/{chain_id}/{nonce}/{token_id}/{new_usd_price}";
56pub const EVM_CONTRACT_UPGRADE_PATH_WITH_CALLDATA: &str =
57    "/sign/upgrade_evm_contract/{chain_id}/{nonce}/{proxy_address}/{new_impl_address}/{calldata}";
58pub const EVM_CONTRACT_UPGRADE_PATH: &str =
59    "/sign/upgrade_evm_contract/{chain_id}/{nonce}/{proxy_address}/{new_impl_address}";
60pub const ADD_TOKENS_ON_IOTA_PATH: &str = "/sign/add_tokens_on_iota/{chain_id}/{nonce}/{native}/{token_ids}/{token_type_names}/{token_prices}";
61pub const ADD_TOKENS_ON_EVM_PATH: &str = "/sign/add_tokens_on_evm/{chain_id}/{nonce}/{native}/{token_ids}/{token_addresses}/{token_iota_decimals}/{token_prices}";
62
63/// BridgeNode's public metadata that is acceesible via the `/ping` endpoint.
64// Be careful with what to put here, as it is public.
65#[derive(serde::Serialize, Clone)]
66pub struct BridgeNodePublicMetadata {
67    pub version: Option<String>,
68}
69
70impl BridgeNodePublicMetadata {
71    pub fn new(version: String) -> Self {
72        Self {
73            version: Some(version),
74        }
75    }
76
77    pub fn empty_for_testing() -> Self {
78        Self { version: None }
79    }
80}
81
82pub fn run_server(
83    socket_address: &SocketAddr,
84    handler: BridgeRequestHandler,
85    metrics: Arc<BridgeMetrics>,
86    metadata: Arc<BridgeNodePublicMetadata>,
87) -> tokio::task::JoinHandle<()> {
88    let socket_address = *socket_address;
89    tokio::spawn(async move {
90        let listener = tokio::net::TcpListener::bind(socket_address).await.unwrap();
91        axum::serve(
92            listener,
93            make_router(Arc::new(handler), metrics, metadata).into_make_service(),
94        )
95        .await
96        .unwrap();
97    })
98}
99
100pub(crate) fn make_router(
101    handler: Arc<impl BridgeRequestHandlerTrait + Sync + Send + 'static>,
102    metrics: Arc<BridgeMetrics>,
103    metadata: Arc<BridgeNodePublicMetadata>,
104) -> Router {
105    Router::new()
106        .route("/", get(health_check))
107        .route(PING_PATH, get(ping))
108        .route(ETH_TO_IOTA_TX_PATH, get(handle_eth_tx_hash))
109        .route(IOTA_TO_ETH_TX_PATH, get(handle_iota_tx_digest))
110        .route(
111            COMMITTEE_BLOCKLIST_UPDATE_PATH,
112            get(handle_update_committee_blocklist_action),
113        )
114        .route(EMERGENCY_BUTTON_PATH, get(handle_emergency_action))
115        .route(LIMIT_UPDATE_PATH, get(handle_limit_update_action))
116        .route(
117            ASSET_PRICE_UPDATE_PATH,
118            get(handle_asset_price_update_action),
119        )
120        .route(EVM_CONTRACT_UPGRADE_PATH, get(handle_evm_contract_upgrade))
121        .route(
122            EVM_CONTRACT_UPGRADE_PATH_WITH_CALLDATA,
123            get(handle_evm_contract_upgrade_with_calldata),
124        )
125        .route(ADD_TOKENS_ON_IOTA_PATH, get(handle_add_tokens_on_iota))
126        .route(ADD_TOKENS_ON_EVM_PATH, get(handle_add_tokens_on_evm))
127        .with_state((handler, metrics, metadata))
128}
129
130impl axum::response::IntoResponse for BridgeError {
131    // TODO: distinguish client error.
132    fn into_response(self) -> axum::response::Response {
133        (
134            StatusCode::INTERNAL_SERVER_ERROR,
135            format!("Something went wrong: {:?}", self),
136        )
137            .into_response()
138    }
139}
140
141impl<E> From<E> for BridgeError
142where
143    E: Into<anyhow::Error>,
144{
145    fn from(err: E) -> Self {
146        Self::Generic(err.into().to_string())
147    }
148}
149
150async fn health_check() -> StatusCode {
151    StatusCode::OK
152}
153
154async fn ping(
155    State((_handler, _metrics, metadata)): State<(
156        Arc<impl BridgeRequestHandlerTrait + Sync + Send>,
157        Arc<BridgeMetrics>,
158        Arc<BridgeNodePublicMetadata>,
159    )>,
160) -> Result<Json<BridgeNodePublicMetadata>, BridgeError> {
161    Ok(Json(metadata.as_ref().clone()))
162}
163
164#[instrument(level = "error", skip_all, fields(tx_hash_hex=tx_hash_hex, event_idx=event_idx))]
165async fn handle_eth_tx_hash(
166    Path((tx_hash_hex, event_idx)): Path<(String, u16)>,
167    State((handler, metrics, _metadata)): State<(
168        Arc<impl BridgeRequestHandlerTrait + Sync + Send>,
169        Arc<BridgeMetrics>,
170        Arc<BridgeNodePublicMetadata>,
171    )>,
172) -> Result<Json<SignedBridgeAction>, BridgeError> {
173    let future = async {
174        let sig = handler.handle_eth_tx_hash(tx_hash_hex, event_idx).await?;
175        Ok(sig)
176    };
177    with_metrics!(metrics.clone(), "handle_eth_tx_hash", future).await
178}
179
180#[instrument(level = "error", skip_all, fields(tx_digest_base58=tx_digest_base58, event_idx=event_idx))]
181async fn handle_iota_tx_digest(
182    Path((tx_digest_base58, event_idx)): Path<(String, u16)>,
183    State((handler, metrics, _metadata)): State<(
184        Arc<impl BridgeRequestHandlerTrait + Sync + Send>,
185        Arc<BridgeMetrics>,
186        Arc<BridgeNodePublicMetadata>,
187    )>,
188) -> Result<Json<SignedBridgeAction>, BridgeError> {
189    let future = async {
190        let sig: Json<SignedBridgeAction> = handler
191            .handle_iota_tx_digest(tx_digest_base58, event_idx)
192            .await?;
193        Ok(sig)
194    };
195    with_metrics!(metrics.clone(), "handle_iota_tx_digest", future).await
196}
197
198#[instrument(level = "error", skip_all, fields(chain_id=chain_id, nonce=nonce, blocklist_type=blocklist_type, keys=keys))]
199async fn handle_update_committee_blocklist_action(
200    Path((chain_id, nonce, blocklist_type, keys)): Path<(u8, u64, u8, String)>,
201    State((handler, metrics, _metadata)): State<(
202        Arc<impl BridgeRequestHandlerTrait + Sync + Send>,
203        Arc<BridgeMetrics>,
204        Arc<BridgeNodePublicMetadata>,
205    )>,
206) -> Result<Json<SignedBridgeAction>, BridgeError> {
207    let future = async {
208        let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| {
209            BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err))
210        })?;
211        let blocklist_type = BlocklistType::try_from(blocklist_type).map_err(|err| {
212            BridgeError::InvalidBridgeClientRequest(format!(
213                "Invalid blocklist action type: {:?}",
214                err
215            ))
216        })?;
217        let members_to_update = keys
218            .split(',')
219            .map(|s| {
220                let bytes = Hex::decode(s).map_err(|e| anyhow::anyhow!("{:?}", e))?;
221                BridgeAuthorityPublicKeyBytes::from_bytes(&bytes)
222                    .map_err(|e| anyhow::anyhow!("{:?}", e))
223            })
224            .collect::<Result<Vec<_>, _>>()
225            .map_err(|e| BridgeError::InvalidBridgeClientRequest(format!("{:?}", e)))?;
226        let action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
227            chain_id,
228            nonce,
229            blocklist_type,
230            members_to_update,
231        });
232
233        let sig: Json<SignedBridgeAction> = handler.handle_governance_action(action).await?;
234        Ok(sig)
235    };
236    with_metrics!(
237        metrics.clone(),
238        "handle_update_committee_blocklist_action",
239        future
240    )
241    .await
242}
243
244#[instrument(level = "error", skip_all, fields(chain_id=chain_id, nonce=nonce, action_type=action_type))]
245async fn handle_emergency_action(
246    Path((chain_id, nonce, action_type)): Path<(u8, u64, u8)>,
247    State((handler, metrics, _metadata)): State<(
248        Arc<impl BridgeRequestHandlerTrait + Sync + Send>,
249        Arc<BridgeMetrics>,
250        Arc<BridgeNodePublicMetadata>,
251    )>,
252) -> Result<Json<SignedBridgeAction>, BridgeError> {
253    let future = async {
254        let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| {
255            BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err))
256        })?;
257        let action_type = EmergencyActionType::try_from(action_type).map_err(|err| {
258            BridgeError::InvalidBridgeClientRequest(format!(
259                "Invalid emergency action type: {:?}",
260                err
261            ))
262        })?;
263        let action = BridgeAction::EmergencyAction(EmergencyAction {
264            chain_id,
265            nonce,
266            action_type,
267        });
268        let sig: Json<SignedBridgeAction> = handler.handle_governance_action(action).await?;
269        Ok(sig)
270    };
271    with_metrics!(metrics.clone(), "handle_emergency_action", future).await
272}
273
274#[instrument(level = "error", skip_all, fields(chain_id=chain_id, nonce=nonce, sending_chain_id=sending_chain_id, new_usd_limit=new_usd_limit))]
275async fn handle_limit_update_action(
276    Path((chain_id, nonce, sending_chain_id, new_usd_limit)): Path<(u8, u64, u8, u64)>,
277    State((handler, metrics, _metadata)): State<(
278        Arc<impl BridgeRequestHandlerTrait + Sync + Send>,
279        Arc<BridgeMetrics>,
280        Arc<BridgeNodePublicMetadata>,
281    )>,
282) -> Result<Json<SignedBridgeAction>, BridgeError> {
283    let future = async {
284        let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| {
285            BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err))
286        })?;
287        let sending_chain_id = BridgeChainId::try_from(sending_chain_id).map_err(|err| {
288            BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err))
289        })?;
290        let action = BridgeAction::LimitUpdateAction(LimitUpdateAction {
291            chain_id,
292            nonce,
293            sending_chain_id,
294            new_usd_limit,
295        });
296        let sig: Json<SignedBridgeAction> = handler.handle_governance_action(action).await?;
297        Ok(sig)
298    };
299    with_metrics!(metrics.clone(), "handle_limit_update_action", future).await
300}
301
302#[instrument(level = "error", skip_all, fields(chain_id=chain_id, nonce=nonce, token_id=token_id, new_usd_price=new_usd_price))]
303async fn handle_asset_price_update_action(
304    Path((chain_id, nonce, token_id, new_usd_price)): Path<(u8, u64, u8, u64)>,
305    State((handler, metrics, _metadata)): State<(
306        Arc<impl BridgeRequestHandlerTrait + Sync + Send>,
307        Arc<BridgeMetrics>,
308        Arc<BridgeNodePublicMetadata>,
309    )>,
310) -> Result<Json<SignedBridgeAction>, BridgeError> {
311    let future = async {
312        let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| {
313            BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err))
314        })?;
315        let action = BridgeAction::AssetPriceUpdateAction(AssetPriceUpdateAction {
316            chain_id,
317            nonce,
318            token_id,
319            new_usd_price,
320        });
321        let sig: Json<SignedBridgeAction> = handler.handle_governance_action(action).await?;
322        Ok(sig)
323    };
324    with_metrics!(metrics.clone(), "handle_asset_price_update_action", future).await
325}
326
327#[instrument(level = "error", skip_all, fields(chain_id=chain_id, nonce=nonce, proxy_address=format!("{:x}", proxy_address), new_impl_address=format!("{:x}", new_impl_address)))]
328async fn handle_evm_contract_upgrade_with_calldata(
329    Path((chain_id, nonce, proxy_address, new_impl_address, calldata)): Path<(
330        u8,
331        u64,
332        EthAddress,
333        EthAddress,
334        String,
335    )>,
336    State((handler, metrics, _metadata)): State<(
337        Arc<impl BridgeRequestHandlerTrait + Sync + Send>,
338        Arc<BridgeMetrics>,
339        Arc<BridgeNodePublicMetadata>,
340    )>,
341) -> Result<Json<SignedBridgeAction>, BridgeError> {
342    let future = async {
343        let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| {
344            BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err))
345        })?;
346        let call_data = Hex::decode(&calldata).map_err(|e| {
347            BridgeError::InvalidBridgeClientRequest(format!("Invalid call data: {:?}", e))
348        })?;
349        let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
350            chain_id,
351            nonce,
352            proxy_address,
353            new_impl_address,
354            call_data,
355        });
356        let sig: Json<SignedBridgeAction> = handler.handle_governance_action(action).await?;
357        Ok(sig)
358    };
359    with_metrics!(
360        metrics.clone(),
361        "handle_evm_contract_upgrade_with_calldata",
362        future
363    )
364    .await
365}
366
367#[instrument(
368    level = "error",
369    skip_all,
370    fields(chain_id, nonce, proxy_address, new_impl_address)
371)]
372async fn handle_evm_contract_upgrade(
373    Path((chain_id, nonce, proxy_address, new_impl_address)): Path<(
374        u8,
375        u64,
376        EthAddress,
377        EthAddress,
378    )>,
379    State((handler, metrics, _metadata)): State<(
380        Arc<impl BridgeRequestHandlerTrait + Sync + Send>,
381        Arc<BridgeMetrics>,
382        Arc<BridgeNodePublicMetadata>,
383    )>,
384) -> Result<Json<SignedBridgeAction>, BridgeError> {
385    let future = async {
386        let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| {
387            BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err))
388        })?;
389        let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
390            chain_id,
391            nonce,
392            proxy_address,
393            new_impl_address,
394            call_data: vec![],
395        });
396        let sig: Json<SignedBridgeAction> = handler.handle_governance_action(action).await?;
397
398        Ok(sig)
399    };
400    with_metrics!(metrics.clone(), "handle_evm_contract_upgrade", future).await
401}
402
403#[instrument(level = "error", skip_all, fields(chain_id=chain_id, nonce=nonce, native=native, token_ids=token_ids, token_type_names=token_type_names, token_prices=token_prices))]
404async fn handle_add_tokens_on_iota(
405    Path((chain_id, nonce, native, token_ids, token_type_names, token_prices)): Path<(
406        u8,
407        u64,
408        u8,
409        String,
410        String,
411        String,
412    )>,
413    State((handler, metrics, _metadata)): State<(
414        Arc<impl BridgeRequestHandlerTrait + Sync + Send>,
415        Arc<BridgeMetrics>,
416        Arc<BridgeNodePublicMetadata>,
417    )>,
418) -> Result<Json<SignedBridgeAction>, BridgeError> {
419    let future = async {
420        let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| {
421            BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err))
422        })?;
423
424        if !chain_id.is_iota_chain() {
425            return Err(BridgeError::InvalidBridgeClientRequest(
426                "handle_add_tokens_on_iota only expects IOTA chain id".to_string(),
427            ));
428        }
429
430        let native = match native {
431            1 => true,
432            0 => false,
433            _ => {
434                return Err(BridgeError::InvalidBridgeClientRequest(format!(
435                    "Invalid native flag: {}",
436                    native
437                )));
438            }
439        };
440        let token_ids = token_ids
441            .split(',')
442            .map(|s| {
443                s.parse::<u8>().map_err(|err| {
444                    BridgeError::InvalidBridgeClientRequest(format!("Invalid token id: {:?}", err))
445                })
446            })
447            .collect::<Result<Vec<_>, _>>()?;
448        let token_type_names = token_type_names
449            .split(',')
450            .map(|s| {
451                TypeTag::from_str(s).map_err(|err| {
452                    BridgeError::InvalidBridgeClientRequest(format!(
453                        "Invalid token type name: {:?}",
454                        err
455                    ))
456                })
457            })
458            .collect::<Result<Vec<_>, _>>()?;
459        let token_prices = token_prices
460            .split(',')
461            .map(|s| {
462                s.parse::<u64>().map_err(|err| {
463                    BridgeError::InvalidBridgeClientRequest(format!(
464                        "Invalid token price: {:?}",
465                        err
466                    ))
467                })
468            })
469            .collect::<Result<Vec<_>, _>>()?;
470        let action = BridgeAction::AddTokensOnIotaAction(AddTokensOnIotaAction {
471            chain_id,
472            nonce,
473            native,
474            token_ids,
475            token_type_names,
476            token_prices,
477        });
478        let sig: Json<SignedBridgeAction> = handler.handle_governance_action(action).await?;
479        Ok(sig)
480    };
481    with_metrics!(metrics.clone(), "handle_add_tokens_on_iota", future).await
482}
483
484#[instrument(level = "error", skip_all, fields(chain_id=chain_id, nonce=nonce, native=native, token_ids=token_ids, token_addresses=token_addresses, token_iota_decimals=token_iota_decimals, token_prices=token_prices))]
485async fn handle_add_tokens_on_evm(
486    Path((chain_id, nonce, native, token_ids, token_addresses, token_iota_decimals, token_prices)): Path<(
487        u8,
488        u64,
489        u8,
490        String,
491        String,
492        String,
493        String,
494    )>,
495    State((handler, metrics, _metadata)): State<(
496        Arc<impl BridgeRequestHandlerTrait + Sync + Send>,
497        Arc<BridgeMetrics>,
498        Arc<BridgeNodePublicMetadata>,
499    )>,
500) -> Result<Json<SignedBridgeAction>, BridgeError> {
501    let future = async {
502        let chain_id = BridgeChainId::try_from(chain_id).map_err(|err| {
503            BridgeError::InvalidBridgeClientRequest(format!("Invalid chain id: {:?}", err))
504        })?;
505        if chain_id.is_iota_chain() {
506            return Err(BridgeError::InvalidBridgeClientRequest(
507                "handle_add_tokens_on_evm does not expect IOTA chain id".to_string(),
508            ));
509        }
510
511        let native = match native {
512            1 => true,
513            0 => false,
514            _ => {
515                return Err(BridgeError::InvalidBridgeClientRequest(format!(
516                    "Invalid native flag: {}",
517                    native
518                )));
519            }
520        };
521        let token_ids = token_ids
522            .split(',')
523            .map(|s| {
524                s.parse::<u8>().map_err(|err| {
525                    BridgeError::InvalidBridgeClientRequest(format!("Invalid token id: {:?}", err))
526                })
527            })
528            .collect::<Result<Vec<_>, _>>()?;
529        let token_addresses = token_addresses
530            .split(',')
531            .map(|s| {
532                EthAddress::from_str(s).map_err(|err| {
533                    BridgeError::InvalidBridgeClientRequest(format!(
534                        "Invalid token address: {:?}",
535                        err
536                    ))
537                })
538            })
539            .collect::<Result<Vec<_>, _>>()?;
540        let token_iota_decimals = token_iota_decimals
541            .split(',')
542            .map(|s| {
543                s.parse::<u8>().map_err(|err| {
544                    BridgeError::InvalidBridgeClientRequest(format!(
545                        "Invalid token iota decimals: {:?}",
546                        err
547                    ))
548                })
549            })
550            .collect::<Result<Vec<_>, _>>()?;
551        let token_prices = token_prices
552            .split(',')
553            .map(|s| {
554                s.parse::<u64>().map_err(|err| {
555                    BridgeError::InvalidBridgeClientRequest(format!(
556                        "Invalid token price: {:?}",
557                        err
558                    ))
559                })
560            })
561            .collect::<Result<Vec<_>, _>>()?;
562        let action = BridgeAction::AddTokensOnEvmAction(AddTokensOnEvmAction {
563            chain_id,
564            nonce,
565            native,
566            token_ids,
567            token_addresses,
568            token_iota_decimals,
569            token_prices,
570        });
571        let sig: Json<SignedBridgeAction> = handler.handle_governance_action(action).await?;
572        Ok(sig)
573    };
574    with_metrics!(metrics.clone(), "handle_add_tokens_on_evm", future).await
575}
576
577#[macro_export]
578macro_rules! with_metrics {
579    ($metrics:expr, $type_:expr, $func:expr) => {
580        async move {
581            info!("Received {} request", $type_);
582            $metrics
583                .requests_received
584                .with_label_values(&[$type_])
585                .inc();
586            $metrics
587                .requests_inflight
588                .with_label_values(&[$type_])
589                .inc();
590
591            let result = $func.await;
592
593            match &result {
594                Ok(_) => {
595                    info!("{} request succeeded", $type_);
596                    $metrics.requests_ok.with_label_values(&[$type_]).inc();
597                }
598                Err(e) => {
599                    info!("{} request failed: {:?}", $type_, e);
600                    $metrics.err_requests.with_label_values(&[$type_]).inc();
601                }
602            }
603
604            $metrics
605                .requests_inflight
606                .with_label_values(&[$type_])
607                .dec();
608            result
609        }
610    };
611}
612
613#[cfg(test)]
614mod tests {
615    use iota_types::bridge::TOKEN_ID_BTC;
616
617    use super::*;
618    use crate::{
619        client::bridge_client::BridgeClient, server::mock_handler::BridgeRequestMockHandler,
620        test_utils::get_test_authorities_and_run_mock_bridge_server, types::BridgeCommittee,
621    };
622
623    #[tokio::test]
624    #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
625    async fn test_bridge_server_handle_blocklist_update_action_path() {
626        let client = setup();
627
628        let pub_key_bytes = BridgeAuthorityPublicKeyBytes::from_bytes(
629            &Hex::decode("02321ede33d2c2d7a8a152f275a1484edef2098f034121a602cb7d767d38680aa4")
630                .unwrap(),
631        )
632        .unwrap();
633        let action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
634            nonce: 129,
635            chain_id: BridgeChainId::IotaCustom,
636            blocklist_type: BlocklistType::Blocklist,
637            members_to_update: vec![pub_key_bytes.clone()],
638        });
639        client.request_sign_bridge_action(action).await.unwrap();
640    }
641
642    #[tokio::test]
643    #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
644    async fn test_bridge_server_handle_emergency_action_path() {
645        let client = setup();
646
647        let action = BridgeAction::EmergencyAction(EmergencyAction {
648            nonce: 55,
649            chain_id: BridgeChainId::IotaCustom,
650            action_type: EmergencyActionType::Pause,
651        });
652        client.request_sign_bridge_action(action).await.unwrap();
653    }
654
655    #[tokio::test]
656    #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
657    async fn test_bridge_server_handle_limit_update_action_path() {
658        let client = setup();
659
660        let action = BridgeAction::LimitUpdateAction(LimitUpdateAction {
661            nonce: 15,
662            chain_id: BridgeChainId::IotaCustom,
663            sending_chain_id: BridgeChainId::EthCustom,
664            new_usd_limit: 1_000_000_0000, // $1M USD
665        });
666        client.request_sign_bridge_action(action).await.unwrap();
667    }
668
669    #[tokio::test]
670    #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
671    async fn test_bridge_server_handle_asset_price_update_action_path() {
672        let client = setup();
673
674        let action = BridgeAction::AssetPriceUpdateAction(AssetPriceUpdateAction {
675            nonce: 266,
676            chain_id: BridgeChainId::IotaCustom,
677            token_id: TOKEN_ID_BTC,
678            new_usd_price: 100_000_0000, // $100k USD
679        });
680        client.request_sign_bridge_action(action).await.unwrap();
681    }
682
683    #[tokio::test]
684    #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
685    async fn test_bridge_server_handle_evm_contract_upgrade_action_path() {
686        let client = setup();
687
688        let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
689            nonce: 123,
690            chain_id: BridgeChainId::EthCustom,
691            proxy_address: EthAddress::repeat_byte(6),
692            new_impl_address: EthAddress::repeat_byte(9),
693            call_data: vec![],
694        });
695        client.request_sign_bridge_action(action).await.unwrap();
696
697        let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
698            nonce: 123,
699            chain_id: BridgeChainId::EthCustom,
700            proxy_address: EthAddress::repeat_byte(6),
701            new_impl_address: EthAddress::repeat_byte(9),
702            call_data: vec![12, 34, 56],
703        });
704        client.request_sign_bridge_action(action).await.unwrap();
705    }
706
707    #[tokio::test]
708    #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
709    async fn test_bridge_server_handle_add_tokens_on_iota_action_path() {
710        let client = setup();
711
712        let action = BridgeAction::AddTokensOnIotaAction(AddTokensOnIotaAction {
713            nonce: 266,
714            chain_id: BridgeChainId::IotaCustom,
715            native: false,
716            token_ids: vec![100, 101, 102],
717            token_type_names: vec![
718                TypeTag::from_str("0x0000000000000000000000000000000000000000000000000000000000000abc::my_coin::MyCoin1").unwrap(),
719                TypeTag::from_str("0x0000000000000000000000000000000000000000000000000000000000000abc::my_coin::MyCoin2").unwrap(),
720                TypeTag::from_str("0x0000000000000000000000000000000000000000000000000000000000000abc::my_coin::MyCoin3").unwrap(),
721            ],
722            token_prices: vec![100_000_0000, 200_000_0000, 300_000_0000],
723        });
724        client.request_sign_bridge_action(action).await.unwrap();
725    }
726
727    #[tokio::test]
728    #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
729    async fn test_bridge_server_handle_add_tokens_on_evm_action_path() {
730        let client = setup();
731
732        let action = BridgeAction::AddTokensOnEvmAction(crate::types::AddTokensOnEvmAction {
733            nonce: 0,
734            chain_id: BridgeChainId::EthCustom,
735            native: false,
736            token_ids: vec![99, 100, 101],
737            token_addresses: vec![
738                EthAddress::repeat_byte(1),
739                EthAddress::repeat_byte(2),
740                EthAddress::repeat_byte(3),
741            ],
742            token_iota_decimals: vec![5, 6, 7],
743            token_prices: vec![1_000_000_000, 2_000_000_000, 3_000_000_000],
744        });
745        client.request_sign_bridge_action(action).await.unwrap();
746    }
747
748    fn setup() -> BridgeClient {
749        let mock = BridgeRequestMockHandler::new();
750        let (_handles, authorities, mut secrets) =
751            get_test_authorities_and_run_mock_bridge_server(vec![10000], vec![mock.clone()]);
752        mock.set_signer(secrets.swap_remove(0));
753        let committee = BridgeCommittee::new(authorities).unwrap();
754        let pub_key = committee.members().keys().next().unwrap();
755        BridgeClient::new(pub_key.clone(), Arc::new(committee)).unwrap()
756    }
757}