1use enum_dispatch::enum_dispatch;
6use ethers::types::Address as EthAddress;
7use iota_types::base_types::IOTA_ADDRESS_LENGTH;
8
9use crate::types::{
10 AddTokensOnEvmAction, AddTokensOnIotaAction, AssetPriceUpdateAction, BlocklistCommitteeAction,
11 BridgeAction, BridgeActionType, EmergencyAction, EthToIotaBridgeAction,
12 EvmContractUpgradeAction, IotaToEthBridgeAction, LimitUpdateAction,
13};
14
15pub const TOKEN_TRANSFER_MESSAGE_VERSION: u8 = 1;
16pub const COMMITTEE_BLOCKLIST_MESSAGE_VERSION: u8 = 1;
17pub const EMERGENCY_BUTTON_MESSAGE_VERSION: u8 = 1;
18pub const LIMIT_UPDATE_MESSAGE_VERSION: u8 = 1;
19pub const ASSET_PRICE_UPDATE_MESSAGE_VERSION: u8 = 1;
20pub const EVM_CONTRACT_UPGRADE_MESSAGE_VERSION: u8 = 1;
21pub const ADD_TOKENS_ON_IOTA_MESSAGE_VERSION: u8 = 1;
22pub const ADD_TOKENS_ON_EVM_MESSAGE_VERSION: u8 = 1;
23
24pub const BRIDGE_MESSAGE_PREFIX: &[u8] = b"IOTA_BRIDGE_MESSAGE";
25
26#[enum_dispatch]
33pub trait BridgeMessageEncoding {
34 fn as_bytes(&self) -> Vec<u8>;
36 fn as_payload_bytes(&self) -> Vec<u8>;
38}
39
40impl BridgeMessageEncoding for IotaToEthBridgeAction {
41 fn as_bytes(&self) -> Vec<u8> {
42 let mut bytes = Vec::new();
43 let e = &self.iota_bridge_event;
44 bytes.push(BridgeActionType::TokenTransfer as u8);
46 bytes.push(TOKEN_TRANSFER_MESSAGE_VERSION);
48 bytes.extend_from_slice(&e.nonce.to_be_bytes());
50 bytes.push(e.iota_chain_id as u8);
52
53 bytes.extend_from_slice(&self.as_payload_bytes());
55
56 bytes
57 }
58
59 fn as_payload_bytes(&self) -> Vec<u8> {
60 let mut bytes = Vec::new();
61 let e = &self.iota_bridge_event;
62
63 bytes.push(IOTA_ADDRESS_LENGTH as u8);
65 bytes.extend_from_slice(&e.iota_address.to_vec());
67 bytes.push(e.eth_chain_id as u8);
69 bytes.push(EthAddress::len_bytes() as u8);
71 bytes.extend_from_slice(e.eth_address.as_bytes());
73
74 bytes.push(e.token_id);
76
77 bytes.extend_from_slice(&e.amount_iota_adjusted.to_be_bytes());
79
80 bytes
81 }
82}
83
84impl BridgeMessageEncoding for EthToIotaBridgeAction {
85 fn as_bytes(&self) -> Vec<u8> {
86 let mut bytes = Vec::new();
87 let e = &self.eth_bridge_event;
88 bytes.push(BridgeActionType::TokenTransfer as u8);
90 bytes.push(TOKEN_TRANSFER_MESSAGE_VERSION);
92 bytes.extend_from_slice(&e.nonce.to_be_bytes());
94 bytes.push(e.eth_chain_id as u8);
96
97 bytes.extend_from_slice(&self.as_payload_bytes());
99
100 bytes
101 }
102
103 fn as_payload_bytes(&self) -> Vec<u8> {
104 let mut bytes = Vec::new();
105 let e = &self.eth_bridge_event;
106
107 bytes.push(EthAddress::len_bytes() as u8);
109 bytes.extend_from_slice(e.eth_address.as_bytes());
111 bytes.push(e.iota_chain_id as u8);
113 bytes.push(IOTA_ADDRESS_LENGTH as u8);
115 bytes.extend_from_slice(&e.iota_address.to_vec());
117
118 bytes.push(e.token_id);
120
121 bytes.extend_from_slice(&e.iota_adjusted_amount.to_be_bytes());
123
124 bytes
125 }
126}
127
128impl BridgeMessageEncoding for BlocklistCommitteeAction {
129 fn as_bytes(&self) -> Vec<u8> {
130 let mut bytes = Vec::new();
131 bytes.push(BridgeActionType::UpdateCommitteeBlocklist as u8);
133 bytes.push(COMMITTEE_BLOCKLIST_MESSAGE_VERSION);
135 bytes.extend_from_slice(&self.nonce.to_be_bytes());
137 bytes.push(self.chain_id as u8);
139
140 bytes.extend_from_slice(&self.as_payload_bytes());
142
143 bytes
144 }
145
146 fn as_payload_bytes(&self) -> Vec<u8> {
147 let mut bytes = Vec::new();
148
149 bytes.push(self.blocklist_type as u8);
151 bytes.push(u8::try_from(self.members_to_update.len()).unwrap());
154
155 let members_bytes = self
158 .members_to_update
159 .iter()
160 .map(|m| m.to_eth_address().to_fixed_bytes().to_vec())
161 .collect::<Vec<_>>();
162 for members_bytes in members_bytes {
163 bytes.extend_from_slice(&members_bytes);
164 }
165
166 bytes
167 }
168}
169
170impl BridgeMessageEncoding for EmergencyAction {
171 fn as_bytes(&self) -> Vec<u8> {
172 let mut bytes = Vec::new();
173 bytes.push(BridgeActionType::EmergencyButton as u8);
175 bytes.push(EMERGENCY_BUTTON_MESSAGE_VERSION);
177 bytes.extend_from_slice(&self.nonce.to_be_bytes());
179 bytes.push(self.chain_id as u8);
181
182 bytes.extend_from_slice(&self.as_payload_bytes());
184
185 bytes
186 }
187
188 fn as_payload_bytes(&self) -> Vec<u8> {
189 vec![self.action_type as u8]
190 }
191}
192
193impl BridgeMessageEncoding for LimitUpdateAction {
194 fn as_bytes(&self) -> Vec<u8> {
195 let mut bytes = Vec::new();
196 bytes.push(BridgeActionType::LimitUpdate as u8);
198 bytes.push(LIMIT_UPDATE_MESSAGE_VERSION);
200 bytes.extend_from_slice(&self.nonce.to_be_bytes());
202 bytes.push(self.chain_id as u8);
204
205 bytes.extend_from_slice(&self.as_payload_bytes());
207
208 bytes
209 }
210
211 fn as_payload_bytes(&self) -> Vec<u8> {
212 let mut bytes = Vec::new();
213 bytes.push(self.sending_chain_id as u8);
215 bytes.extend_from_slice(&self.new_usd_limit.to_be_bytes());
217 bytes
218 }
219}
220
221impl BridgeMessageEncoding for AssetPriceUpdateAction {
222 fn as_bytes(&self) -> Vec<u8> {
223 let mut bytes = Vec::new();
224 bytes.push(BridgeActionType::AssetPriceUpdate as u8);
226 bytes.push(EMERGENCY_BUTTON_MESSAGE_VERSION);
228 bytes.extend_from_slice(&self.nonce.to_be_bytes());
230 bytes.push(self.chain_id as u8);
232
233 bytes.extend_from_slice(&self.as_payload_bytes());
235
236 bytes
237 }
238
239 fn as_payload_bytes(&self) -> Vec<u8> {
240 let mut bytes = Vec::new();
241 bytes.push(self.token_id);
243 bytes.extend_from_slice(&self.new_usd_price.to_be_bytes());
245 bytes
246 }
247}
248
249impl BridgeMessageEncoding for EvmContractUpgradeAction {
250 fn as_bytes(&self) -> Vec<u8> {
251 let mut bytes = Vec::new();
252 bytes.push(BridgeActionType::EvmContractUpgrade as u8);
254 bytes.push(EVM_CONTRACT_UPGRADE_MESSAGE_VERSION);
256 bytes.extend_from_slice(&self.nonce.to_be_bytes());
258 bytes.push(self.chain_id as u8);
260
261 bytes.extend_from_slice(&self.as_payload_bytes());
263
264 bytes
265 }
266
267 fn as_payload_bytes(&self) -> Vec<u8> {
268 ethers::abi::encode(&[
269 ethers::abi::Token::Address(self.proxy_address),
270 ethers::abi::Token::Address(self.new_impl_address),
271 ethers::abi::Token::Bytes(self.call_data.clone()),
272 ])
273 }
274}
275
276impl BridgeMessageEncoding for AddTokensOnIotaAction {
277 fn as_bytes(&self) -> Vec<u8> {
278 let mut bytes = Vec::new();
279 bytes.push(BridgeActionType::AddTokensOnIota as u8);
281 bytes.push(ADD_TOKENS_ON_IOTA_MESSAGE_VERSION);
283 bytes.extend_from_slice(&self.nonce.to_be_bytes());
285 bytes.push(self.chain_id as u8);
287
288 bytes.extend_from_slice(&self.as_payload_bytes());
290
291 bytes
292 }
293
294 fn as_payload_bytes(&self) -> Vec<u8> {
295 let mut bytes = Vec::new();
296 bytes.push(self.native as u8);
298 bytes.extend_from_slice(&bcs::to_bytes(&self.token_ids).unwrap());
301
302 bytes.extend_from_slice(
305 &bcs::to_bytes(
306 &self
307 .token_type_names
308 .iter()
309 .map(|m| m.to_canonical_string(false))
310 .collect::<Vec<_>>(),
311 )
312 .unwrap(),
313 );
314
315 bytes.extend_from_slice(&bcs::to_bytes(&self.token_prices).unwrap());
318
319 bytes
320 }
321}
322
323impl BridgeMessageEncoding for AddTokensOnEvmAction {
324 fn as_bytes(&self) -> Vec<u8> {
325 let mut bytes = Vec::new();
326 bytes.push(BridgeActionType::AddTokensOnEvm as u8);
328 bytes.push(ADD_TOKENS_ON_EVM_MESSAGE_VERSION);
330 bytes.extend_from_slice(&self.nonce.to_be_bytes());
332 bytes.push(self.chain_id as u8);
334
335 bytes.extend_from_slice(&self.as_payload_bytes());
337
338 bytes
339 }
340
341 fn as_payload_bytes(&self) -> Vec<u8> {
342 let mut bytes = Vec::new();
343 bytes.push(self.native as u8);
345 bytes.push(u8::try_from(self.token_ids.len()).unwrap());
348 for token_id in &self.token_ids {
349 bytes.push(*token_id);
350 }
351
352 bytes.push(u8::try_from(self.token_addresses.len()).unwrap());
355 for token_address in &self.token_addresses {
356 bytes.extend_from_slice(&token_address.to_fixed_bytes());
357 }
358
359 bytes.push(u8::try_from(self.token_iota_decimals.len()).unwrap());
362 for token_iota_decimal in &self.token_iota_decimals {
363 bytes.push(*token_iota_decimal);
364 }
365
366 bytes.push(u8::try_from(self.token_prices.len()).unwrap());
369 for token_price in &self.token_prices {
370 bytes.extend_from_slice(&token_price.to_be_bytes());
371 }
372 bytes
373 }
374}
375
376impl BridgeAction {
377 pub fn to_bytes(&self) -> Vec<u8> {
379 let mut bytes = Vec::new();
380 bytes.extend_from_slice(BRIDGE_MESSAGE_PREFIX);
382 bytes.extend_from_slice(&self.as_bytes());
384 bytes
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use std::str::FromStr;
391
392 use ethers::{
393 abi::ParamType,
394 types::{Address as EthAddress, TxHash},
395 };
396 use fastcrypto::{
397 encoding::{Encoding, Hex},
398 hash::{HashFunction, Keccak256},
399 traits::ToFromBytes,
400 };
401 use iota_types::{
402 TypeTag,
403 base_types::{IotaAddress, TransactionDigest},
404 bridge::{BridgeChainId, TOKEN_ID_BTC, TOKEN_ID_USDC},
405 };
406 use prometheus::Registry;
407
408 use super::*;
409 use crate::{
410 abi::EthToIotaTokenBridgeV1,
411 crypto::{BridgeAuthorityKeyPair, BridgeAuthorityPublicKeyBytes, BridgeAuthoritySignInfo},
412 events::EmittedIotaToEthTokenBridgeV1,
413 types::{BlocklistType, EmergencyActionType, USD_MULTIPLIER},
414 };
415
416 #[test]
417 #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
418 fn test_bridge_message_encoding() -> anyhow::Result<()> {
419 telemetry_subscribers::init_for_testing();
420 let registry = Registry::new();
421 iota_metrics::init_metrics(®istry);
422 let nonce = 54321u64;
423 let iota_tx_digest = TransactionDigest::random();
424 let iota_chain_id = BridgeChainId::IotaTestnet;
425 let iota_tx_event_index = 1u16;
426 let eth_chain_id = BridgeChainId::EthSepolia;
427 let iota_address = IotaAddress::random_for_testing_only();
428 let eth_address = EthAddress::random();
429 let token_id = TOKEN_ID_USDC;
430 let amount_iota_adjusted = 1_000_000;
431
432 let iota_bridge_event = EmittedIotaToEthTokenBridgeV1 {
433 nonce,
434 iota_chain_id,
435 eth_chain_id,
436 iota_address,
437 eth_address,
438 token_id,
439 amount_iota_adjusted,
440 };
441
442 let encoded_bytes = BridgeAction::IotaToEthBridgeAction(IotaToEthBridgeAction {
443 iota_tx_digest,
444 iota_tx_event_index,
445 iota_bridge_event,
446 })
447 .to_bytes();
448
449 let prefix_bytes = BRIDGE_MESSAGE_PREFIX.to_vec(); let message_type = vec![BridgeActionType::TokenTransfer as u8]; let message_version = vec![TOKEN_TRANSFER_MESSAGE_VERSION]; let nonce_bytes = nonce.to_be_bytes().to_vec(); let source_chain_id_bytes = vec![iota_chain_id as u8]; let iota_address_length_bytes = vec![IOTA_ADDRESS_LENGTH as u8]; let iota_address_bytes = iota_address.to_vec(); let dest_chain_id_bytes = vec![eth_chain_id as u8]; let eth_address_length_bytes = vec![EthAddress::len_bytes() as u8]; let eth_address_bytes = eth_address.as_bytes().to_vec(); let token_id_bytes = vec![token_id]; let token_amount_bytes = amount_iota_adjusted.to_be_bytes().to_vec(); let mut combined_bytes = Vec::new();
466 combined_bytes.extend_from_slice(&prefix_bytes);
467 combined_bytes.extend_from_slice(&message_type);
468 combined_bytes.extend_from_slice(&message_version);
469 combined_bytes.extend_from_slice(&nonce_bytes);
470 combined_bytes.extend_from_slice(&source_chain_id_bytes);
471 combined_bytes.extend_from_slice(&iota_address_length_bytes);
472 combined_bytes.extend_from_slice(&iota_address_bytes);
473 combined_bytes.extend_from_slice(&dest_chain_id_bytes);
474 combined_bytes.extend_from_slice(ð_address_length_bytes);
475 combined_bytes.extend_from_slice(ð_address_bytes);
476 combined_bytes.extend_from_slice(&token_id_bytes);
477 combined_bytes.extend_from_slice(&token_amount_bytes);
478
479 assert_eq!(combined_bytes, encoded_bytes);
480
481 assert_eq!(
484 combined_bytes.len(),
485 18 + 1 + 1 + 8 + 1 + 1 + 32 + 1 + 20 + 1 + 1 + 8
486 );
487 Ok(())
488 }
489
490 #[test]
491 #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
492 fn test_bridge_message_encoding_regression_emitted_iota_to_eth_token_bridge_v1()
493 -> anyhow::Result<()> {
494 telemetry_subscribers::init_for_testing();
495 let registry = Registry::new();
496 iota_metrics::init_metrics(®istry);
497 let iota_tx_digest = TransactionDigest::random();
498 let iota_tx_event_index = 1u16;
499
500 let nonce = 10u64;
501 let iota_chain_id = BridgeChainId::IotaTestnet;
502 let eth_chain_id = BridgeChainId::EthSepolia;
503 let iota_address = IotaAddress::from_str(
504 "0x0000000000000000000000000000000000000000000000000000000000000064",
505 )
506 .unwrap();
507 let eth_address =
508 EthAddress::from_str("0x00000000000000000000000000000000000000c8").unwrap();
509 let token_id = TOKEN_ID_USDC;
510 let amount_iota_adjusted = 12345;
511
512 let iota_bridge_event = EmittedIotaToEthTokenBridgeV1 {
513 nonce,
514 iota_chain_id,
515 eth_chain_id,
516 iota_address,
517 eth_address,
518 token_id,
519 amount_iota_adjusted,
520 };
521 let encoded_bytes = BridgeAction::IotaToEthBridgeAction(IotaToEthBridgeAction {
522 iota_tx_digest,
523 iota_tx_event_index,
524 iota_bridge_event,
525 })
526 .to_bytes();
527 assert_eq!(
528 encoded_bytes,
529 Hex::decode("5355495f4252494447455f4d4553534147450001000000000000000a012000000000000000000000000000000000000000000000000000000000000000640b1400000000000000000000000000000000000000c8030000000000003039").unwrap(),
530 );
531
532 let hash = Keccak256::digest(encoded_bytes).digest;
533 assert_eq!(
534 hash.to_vec(),
535 Hex::decode("6ab34c52b6264cbc12fe8c3874f9b08f8481d2e81530d136386646dbe2f8baf4")
536 .unwrap(),
537 );
538 Ok(())
539 }
540
541 #[test]
542 #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
543 fn test_bridge_message_encoding_blocklist_update_v1() {
544 telemetry_subscribers::init_for_testing();
545 let registry = Registry::new();
546 iota_metrics::init_metrics(®istry);
547
548 let pub_key_bytes = BridgeAuthorityPublicKeyBytes::from_bytes(
549 &Hex::decode("02321ede33d2c2d7a8a152f275a1484edef2098f034121a602cb7d767d38680aa4")
550 .unwrap(),
551 )
552 .unwrap();
553 let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
554 nonce: 129,
555 chain_id: BridgeChainId::IotaCustom,
556 blocklist_type: BlocklistType::Blocklist,
557 members_to_update: vec![pub_key_bytes.clone()],
558 });
559 let bytes = blocklist_action.to_bytes();
560 assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d4553534147450101000000000000008102000168b43fd906c0b8f024a18c56e06744f7c6157c65").unwrap());
571
572 let pub_key_bytes_2 = BridgeAuthorityPublicKeyBytes::from_bytes(
573 &Hex::decode("027f1178ff417fc9f5b8290bd8876f0a157a505a6c52db100a8492203ddd1d4279")
574 .unwrap(),
575 )
576 .unwrap();
577 let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
579 nonce: 68,
580 chain_id: BridgeChainId::IotaCustom,
581 blocklist_type: BlocklistType::Unblocklist,
582 members_to_update: vec![pub_key_bytes.clone(), pub_key_bytes_2.clone()],
583 });
584 let bytes = blocklist_action.to_bytes();
585 assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d4553534147450101000000000000004402010268b43fd906c0b8f024a18c56e06744f7c6157c65acaef39832cb995c4e049437a3e2ec6a7bad1ab5").unwrap());
597
598 let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
599 nonce: 49,
600 chain_id: BridgeChainId::EthCustom,
601 blocklist_type: BlocklistType::Blocklist,
602 members_to_update: vec![pub_key_bytes.clone()],
603 });
604 let bytes = blocklist_action.to_bytes();
605 assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d455353414745010100000000000000310c000168b43fd906c0b8f024a18c56e06744f7c6157c65").unwrap());
616
617 let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
618 nonce: 94,
619 chain_id: BridgeChainId::EthSepolia,
620 blocklist_type: BlocklistType::Unblocklist,
621 members_to_update: vec![pub_key_bytes.clone(), pub_key_bytes_2.clone()],
622 });
623 let bytes = blocklist_action.to_bytes();
624 assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d4553534147450101000000000000005e0b010268b43fd906c0b8f024a18c56e06744f7c6157c65acaef39832cb995c4e049437a3e2ec6a7bad1ab5").unwrap());
636 }
637
638 #[test]
639 #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
640 fn test_bridge_message_encoding_emergency_action() {
641 let action = BridgeAction::EmergencyAction(EmergencyAction {
642 nonce: 55,
643 chain_id: BridgeChainId::IotaCustom,
644 action_type: EmergencyActionType::Pause,
645 });
646 let bytes = action.to_bytes();
647 assert_eq!(
654 bytes,
655 Hex::decode("5355495f4252494447455f4d455353414745020100000000000000370200").unwrap()
656 );
657
658 let action = BridgeAction::EmergencyAction(EmergencyAction {
659 nonce: 56,
660 chain_id: BridgeChainId::EthSepolia,
661 action_type: EmergencyActionType::Unpause,
662 });
663 let bytes = action.to_bytes();
664 assert_eq!(
671 bytes,
672 Hex::decode("5355495f4252494447455f4d455353414745020100000000000000380b01").unwrap()
673 );
674 }
675
676 #[test]
677 #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
678 fn test_bridge_message_encoding_limit_update_action() {
679 let action = BridgeAction::LimitUpdateAction(LimitUpdateAction {
680 nonce: 15,
681 chain_id: BridgeChainId::IotaCustom,
682 sending_chain_id: BridgeChainId::EthCustom,
683 new_usd_limit: 1_000_000 * USD_MULTIPLIER, });
685 let bytes = action.to_bytes();
686 assert_eq!(
694 bytes,
695 Hex::decode(
696 "5355495f4252494447455f4d4553534147450301000000000000000f020c00000002540be400"
697 )
698 .unwrap()
699 );
700 }
701
702 #[test]
703 #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
704 fn test_bridge_message_encoding_asset_price_update_action() {
705 let action = BridgeAction::AssetPriceUpdateAction(AssetPriceUpdateAction {
706 nonce: 266,
707 chain_id: BridgeChainId::IotaCustom,
708 token_id: TOKEN_ID_BTC,
709 new_usd_price: 100_000 * USD_MULTIPLIER, });
711 let bytes = action.to_bytes();
712 assert_eq!(
720 bytes,
721 Hex::decode(
722 "5355495f4252494447455f4d4553534147450401000000000000010a0201000000003b9aca00"
723 )
724 .unwrap()
725 );
726 }
727
728 #[test]
729 #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
730 fn test_bridge_message_encoding_evm_contract_upgrade_action() {
731 let function_signature = "initializeV2()";
734 let selector = &Keccak256::digest(function_signature).digest[0..4];
735 let call_data = selector.to_vec();
736 assert_eq!(Hex::encode(call_data.clone()), "5cd8a76b");
737
738 let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
739 nonce: 123,
740 chain_id: BridgeChainId::EthCustom,
741 proxy_address: EthAddress::repeat_byte(6),
742 new_impl_address: EthAddress::repeat_byte(9),
743 call_data,
744 });
745 assert_eq!(
759 Hex::encode(action.to_bytes().clone()),
760 "5355495f4252494447455f4d4553534147450501000000000000007b0c00000000000000000000000006060606060606060606060606060606060606060000000000000000000000000909090909090909090909090909090909090909000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000045cd8a76b00000000000000000000000000000000000000000000000000000000"
761 );
762
763 let function_signature = "newMockFunction(bool)";
765 let selector = &Keccak256::digest(function_signature).digest[0..4];
766 let mut call_data = selector.to_vec();
767 call_data.extend(ethers::abi::encode(&[ethers::abi::Token::Bool(true)]));
768 assert_eq!(
769 Hex::encode(call_data.clone()),
770 "417795ef0000000000000000000000000000000000000000000000000000000000000001"
771 );
772 let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
773 nonce: 123,
774 chain_id: BridgeChainId::EthCustom,
775 proxy_address: EthAddress::repeat_byte(6),
776 new_impl_address: EthAddress::repeat_byte(9),
777 call_data,
778 });
779 assert_eq!(
794 Hex::encode(action.to_bytes().clone()),
795 "5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024417795ef000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000"
796 );
797
798 let function_signature = "newMockFunction(bool,uint8)";
800 let selector = &Keccak256::digest(function_signature).digest[0..4];
801 let mut call_data = selector.to_vec();
802 call_data.extend(ethers::abi::encode(&[
803 ethers::abi::Token::Bool(true),
804 ethers::abi::Token::Uint(42u8.into()),
805 ]));
806 assert_eq!(
807 Hex::encode(call_data.clone()),
808 "be8fc25d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002a"
809 );
810 let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
811 nonce: 123,
812 chain_id: BridgeChainId::EthCustom,
813 proxy_address: EthAddress::repeat_byte(6),
814 new_impl_address: EthAddress::repeat_byte(9),
815 call_data,
816 });
817 assert_eq!(
833 Hex::encode(action.to_bytes().clone()),
834 "5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044be8fc25d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000"
835 );
836
837 let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
839 nonce: 123,
840 chain_id: BridgeChainId::EthCustom,
841 proxy_address: EthAddress::repeat_byte(6),
842 new_impl_address: EthAddress::repeat_byte(9),
843 call_data: vec![],
844 });
845 let data = action.to_bytes();
858 assert_eq!(
859 Hex::encode(data.clone()),
860 "5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"
861 );
862 let types = vec![ParamType::Address, ParamType::Address, ParamType::Bytes];
863 ethers::abi::decode(&types, &data[29..]).unwrap();
865 }
866
867 #[test]
868 #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
869 fn test_bridge_message_encoding_regression_eth_to_iota_token_bridge_v1() -> anyhow::Result<()> {
870 telemetry_subscribers::init_for_testing();
871 let registry = Registry::new();
872 iota_metrics::init_metrics(®istry);
873 let eth_tx_hash = TxHash::random();
874 let eth_event_index = 1u16;
875
876 let nonce = 10u64;
877 let iota_chain_id = BridgeChainId::IotaTestnet;
878 let eth_chain_id = BridgeChainId::EthSepolia;
879 let iota_address = IotaAddress::from_str(
880 "0x0000000000000000000000000000000000000000000000000000000000000064",
881 )
882 .unwrap();
883 let eth_address =
884 EthAddress::from_str("0x00000000000000000000000000000000000000c8").unwrap();
885 let token_id = TOKEN_ID_USDC;
886 let iota_adjusted_amount = 12345;
887
888 let eth_bridge_event = EthToIotaTokenBridgeV1 {
889 nonce,
890 iota_chain_id,
891 eth_chain_id,
892 iota_address,
893 eth_address,
894 token_id,
895 iota_adjusted_amount,
896 };
897 let encoded_bytes = BridgeAction::EthToIotaBridgeAction(EthToIotaBridgeAction {
898 eth_tx_hash,
899 eth_event_index,
900 eth_bridge_event,
901 })
902 .to_bytes();
903
904 assert_eq!(
905 encoded_bytes,
906 Hex::decode("5355495f4252494447455f4d4553534147450001000000000000000a0b1400000000000000000000000000000000000000c801200000000000000000000000000000000000000000000000000000000000000064030000000000003039").unwrap(),
907 );
908
909 let hash = Keccak256::digest(encoded_bytes).digest;
910 assert_eq!(
911 hash.to_vec(),
912 Hex::decode("b352508c301a37bb1b68a75dd0fc42b6f692b2650818631c8f8a4d4d3e5bef46")
913 .unwrap(),
914 );
915 Ok(())
916 }
917
918 #[test]
919 #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
920 fn test_bridge_message_encoding_regression_add_coins_on_iota() -> anyhow::Result<()> {
921 telemetry_subscribers::init_for_testing();
922
923 let action = BridgeAction::AddTokensOnIotaAction(AddTokensOnIotaAction {
924 nonce: 0,
925 chain_id: BridgeChainId::IotaCustom,
926 native: false,
927 token_ids: vec![1, 2, 3, 4],
928 token_type_names: vec![
929 TypeTag::from_str("0x9b5e13bcd0cb23ff25c07698e89d48056c745338d8c9dbd033a4172b87027073::btc::BTC").unwrap(),
930 TypeTag::from_str("0x7970d71c03573f540a7157f0d3970e117effa6ae16cefd50b45c749670b24e6a::eth::ETH").unwrap(),
931 TypeTag::from_str("0x500e429a24478405d5130222b20f8570a746b6bc22423f14b4d4e6a8ea580736::usdc::USDC").unwrap(),
932 TypeTag::from_str("0x46bfe51da1bd9511919a92eb1154149b36c0f4212121808e13e3e5857d607a9c::usdt::USDT").unwrap(),
933 ],
934 token_prices: vec![
935 500_000_000u64,
936 30_000_000u64,
937 1_000u64,
938 1_000u64,
939 ]
940 });
941 let encoded_bytes = action.to_bytes();
942
943 assert_eq!(
944 Hex::encode(encoded_bytes),
945 "5355495f4252494447455f4d4553534147450601000000000000000002000401020304044a396235653133626364306362323366663235633037363938653839643438303536633734353333386438633964626430333361343137326238373032373037333a3a6274633a3a4254434a373937306437316330333537336635343061373135376630643339373065313137656666613661653136636566643530623435633734393637306232346536613a3a6574683a3a4554484c353030653432396132343437383430356435313330323232623230663835373061373436623662633232343233663134623464346536613865613538303733363a3a757364633a3a555344434c343662666535316461316264393531313931396139326562313135343134396233366330663432313231323138303865313365336535383537643630376139633a3a757364743a3a55534454040065cd1d0000000080c3c90100000000e803000000000000e803000000000000",
946 );
947 Ok(())
948 }
949
950 #[test]
951 #[ignore = "https://github.com/iotaledger/iota/issues/3224"]
952 fn test_bridge_message_encoding_regression_add_coins_on_evm() -> anyhow::Result<()> {
953 let action = BridgeAction::AddTokensOnEvmAction(crate::types::AddTokensOnEvmAction {
954 nonce: 0,
955 chain_id: BridgeChainId::EthCustom,
956 native: true,
957 token_ids: vec![99, 100, 101],
958 token_addresses: vec![
959 EthAddress::from_str("0x6B175474E89094C44Da98b954EedeAC495271d0F").unwrap(),
960 EthAddress::from_str("0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84").unwrap(),
961 EthAddress::from_str("0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72").unwrap(),
962 ],
963 token_iota_decimals: vec![5, 6, 7],
964 token_prices: vec![1_000_000_000, 2_000_000_000, 3_000_000_000],
965 });
966 let encoded_bytes = action.to_bytes();
967
968 assert_eq!(
969 Hex::encode(encoded_bytes),
970 "5355495f4252494447455f4d455353414745070100000000000000000c0103636465036b175474e89094c44da98b954eedeac495271d0fae7ab96520de3a18e5e111b5eaab095312d7fe84c18360217d8f7ab5e7c516566761ea12ce7f9d720305060703000000003b9aca00000000007735940000000000b2d05e00",
971 );
972 let keys = get_bridge_encoding_regression_test_keys();
974 for key in keys {
975 let pub_key = key.public.as_bytes();
976 println!("pub_key: {:?}", Hex::encode(pub_key));
977 println!(
978 "sig: {:?}",
979 Hex::encode(
980 BridgeAuthoritySignInfo::new(&action, &key)
981 .signature
982 .as_bytes()
983 )
984 );
985 }
986 Ok(())
987 }
988
989 fn get_bridge_encoding_regression_test_keys() -> Vec<BridgeAuthorityKeyPair> {
990 vec![
991 BridgeAuthorityKeyPair::from_bytes(
992 &Hex::decode("e42c82337ce12d4a7ad6cd65876d91b2ab6594fd50cdab1737c91773ba7451db")
993 .unwrap(),
994 )
995 .unwrap(),
996 BridgeAuthorityKeyPair::from_bytes(
997 &Hex::decode("1aacd610da3d0cc691a04b83b01c34c6c65cda0fe8d502df25ff4b3185c85687")
998 .unwrap(),
999 )
1000 .unwrap(),
1001 BridgeAuthorityKeyPair::from_bytes(
1002 &Hex::decode("53e7baf8378fbc62692e3056c2e10c6666ef8b5b3a53914830f47636d1678140")
1003 .unwrap(),
1004 )
1005 .unwrap(),
1006 BridgeAuthorityKeyPair::from_bytes(
1007 &Hex::decode("08b5350a091faabd5f25b6e290bfc3f505d43208775b9110dfed5ee6c7a653f0")
1008 .unwrap(),
1009 )
1010 .unwrap(),
1011 ]
1012 }
1013}