Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Determine Participant Id automatically #400

Merged
merged 24 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 59 additions & 92 deletions contract/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,65 +1,38 @@
pub mod primitives;

use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::serde::{Deserialize, Serialize};
use near_sdk::{env, near_bindgen, AccountId, PanicOnDefault, PublicKey};
use primitives::{CandidateInfo, Candidates, ParticipantInfo, Participants, PkVotes, Votes};
use std::collections::{BTreeMap, HashSet};

type ParticipantId = u32;

pub mod hpke {
pub type PublicKey = [u8; 32];
}

#[derive(
Serialize,
Deserialize,
BorshDeserialize,
BorshSerialize,
Clone,
Hash,
PartialEq,
Eq,
PartialOrd,
Ord,
Debug,
)]
pub struct ParticipantInfo {
pub id: ParticipantId,
pub account_id: AccountId,
pub url: String,
/// The public key used for encrypting messages.
pub cipher_pk: hpke::PublicKey,
/// The public key used for verifying messages.
pub sign_pk: PublicKey,
}

#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Debug)]
pub struct InitializingContractState {
pub participants: BTreeMap<AccountId, ParticipantInfo>,
pub participants: Participants,
pub threshold: usize,
pub pk_votes: BTreeMap<PublicKey, HashSet<ParticipantId>>,
pub pk_votes: PkVotes,
}

#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Debug)]
pub struct RunningContractState {
pub epoch: u64,
// TODO: why is this account id for participants instead of participant id?
pub participants: BTreeMap<AccountId, ParticipantInfo>,
pub participants: Participants,
pub threshold: usize,
pub public_key: PublicKey,
pub candidates: BTreeMap<ParticipantId, ParticipantInfo>,
pub join_votes: BTreeMap<ParticipantId, HashSet<ParticipantId>>,
pub leave_votes: BTreeMap<ParticipantId, HashSet<ParticipantId>>,
pub candidates: Candidates,
pub join_votes: Votes,
pub leave_votes: Votes,
}

#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Debug)]
pub struct ResharingContractState {
pub old_epoch: u64,
pub old_participants: BTreeMap<AccountId, ParticipantInfo>,
pub old_participants: Participants,
// TODO: only store diff to save on storage
pub new_participants: BTreeMap<AccountId, ParticipantInfo>,
pub new_participants: Participants,
pub threshold: usize,
pub public_key: PublicKey,
pub finished_votes: HashSet<ParticipantId>,
pub finished_votes: HashSet<AccountId>,
}

