1#![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
45pub 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#[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 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, });
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, });
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}