sui_bridge/server/
governance_verifier.rs1use std::collections::HashMap;
5
6use crate::error::{BridgeError, BridgeResult};
7use crate::server::handler::ActionVerifier;
8use crate::types::{BridgeAction, BridgeActionDigest};
9
10#[derive(Debug)]
11pub struct GovernanceVerifier {
12 approved_goverance_actions: HashMap<BridgeActionDigest, BridgeAction>,
13}
14
15impl GovernanceVerifier {
16 pub fn new(approved_actions: Vec<BridgeAction>) -> BridgeResult<Self> {
17 let mut approved_goverance_actions = HashMap::new();
19 for action in approved_actions {
20 if !action.is_governace_action() {
21 return Err(BridgeError::ActionIsNotGovernanceAction(action));
22 }
23 approved_goverance_actions.insert(action.digest(), action);
24 }
25 Ok(Self {
26 approved_goverance_actions,
27 })
28 }
29}
30
31#[async_trait::async_trait]
32impl ActionVerifier<BridgeAction> for GovernanceVerifier {
33 fn name(&self) -> &'static str {
34 "GovernanceVerifier"
35 }
36
37 async fn verify(&self, key: BridgeAction) -> BridgeResult<BridgeAction> {
38 if !key.is_governace_action() {
40 return Err(BridgeError::ActionIsNotGovernanceAction(key));
41 }
42 if let Some(approved_action) = self.approved_goverance_actions.get(&key.digest()) {
43 assert_eq!(
44 &key, approved_action,
45 "Mismatched action found in approved_actions"
46 );
47 return Ok(key);
48 }
49 return Err(BridgeError::GovernanceActionIsNotApproved);
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use crate::{
57 test_utils::get_test_sui_to_eth_bridge_action,
58 types::{BridgeAction, EmergencyAction, EmergencyActionType, LimitUpdateAction},
59 };
60 use sui_types::bridge::BridgeChainId;
61
62 #[tokio::test]
63 async fn test_governance_verifier() {
64 let action_1 = BridgeAction::EmergencyAction(EmergencyAction {
65 chain_id: BridgeChainId::EthCustom,
66 nonce: 1,
67 action_type: EmergencyActionType::Pause,
68 });
69 let action_2 = BridgeAction::LimitUpdateAction(LimitUpdateAction {
70 chain_id: BridgeChainId::EthCustom,
71 sending_chain_id: BridgeChainId::SuiCustom,
72 nonce: 1,
73 new_usd_limit: 10000,
74 });
75
76 let verifier = GovernanceVerifier::new(vec![action_1.clone(), action_2.clone()]).unwrap();
77 assert_eq!(
78 verifier.verify(action_1.clone()).await.unwrap(),
79 action_1.clone()
80 );
81 assert_eq!(
82 verifier.verify(action_2.clone()).await.unwrap(),
83 action_2.clone()
84 );
85
86 let action_3 = BridgeAction::LimitUpdateAction(LimitUpdateAction {
87 chain_id: BridgeChainId::EthCustom,
88 sending_chain_id: BridgeChainId::SuiCustom,
89 nonce: 2,
90 new_usd_limit: 10000,
91 });
92 assert_eq!(
93 verifier.verify(action_3).await.unwrap_err(),
94 BridgeError::GovernanceActionIsNotApproved
95 );
96
97 let action_4 = get_test_sui_to_eth_bridge_action(None, None, None, None, None, None, None);
99 assert!(matches!(
100 GovernanceVerifier::new(vec![action_1, action_2, action_4.clone()]).unwrap_err(),
101 BridgeError::ActionIsNotGovernanceAction(..)
102 ));
103
104 assert!(matches!(
106 verifier.verify(action_4).await.unwrap_err(),
107 BridgeError::ActionIsNotGovernanceAction(..)
108 ));
109 }
110}