Skip to content

Commit

Permalink
[bridge 76/n] add events for committee.move (#17374)
Browse files Browse the repository at this point in the history
## Description 

This PR adds events for committee.move in bridge node.

## Test plan 

tests

---

## Release notes

Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.

For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.

- [ ] Protocol: 
- [ ] Nodes (Validators and Full nodes): 
- [ ] Indexer: 
- [ ] JSON-RPC: 
- [ ] GraphQL: 
- [ ] CLI: 
- [ ] Rust SDK:
  • Loading branch information
longbowlu authored and patrickkuo committed May 9, 2024
1 parent a3dbcf0 commit 8e00573
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 41 deletions.
3 changes: 2 additions & 1 deletion crates/sui-bridge/src/e2e_tests/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use sui_types::digests::TransactionDigest;
use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder;
use sui_types::transaction::{ObjectArg, TransactionData};
use sui_types::BRIDGE_PACKAGE_ID;
use sui_types::SUI_BRIDGE_OBJECT_ID;
use tokio::join;
use tokio::task::JoinHandle;

Expand Down Expand Up @@ -309,7 +310,7 @@ impl BridgeTestCluster {
.read_api()
.query_transaction_blocks(
SuiTransactionBlockResponseQuery {
filter: Some(TransactionFilter::InputObject(BRIDGE_PACKAGE_ID)),
filter: Some(TransactionFilter::InputObject(SUI_BRIDGE_OBJECT_ID)),
options: Some(SuiTransactionBlockResponseOptions::full_content()),
},
self.bridge_tx_cursor,
Expand Down
115 changes: 112 additions & 3 deletions crates/sui-bridge/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@

#![allow(non_upper_case_globals)]

use std::str::FromStr;

use crate::crypto::BridgeAuthorityPublicKey;
use crate::error::BridgeError;
use crate::error::BridgeResult;
use crate::types::BridgeAction;
Expand All @@ -20,11 +19,15 @@ use fastcrypto::encoding::Hex;
use move_core_types::language_storage::StructTag;
use once_cell::sync::OnceCell;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
use sui_json_rpc_types::SuiEvent;
use sui_types::base_types::SuiAddress;
use sui_types::bridge::BridgeChainId;

use sui_types::bridge::MoveTypeBridgeMessageKey;
use sui_types::bridge::MoveTypeCommitteeMember;
use sui_types::bridge::MoveTypeCommitteeMemberRegistration;
use sui_types::collection_types::VecMap;
use sui_types::crypto::ToFromBytes;
use sui_types::digests::TransactionDigest;
use sui_types::BRIDGE_PACKAGE_ID;

Expand Down Expand Up @@ -64,6 +67,20 @@ pub struct MoveTokenTransferAlreadyClaimed {
pub message_key: MoveTypeBridgeMessageKey,
}

// `CommitteeUpdateEvent` emitted in committee.move
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct MoveCommitteeUpdateEvent {
pub members: VecMap<Vec<u8>, MoveTypeCommitteeMember>,
pub stake_participation_percentage: u64,
}

// `BlocklistValidatorEvent` emitted in committee.move
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct MoveBlocklistValidatorEvent {
pub blocklisted: bool,
pub public_keys: Vec<Vec<u8>>,
}

// Sanitized version of MoveTokenDepositedEvent
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Hash)]
pub struct EmittedSuiToEthTokenBridgeV1 {
Expand Down Expand Up @@ -173,6 +190,53 @@ impl TryFrom<MoveTokenTransferAlreadyClaimed> for TokenTransferAlreadyClaimed {
}
}

// Sanitized version of MoveCommitteeUpdateEvent
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct CommitteeUpdate {
pub members: Vec<MoveTypeCommitteeMember>,
pub stake_participation_percentage: u64,
}

impl TryFrom<MoveCommitteeUpdateEvent> for CommitteeUpdate {
type Error = BridgeError;

fn try_from(event: MoveCommitteeUpdateEvent) -> BridgeResult<Self> {
let members = event
.members
.contents
.into_iter()
.map(|v| v.value)
.collect();
Ok(Self {
members,
stake_participation_percentage: event.stake_participation_percentage,
})
}
}

// Sanitized version of MoveBlocklistValidatorEvent
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct BlocklistValidatorEvent {
pub blocklisted: bool,
pub public_keys: Vec<BridgeAuthorityPublicKey>,
}

impl TryFrom<MoveBlocklistValidatorEvent> for BlocklistValidatorEvent {
type Error = BridgeError;

fn try_from(event: MoveBlocklistValidatorEvent) -> BridgeResult<Self> {
let public_keys = event.public_keys.into_iter().map(|bytes|
BridgeAuthorityPublicKey::from_bytes(&bytes).map_err(|e|
BridgeError::Generic(format!("Failed to convert MoveBlocklistValidatorEvent to BlocklistValidatorEvent. Failed to convert public key to BridgeAuthorityPublicKey: {:?}", e))
)
).collect::<BridgeResult<Vec<_>>>()?;
Ok(Self {
blocklisted: event.blocklisted,
public_keys,
})
}
}

impl TryFrom<MoveTokenDepositedEvent> for EmittedSuiToEthTokenBridgeV1 {
type Error = BridgeError;

Expand Down Expand Up @@ -232,6 +296,12 @@ crate::declare_events!(
TokenTransferClaimed(TokenTransferClaimed) => ("bridge::TokenTransferClaimed", MoveTokenTransferClaimed),
TokenTransferAlreadyApproved(TokenTransferAlreadyApproved) => ("bridge::TokenTransferAlreadyApproved", MoveTokenTransferAlreadyApproved),
TokenTransferAlreadyClaimed(TokenTransferAlreadyClaimed) => ("bridge::TokenTransferAlreadyClaimed", MoveTokenTransferAlreadyClaimed),
// No need to define a sanitized event struct for MoveTypeCommitteeMemberRegistration
// because the info provided by validators could be invalid
CommitteeMemberRegistration(MoveTypeCommitteeMemberRegistration) => ("committee::CommitteeMemberRegistration", MoveTypeCommitteeMemberRegistration),
CommitteeUpdateEvent(CommitteeUpdate) => ("committee::CommitteeUpdateEvent", MoveCommitteeUpdateEvent),
BlocklistValidator(BlocklistValidatorEvent) => ("committee::CommitteeUpdateEvent", MoveBlocklistValidatorEvent),

// Add new event types here. Format:
// EnumVariantName(Struct) => ("{module}::{event_struct}", CorrespondingMoveStruct)
);
Expand Down Expand Up @@ -289,13 +359,19 @@ impl SuiBridgeEvent {
SuiBridgeEvent::TokenTransferClaimed(_event) => None,
SuiBridgeEvent::TokenTransferAlreadyApproved(_event) => None,
SuiBridgeEvent::TokenTransferAlreadyClaimed(_event) => None,
SuiBridgeEvent::CommitteeMemberRegistration(_event) => None,
SuiBridgeEvent::CommitteeUpdateEvent(_event) => None,
SuiBridgeEvent::BlocklistValidator(_event) => None,
}
}
}