#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Debug)]
Expand All @@ -82,9 +55,9 @@ impl MpcContract {
pub fn init(threshold: usize, participants: BTreeMap<AccountId, ParticipantInfo>) -> Self {
MpcContract {
protocol_state: ProtocolContractState::Initializing(InitializingContractState {
participants,
participants: Participants { participants },
threshold,
pk_votes: BTreeMap::new(),
pk_votes: PkVotes::new(),
}),
}
}
Expand All @@ -95,9 +68,8 @@ impl MpcContract {

pub fn join(
&mut self,
participant_id: ParticipantId,
url: String,
cipher_pk: hpke::PublicKey,
cipher_pk: primitives::hpke::PublicKey,
sign_pk: PublicKey,
) {
match &mut self.protocol_state {
Expand All @@ -106,15 +78,14 @@ impl MpcContract {
candidates,
..
}) => {
let account_id = env::signer_account_id();
if participants.contains_key(&account_id) {
let signer_account_id = env::signer_account_id();
if participants.contains_key(&signer_account_id) {
env::panic_str("this participant is already in the participant set");
}
candidates.insert(
participant_id,
ParticipantInfo {
id: participant_id,
account_id,
signer_account_id.clone(),
CandidateInfo {
account_id: signer_account_id,
url,
cipher_pk,
sign_pk,
Expand All @@ -125,7 +96,7 @@ impl MpcContract {
}
}

pub fn vote_join(&mut self, participant: ParticipantId) -> bool {
pub fn vote_join(&mut self, candidate_account_id: AccountId) -> bool {
match &mut self.protocol_state {
ProtocolContractState::Running(RunningContractState {
epoch,
Expand All @@ -136,19 +107,19 @@ impl MpcContract {
join_votes,
..
}) => {
let voting_participant = participants
.get(&env::signer_account_id())
.unwrap_or_else(|| {
env::panic_str("calling account is not in the participant set")
});
let candidate = candidates
.get(&participant)
let signer_account_id = env::signer_account_id();
if !participants.contains_key(&signer_account_id) {
env::panic_str("calling account is not in the participant set");
}
let candidate_info = candidates
.get(&candidate_account_id)
.unwrap_or_else(|| env::panic_str("candidate is not registered"));
let voted = join_votes.entry(participant).or_default();
voted.insert(voting_participant.id);
let voted = join_votes.entry(candidate_account_id.clone());
voted.insert(signer_account_id);
if voted.len() >= *threshold {
let mut new_participants = participants.clone();
new_participants.insert(candidate.account_id.clone(), candidate.clone());
new_participants
.insert(candidate_account_id.clone(), candidate_info.clone().into());
self.protocol_state =
ProtocolContractState::Resharing(ResharingContractState {
old_epoch: *epoch,
Expand All @@ -167,30 +138,28 @@ impl MpcContract {
}
}

pub fn vote_leave(&mut self, participant: ParticipantId) -> bool {
pub fn vote_leave(&mut self, acc_id_to_leave: AccountId) -> bool {
match &mut self.protocol_state {
ProtocolContractState::Running(RunningContractState {
epoch,
participants,
threshold,
public_key,
candidates,
leave_votes,
..
}) => {
let voting_participant = participants
.get(&env::signer_account_id())
.unwrap_or_else(|| {
env::panic_str("calling account is not in the participant set")
});
let candidate = candidates
.get(&participant)
.unwrap_or_else(|| env::panic_str("candidate is not registered"));
let voted = leave_votes.entry(participant).or_default();
voted.insert(voting_participant.id);
let signer_account_id = env::signer_account_id();
if !participants.contains_key(&signer_account_id) {
env::panic_str("calling account is not in the participant set");
}
if !participants.contains_key(&acc_id_to_leave) {
env::panic_str("account to leave is not in the participant set");
}
let voted = leave_votes.entry(acc_id_to_leave.clone());
voted.insert(signer_account_id);
if voted.len() >= *threshold {
let mut new_participants = participants.clone();
new_participants.remove(&candidate.account_id);
new_participants.remove(&acc_id_to_leave);
self.protocol_state =
ProtocolContractState::Resharing(ResharingContractState {
old_epoch: *epoch,
Expand All @@ -216,22 +185,21 @@ impl MpcContract {
threshold,
pk_votes,
}) => {
let voting_participant = participants
.get(&env::signer_account_id())
.unwrap_or_else(|| {
env::panic_str("calling account is not in the participant set")
});
let voted = pk_votes.entry(public_key.clone()).or_default();
voted.insert(voting_participant.id);
let signer_account_id = env::signer_account_id();
if !participants.contains_key(&signer_account_id) {
env::panic_str("calling account is not in the participant set");
}
let voted = pk_votes.entry(public_key.clone());
voted.insert(signer_account_id);
if voted.len() >= *threshold {
self.protocol_state = ProtocolContractState::Running(RunningContractState {
epoch: 0,
participants: participants.clone(),
threshold: *threshold,
public_key,
candidates: BTreeMap::new(),
join_votes: BTreeMap::new(),
leave_votes: BTreeMap::new(),
candidates: Candidates::new(),
join_votes: Votes::new(),
leave_votes: Votes::new(),
});
true
} else {
Expand All @@ -257,21 +225,20 @@ impl MpcContract {
if *old_epoch + 1 != epoch {
env::panic_str("mismatched epochs");
}
let voting_participant = old_participants
.get(&env::signer_account_id())
.unwrap_or_else(|| {
env::panic_str("calling account is not in the old participant set")
});
finished_votes.insert(voting_participant.id);
let signer_account_id = env::signer_account_id();
if !old_participants.contains_key(&signer_account_id) {
env::panic_str("calling account is not in the old participant set");
}
finished_votes.insert(signer_account_id);
if finished_votes.len() >= *threshold {
self.protocol_state = ProtocolContractState::Running(RunningContractState {
epoch: *old_epoch + 1,
participants: new_participants.clone(),
threshold: *threshold,
public_key: public_key.clone(),
candidates: BTreeMap::new(),
join_votes: BTreeMap::new(),
leave_votes: BTreeMap::new(),
candidates: Candidates::new(),
join_votes: Votes::new(),
leave_votes: Votes::new(),
});
true
} else {
Expand Down
Loading
Loading