use 0x1::option;
use 0x1::vector;
use 0x2::ecdsa_k1;
use 0x2::event;
use 0x2::tx_context;
use 0x2::vec_map;
use 0x2::vec_set;
use 0x3::sui_system;
use 0xb::crypto;
use 0xb::message;
Struct BlocklistValidatorEvent
struct BlocklistValidatorEvent has copy, drop
Struct BridgeCommittee
struct BridgeCommittee has store
Struct CommitteeUpdateEvent
struct CommitteeUpdateEvent has copy, drop
Struct CommitteeMemberUrlUpdateEvent
struct CommitteeMemberUrlUpdateEvent has copy, drop
Struct CommitteeMember
struct CommitteeMember has copy, drop, store
Click to openFields
-
sui_address: address
-
The Sui Address of the validator
-
bridge_pubkey_bytes: vector<u8>
-
The public key bytes of the bridge key
-
voting_power: u64
-
Voting power, values are voting power in the scale of 10000.
-
http_rest_url: vector<u8>
-
The HTTP REST URL the member's node listens to
it looks like b'https://127.0.0.1:9191'
-
blocklisted: bool
-
If this member is blocklisted
Struct CommitteeMemberRegistration
struct CommitteeMemberRegistration has copy, drop, store
Click to openFields
-
sui_address: address
-
The Sui Address of the validator
-
bridge_pubkey_bytes: vector<u8>
-
The public key bytes of the bridge key
-
http_rest_url: vector<u8>
-
The HTTP REST URL the member's node listens to
it looks like b'https://127.0.0.1:9191'
Constants
const ENotSystemAddress: u64 = 3;
const EInvalidSignature: u64 = 2;
const ECDSA_COMPRESSED_PUBKEY_LENGTH: u64 = 33;
const ECommitteeAlreadyInitiated: u64 = 7;
const EDuplicatePubkey: u64 = 8;
const EDuplicatedSignature: u64 = 1;
const EInvalidPubkeyLength: u64 = 6;
const ESenderIsNotInBridgeCommittee: u64 = 9;
const ESenderNotActiveValidator: u64 = 5;
const ESignatureBelowThreshold: u64 = 0;
const EValidatorBlocklistContainsUnknownKey: u64 = 4;
const SUI_MESSAGE_PREFIX: vector<u8> = [83, 85, 73, 95, 66, 82, 73, 68, 71, 69, 95, 77, 69, 83, 83, 65, 71, 69];
Function verify_signatures
public fun verify_signatures(self: &committee::BridgeCommittee, message: message::BridgeMessage, signatures: vector<vector<u8>>)
Click to openImplementation
public fun verify_signatures(
self: &BridgeCommittee,
message: BridgeMessage,
signatures: vector<vector<u8>>,
) {
let (mut i, signature_counts) = (0, vector::length(&signatures));
let mut seen_pub_key = vec_set::empty<vector<u8>>();
let required_voting_power = message.required_voting_power();
// add prefix to the message bytes
let mut message_bytes = SUI_MESSAGE_PREFIX;
message_bytes.append(message.serialize_message());
let mut threshold = 0;
while (i < signature_counts) {
let pubkey = ecdsa_k1::secp256k1_ecrecover(&signatures[i], &message_bytes, 0);
// check duplicate
// and make sure pub key is part of the committee
assert!(!seen_pub_key.contains(&pubkey), EDuplicatedSignature);
assert!(self.members.contains(&pubkey), EInvalidSignature);
// get committee signature weight and check pubkey is part of the committee
let member = &self.members[&pubkey];
if (!member.blocklisted) {
threshold = threshold + member.voting_power;
};
seen_pub_key.insert(pubkey);
i = i + 1;
};
assert!(threshold >= required_voting_power, ESignatureBelowThreshold);
}
Function create
public(friend) fun create(ctx: &tx_context::TxContext): committee::BridgeCommittee
Click to openImplementation
Function register
public(friend) fun register(self: &mut committee::BridgeCommittee, system_state: &mut sui_system::SuiSystemState, bridge_pubkey_bytes: vector<u8>, http_rest_url: vector<u8>, ctx: &tx_context::TxContext)
Click to openImplementation
public(package) fun register(
self: &mut BridgeCommittee,
system_state: &mut SuiSystemState,
bridge_pubkey_bytes: vector<u8>,
http_rest_url: vector<u8>,
ctx: &TxContext
) {
// We disallow registration after committee initiated in v1
assert!(self.members.is_empty(), ECommitteeAlreadyInitiated);
// Ensure pubkey is valid
assert!(bridge_pubkey_bytes.length() == ECDSA_COMPRESSED_PUBKEY_LENGTH, EInvalidPubkeyLength);
// sender must be the same sender that created the validator object, this is to prevent DDoS from non-validator actor.
let sender = ctx.sender();
let validators = system_state.active_validator_addresses();
assert!(validators.contains(&sender), ESenderNotActiveValidator);
// Sender is active validator, record the registration
// In case validator need to update the info
let registration = if (self.member_registrations.contains(&sender)) {
let registration = &mut self.member_registrations[&sender];
registration.http_rest_url = http_rest_url;
registration.bridge_pubkey_bytes = bridge_pubkey_bytes;
*registration
} else {
let registration = CommitteeMemberRegistration {
sui_address: sender,
bridge_pubkey_bytes,
http_rest_url,
};
self.member_registrations.insert(sender, registration);
registration
};
// check uniqueness of the bridge pubkey.
// `try_create_next_committee` will abort if bridge_pubkey_bytes are not unique and
// that will fail the end of epoch transaction (possibly "forever", well, we
// need to deploy proper validator changes to stop end of epoch from failing).
check_uniqueness_bridge_keys(self, bridge_pubkey_bytes);
emit(registration)
}
Function try_create_next_committee
public(friend) fun try_create_next_committee(self: &mut committee::BridgeCommittee, active_validator_voting_power: vec_map::VecMap<address, u64>, min_stake_participation_percentage: u64, ctx: &tx_context::TxContext)
Click to openImplementation
public(package) fun try_create_next_committee(
self: &mut BridgeCommittee,
active_validator_voting_power: VecMap<address, u64>,
min_stake_participation_percentage: u64,
ctx: &TxContext
) {
let mut i = 0;
let mut new_members = vec_map::empty();
let mut stake_participation_percentage = 0;
while (i < self.member_registrations.size()) {
// retrieve registration
let (_, registration) = self.member_registrations.get_entry_by_idx(i);
// Find validator stake amount from system state
// Process registration if it's active validator
let voting_power = active_validator_voting_power.try_get(®istration.sui_address);
if (voting_power.is_some()) {
let voting_power = voting_power.destroy_some();
stake_participation_percentage = stake_participation_percentage + voting_power;
let member = CommitteeMember {
sui_address: registration.sui_address,
bridge_pubkey_bytes: registration.bridge_pubkey_bytes,
voting_power: (voting_power as u64),
http_rest_url: registration.http_rest_url,
blocklisted: false,
};
new_members.insert(registration.bridge_pubkey_bytes, member)
};
i = i + 1;
};
// Make sure the new committee represent enough stakes, percentage are accurate to 2DP
if (stake_participation_percentage >= min_stake_participation_percentage) {
// Clear registrations
self.member_registrations = vec_map::empty();
// Store new committee info
self.members = new_members;
self.last_committee_update_epoch = ctx.epoch();
emit(CommitteeUpdateEvent {
members: new_members,
stake_participation_percentage
})
}
}
Function execute_blocklist
public(friend) fun execute_blocklist(self: &mut committee::BridgeCommittee, blocklist: message::Blocklist)
Click to openImplementation
public(package) fun execute_blocklist(self: &mut BridgeCommittee, blocklist: Blocklist) {
let blocklisted = blocklist.blocklist_type() != 1;
let eth_addresses = blocklist.blocklist_validator_addresses();
let list_len = eth_addresses.length();
let mut list_idx = 0;
let mut member_idx = 0;
let mut pub_keys = vector[];
while (list_idx < list_len) {
let target_address = ð_addresses[list_idx];
let mut found = false;
while (member_idx < self.members.size()) {
let (pub_key, member) = self.members.get_entry_by_idx_mut(member_idx);
let eth_address = crypto::ecdsa_pub_key_to_eth_address(pub_key);
if (*target_address == eth_address) {
member.blocklisted = blocklisted;
pub_keys.push_back(*pub_key);
found = true;
member_idx = 0;
break
};
member_idx = member_idx + 1;
};
assert!(found, EValidatorBlocklistContainsUnknownKey);
list_idx = list_idx + 1;
};
emit(BlocklistValidatorEvent {
blocklisted,
public_keys: pub_keys,
})
}
Function committee_members
public(friend) fun committee_members(self: &committee::BridgeCommittee): &vec_map::VecMap<vector<u8>, committee::CommitteeMember>
Click to openImplementation
Function update_node_url
public(friend) fun update_node_url(self: &mut committee::BridgeCommittee, new_url: vector<u8>, ctx: &tx_context::TxContext)
Click to openImplementation
Function check_uniqueness_bridge_keys
fun check_uniqueness_bridge_keys(self: &committee::BridgeCommittee, bridge_pubkey_bytes: vector<u8>)
Click to openImplementation
fun check_uniqueness_bridge_keys(self: &BridgeCommittee, bridge_pubkey_bytes: vector<u8>) {
let mut count = self.member_registrations.size();
// bridge_pubkey_bytes must be found once and once only
let mut bridge_key_found = false;
while (count > 0) {
count = count - 1;
let (_, registration) = self.member_registrations.get_entry_by_idx(count);
if (registration.bridge_pubkey_bytes == bridge_pubkey_bytes) {
assert!(!bridge_key_found, EDuplicatePubkey);
bridge_key_found = true; // bridge_pubkey_bytes found, we must not have another one
}
};
}