#[cfg(test)]
pub mod tests {
use std::collections::HashSet;

use super::*;
use crate::e2e_tests::test_utils::BridgeTestClusterBuilder;
use crate::types::BridgeAction;
use crate::types::SuiToEthBridgeAction;
use ethers::types::Address as EthAddress;
Expand Down Expand Up @@ -355,4 +431,37 @@ pub mod tests {
};
(event, bridge_action)
}

#[tokio::test]
async fn test_bridge_events_conversion() {
telemetry_subscribers::init_for_testing();
init_all_struct_tags();
let mut bridge_test_cluster = BridgeTestClusterBuilder::new()
.with_eth_env(true)
.with_bridge_cluster(false)
.build()
.await;

let events = bridge_test_cluster
.new_bridge_events(
HashSet::from_iter([
CommitteeMemberRegistration.get().unwrap().clone(),
CommitteeUpdateEvent.get().unwrap().clone(),
]),
false,
)
.await;
for event in events.iter() {
match SuiBridgeEvent::try_from_sui_event(event).unwrap().unwrap() {
SuiBridgeEvent::CommitteeMemberRegistration(_event) => (),
SuiBridgeEvent::CommitteeUpdateEvent(_event) => (),
_ => panic!(
"Expected CommitteeMemberRegistration or CommitteeUpdateEvent, got {:?}",
event
),
}
}

// TODO: trigger other events and make sure they are converted correctly
}
}
98 changes: 64 additions & 34 deletions crates/sui-bridge/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ use std::{
sync::Arc,
time::Duration,
};
use sui_types::{bridge::BRIDGE_MODULE_NAME, event::EventID, Identifier};
use sui_types::{
bridge::{BRIDGE_COMMITTEE_MODULE_NAME, BRIDGE_MODULE_NAME},
event::EventID,
Identifier,
};
use tokio::task::JoinHandle;
use tracing::info;

