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