Expand Down Expand Up @@ -113,30 +117,35 @@ fn get_sui_modules_to_watch(
store: &std::sync::Arc<BridgeOrchestratorTables>,
sui_bridge_module_last_processed_event_id_override: Option<EventID>,
) -> HashMap<Identifier, Option<EventID>> {
let module_identifier = BRIDGE_MODULE_NAME.to_owned();
let sui_bridge_modules = vec![module_identifier.clone()];
let sui_bridge_modules = vec![
BRIDGE_MODULE_NAME.to_owned(),
BRIDGE_COMMITTEE_MODULE_NAME.to_owned(),
];
if let Some(cursor) = sui_bridge_module_last_processed_event_id_override {
info!("Overriding cursor for sui bridge modules to {:?}", cursor);
return HashMap::from_iter(
sui_bridge_modules
.iter()
.map(|module| (module.clone(), Some(cursor))),
);
}

let sui_bridge_module_stored_cursor = store
.get_sui_event_cursors(&sui_bridge_modules)
.expect("Failed to get eth sui event cursors from storage")[0];
.expect("Failed to get eth sui event cursors from storage");
let mut sui_modules_to_watch = HashMap::new();
match sui_bridge_module_last_processed_event_id_override {
Some(cursor) => {
for (module_identifier, cursor) in sui_bridge_modules
.iter()
.zip(sui_bridge_module_stored_cursor)
{
if cursor.is_none() {
info!(
"Overriding cursor for sui bridge module {} to {:?}. Stored cursor: {:?}",
module_identifier, cursor, sui_bridge_module_stored_cursor,
"No cursor found for sui bridge module {} in storage or config override, query start from the beginning.",
module_identifier
);
sui_modules_to_watch.insert(module_identifier, Some(cursor));
}
None => {
if sui_bridge_module_stored_cursor.is_none() {
info!(
"No cursor found for sui bridge module {} in storage or config override",
module_identifier
);
}
sui_modules_to_watch.insert(module_identifier, sui_bridge_module_stored_cursor);
}
};
sui_modules_to_watch.insert(module_identifier.clone(), cursor);
}
sui_modules_to_watch
}

Expand Down Expand Up @@ -257,14 +266,18 @@ mod tests {
let temp_dir = tempfile::tempdir().unwrap();

let store = BridgeOrchestratorTables::new(temp_dir.path());
let module = BRIDGE_MODULE_NAME.to_owned();
let bridge_module = BRIDGE_MODULE_NAME.to_owned();
let committee_module = BRIDGE_COMMITTEE_MODULE_NAME.to_owned();
// No override, no stored watermark, use None
let sui_modules_to_watch = get_sui_modules_to_watch(&store, None);
assert_eq!(
sui_modules_to_watch,
vec![(module.clone(), None)]
.into_iter()
.collect::<HashMap<_, _>>()
vec![
(bridge_module.clone(), None),
(committee_module.clone(), None)
]
.into_iter()
.collect::<HashMap<_, _>>()
);

// no stored watermark, use override
Expand All @@ -275,34 +288,51 @@ mod tests {
let sui_modules_to_watch = get_sui_modules_to_watch(&store, Some(override_cursor));
assert_eq!(
sui_modules_to_watch,
vec![(module.clone(), Some(override_cursor))]
.into_iter()
.collect::<HashMap<_, _>>()
vec![
(bridge_module.clone(), Some(override_cursor)),
(committee_module.clone(), Some(override_cursor))
]
.into_iter()
.collect::<HashMap<_, _>>()
);

// No override, found stored watermark, use stored watermark
// No override, found stored watermark for `bridge` module, use stored watermark for `bridge`
// and None for `committee`
let stored_cursor = EventID {
tx_digest: TransactionDigest::random(),
event_seq: 100,
};
store
.update_sui_event_cursor(module.clone(), stored_cursor)
.update_sui_event_cursor(bridge_module.clone(), stored_cursor)
.unwrap();
let sui_modules_to_watch = get_sui_modules_to_watch(&store, None);
assert_eq!(
sui_modules_to_watch,
vec![(module.clone(), Some(stored_cursor))]
.into_iter()
.collect::<HashMap<_, _>>()
vec![
(bridge_module.clone(), Some(stored_cursor)),
(committee_module.clone(), None)
]
.into_iter()
.collect::<HashMap<_, _>>()
);

// found stored watermark, use override
let stored_cursor = EventID {
tx_digest: TransactionDigest::random(),
event_seq: 100,
};
store
.update_sui_event_cursor(committee_module.clone(), stored_cursor)
.unwrap();
let sui_modules_to_watch = get_sui_modules_to_watch(&store, Some(override_cursor));
assert_eq!(
sui_modules_to_watch,
vec![(module.clone(), Some(override_cursor))]
.into_iter()
.collect::<HashMap<_, _>>()
vec![
(bridge_module.clone(), Some(override_cursor)),
(committee_module.clone(), Some(override_cursor))
]
.into_iter()
.collect::<HashMap<_, _>>()
);
}

Expand Down
1 change: 0 additions & 1 deletion crates/sui-bridge/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ impl BridgeAuthority {
}
}

// A static Bridge committee implementation
#[derive(Debug, Clone)]
pub struct BridgeCommittee {
members: BTreeMap<BridgeAuthorityPublicKeyBytes, BridgeAuthority>,
Expand Down
5 changes: 3 additions & 2 deletions crates/sui-types/src/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub type BridgeRecordDyanmicField = Field<
>;

pub const BRIDGE_MODULE_NAME: &IdentStr = ident_str!("bridge");
pub const BRIDGE_COMMITTEE_MODULE_NAME: &IdentStr = ident_str!("committee");
pub const BRIDGE_MESSAGE_MODULE_NAME: &IdentStr = ident_str!("message");
pub const BRIDGE_CREATE_FUNCTION_NAME: &IdentStr = ident_str!("create");
pub const BRIDGE_INIT_COMMITTEE_FUNCTION_NAME: &IdentStr = ident_str!("init_bridge_committee");
Expand Down Expand Up @@ -373,7 +374,7 @@ pub struct MoveTypeBridgeCommittee {

/// Rust version of the Move committee::CommitteeMemberRegistration type.
#[serde_as]
#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, Default)]
#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, Default, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct MoveTypeCommitteeMemberRegistration {
pub sui_address: SuiAddress,
Expand Down Expand Up @@ -408,7 +409,7 @@ pub struct BridgeTreasurySummary {

/// Rust version of the Move committee::CommitteeMember type.
#[serde_as]
#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, Default)]
#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, Default, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct MoveTypeCommitteeMember {
pub sui_address: SuiAddress,
Expand Down

0 comments on commit 8e00573

Please sign in to comment.