diff --git a/cli/lib/lib.rs b/cli/lib/lib.rs index d368653..98330ed 100644 --- a/cli/lib/lib.rs +++ b/cli/lib/lib.rs @@ -21,6 +21,8 @@ use liminal_ark_relations::{ pub mod helpers; +pub const MAJORITY_OF_VOTES: f32 = 0.70; // 70% + pub struct PublicVote { pub id: AccountId, pub pub_key: Vec, @@ -102,13 +104,13 @@ pub fn prepare_counting_inputs( } } - let votes_minimum: u8 = (0.75 * votes.len() as f32).ceil() as u8; + let votes_minimum: u8 = (MAJORITY_OF_VOTES * votes.len() as f32).ceil() as u8; let votes_maximum: u8 = votes.len() as u8 - votes_minimum; let verdict = if sum_votes >= votes_minimum { jurors_banned = jurors_banned .iter() .enumerate() - .filter(|&(index, _)| decoded_votes[index] != 0) + .filter(|&(index, _)| decoded_votes[index] == 0) .map(|(_, &ref value)| value.clone()) .collect(); VerdictRelation::Positive @@ -116,7 +118,7 @@ pub fn prepare_counting_inputs( jurors_banned = jurors_banned .iter() .enumerate() - .filter(|&(index, _)| decoded_votes[index] == 0) + .filter(|&(index, _)| decoded_votes[index] == 1) .map(|(_, &ref value)| value.clone()) .collect(); VerdictRelation::Negative diff --git a/cli/src/bright_disputes.rs b/cli/src/bright_disputes.rs index 3f6c1be..9b22527 100644 --- a/cli/src/bright_disputes.rs +++ b/cli/src/bright_disputes.rs @@ -366,19 +366,4 @@ impl BrightDisputes { Ok(()) } - - /// Calls 'distribute_deposit' of the contract. - pub async fn distribute_deposit( - &self, - connection: &SignedConnection, - dispute_id: u32, - ) -> Result<()> { - let ink_contract: Instance = (&self.contract).into(); - - connection - .exec(ink_contract.distribute_deposit(dispute_id)) - .await?; - - Ok(()) - } } diff --git a/cli/src/bright_disputes_ink.rs b/cli/src/bright_disputes_ink.rs index 3103470..24ab507 100644 --- a/cli/src/bright_disputes_ink.rs +++ b/cli/src/bright_disputes_ink.rs @@ -4,8 +4,8 @@ use scale::Encode as _; #[allow(dead_code)] pub const CODE_HASH: [u8; 32] = [ - 134, 34, 57, 191, 103, 29, 44, 56, 12, 231, 88, 242, 64, 144, 53, 34, 131, 56, 170, 238, 192, - 142, 229, 240, 82, 204, 67, 181, 250, 239, 22, 82, + 172, 163, 202, 102, 55, 35, 203, 88, 236, 195, 156, 213, 90, 245, 231, 194, 112, 179, 169, 197, + 90, 183, 142, 26, 36, 52, 220, 49, 44, 93, 167, 79, ]; #[derive(Debug, Clone, PartialEq, Eq, scale::Encode, scale::Decode)] @@ -225,7 +225,7 @@ impl Instance { ink_wrapper_types::ReadCall::new(self.account_id, data) } - /// Get single dispute by id + /// Remove single dispute by id #[allow(dead_code, clippy::too_many_arguments)] pub fn remove_dispute(&self, dispute_id: u32) -> ink_wrapper_types::ExecCall { let data = { @@ -405,17 +405,6 @@ impl Instance { ink_wrapper_types::ExecCall::new(self.account_id, data) } - /// Judge can confirm his participation in dispute - #[allow(dead_code, clippy::too_many_arguments)] - pub fn distribute_deposit(&self, dispute_id: u32) -> ink_wrapper_types::ExecCall { - let data = { - let mut data = vec![117, 233, 246, 239]; - dispute_id.encode_to(&mut data); - data - }; - ink_wrapper_types::ExecCall::new(self.account_id, data) - } - /// Register a verification key. #[allow(dead_code, clippy::too_many_arguments)] pub fn register_vk(&self, relation: Relation, vk: Vec) -> ink_wrapper_types::ExecCall { diff --git a/cli/src/config.rs b/cli/src/config.rs index ef7934e..31a5232 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -99,9 +99,4 @@ pub enum ContractCmd { caller_account: String, dispute_id: u32, }, - /// Distribute dispute deposit - DistributeDeposit { - caller_account: String, - dispute_id: u32, - }, } diff --git a/cli/src/main.rs b/cli/src/main.rs index 16e00d5..d116ca1 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -26,7 +26,7 @@ use crate::{ Command, Config, ContractCmd::{ ConfirmDefendant, ConfirmJudgeParticipation, ConfirmJurorParticipation, CountTheVotes, - CreateDispute, DistributeDeposit, GetDispute, GetDisputeFull, ProcessDisputeRound, + CreateDispute, GetDispute, GetDisputeFull, ProcessDisputeRound, RegisterAsAnActiveJuror, UnregisterAsAnActiveJuror, UpdateDefendantDescription, UpdateOwnerDescription, Vote, }, @@ -284,18 +284,6 @@ async fn handle_contract_command( dispute_state ); } - DistributeDeposit { - caller_account, - dispute_id, - } => { - let account = keypair_from_string(&caller_account); - let signed_connection = SignedConnection::from_connection(connection, account.clone()); - - bright_dispute - .distribute_deposit(&signed_connection, dispute_id) - .await?; - info!("Deposit distributed successfully!"); - } } Ok(()) } diff --git a/contract/src/contract.rs b/contract/src/contract.rs index fc1ef82..b44fe71 100755 --- a/contract/src/contract.rs +++ b/contract/src/contract.rs @@ -145,7 +145,7 @@ pub mod bright_disputes { self.juries_pool.clone() } - /// Get single dispute by id + /// Remove single dispute by id #[ink(message)] pub fn remove_dispute(&mut self, dispute_id: DisputeId) -> Result<()> { let dispute = self.get_dispute_or_assert(dispute_id)?; @@ -379,10 +379,12 @@ pub mod bright_disputes { // End the dispute match verdict { Verdict::Positive => { - dispute.end_dispute(DisputeResult::Owner, jurors_banned)?; + dispute.end_dispute(Some(DisputeResult::Owner), jurors_banned)?; + self.distribute_deposit(&mut dispute)?; } Verdict::Negative => { - dispute.end_dispute(DisputeResult::Defendant, jurors_banned)?; + dispute.end_dispute(Some(DisputeResult::Defendant), jurors_banned)?; + self.distribute_deposit(&mut dispute)?; } Verdict::None => { // Check if juries votes @@ -394,9 +396,12 @@ pub mod bright_disputes { } // Dispute round ended, but the majority of votes is not reached. - let timestamp = self.env().block_timestamp(); - if let Err(_) = dispute.next_dispute_round(timestamp) { - return Err(BrightDisputesError::MajorityOfVotesNotReached); + match dispute.next_dispute_round(self.env().block_timestamp()) { + Err(BrightDisputesError::DisputeRoundLimitReached) => { + dispute.end_dispute(None, jurors_banned)?; + self.distribute_deposit(&mut dispute)?; + } + _ => (), } } }; @@ -418,6 +423,7 @@ pub mod bright_disputes { let judge = self.get_juror_or_assert(judge_id)?; if judge.is_requested_for_action(dispute_id) { dispute.move_to_banned(judge_id)?; + } else { self.add_to_juries_pool(judge_id)?; } } @@ -427,6 +433,7 @@ pub mod bright_disputes { let juror = self.get_juror_or_assert(juror_id)?; if juror.is_requested_for_action(dispute_id) { dispute.move_to_banned(juror_id)?; + } else { self.add_to_juries_pool(juror_id)?; } } @@ -445,49 +452,6 @@ pub mod bright_disputes { Ok(()) } - /// Judge can confirm his participation in dispute - #[ink(message)] - pub fn distribute_deposit(&mut self, dispute_id: DisputeId) -> Result<()> { - let mut dispute = self.get_dispute_or_assert(dispute_id)?; - - // Check if dispute has ended. - dispute.assert_dispute_ended()?; - - // Close dispute - dispute.close_dispute()?; - self.update_dispute(dispute.clone()); - - let mut accounts: Vec = Vec::new(); - - // Add owner - accounts.push(dispute.owner()); - - // Add defendant, only if he confirmed dispute - if dispute.has_defendant_confirmed_dispute() { - accounts.push(dispute.defendant()); - } - - // Add judge - if let Some(judge_id) = dispute.judge() { - accounts.push(judge_id); - self.add_to_juries_pool(judge_id)?; - } - - // Add juries, who were not banned. - for juror_id in dispute.juries() { - accounts.push(juror_id); - self.add_to_juries_pool(juror_id)?; - } - - // Split deposit and transfer founds. - let founds = dispute.deposit() / accounts.len() as Balance; - for account in accounts { - self.env().transfer(account, founds)?; - } - - Ok(()) - } - /// Register a verification key. #[ink(message)] pub fn register_vk(&mut self, relation: Relation, vk: Vec) -> Result<()> { @@ -554,10 +518,8 @@ pub mod bright_disputes { fn add_to_juries_pool(&mut self, juror_id: AccountId) -> Result<()> { self.assert_juror_not_in_pool(juror_id)?; - let juror = self - .juries - .get(juror_id) - .unwrap_or_else(|| Juror::create(juror_id)); + + let juror = Juror::create(juror_id); self.juries_pool.push(juror.id()); self.update_juror(juror); Ok(()) @@ -590,6 +552,49 @@ pub mod bright_disputes { Ok(juries) } + /// Finish the dispute and distribute the deposit. + fn distribute_deposit(&mut self, dispute: &mut Dispute) -> Result<()> { + // Check if dispute has ended. + dispute.assert_dispute_ended()?; + + // Close dispute + dispute.close_dispute()?; + + let mut accounts: Vec = Vec::new(); + + // Add judge + if let Some(judge_id) = dispute.judge() { + accounts.push(judge_id.clone()); + self.add_to_juries_pool(judge_id)?; + } + + // Add juries, who were not banned. + for juror_id in dispute.juries() { + accounts.push(juror_id.clone()); + self.add_to_juries_pool(juror_id)?; + } + + // If the dispute reaches the maximum number of rounds, + // and the majority of votes isn't reached, return the + // deposit to the Owner and Defendant as well. + if dispute.get_dispute_result().is_none() { + // Add owner + accounts.push(dispute.owner()); + + // Add defendant, only if he confirmed dispute + if dispute.has_defendant_confirmed_dispute() { + accounts.push(dispute.defendant()); + } + } + + // Split deposit and transfer founds. + let founds = dispute.deposit() / accounts.len() as Balance; + for account in accounts { + self.env().transfer(account, founds)?; + } + Ok(()) + } + fn serialize(t: &T) -> Vec { let mut bytes = vec![0; t.serialized_size()]; t.serialize(&mut bytes[..]).expect("Failed to serialize"); @@ -814,6 +819,9 @@ pub mod bright_disputes { let result = bright_disputes.remove_dispute(1); assert_eq!(result, Ok(())); + let result = bright_disputes.remove_dispute(1); + assert_eq!(result, Err(BrightDisputesError::DisputeNotExist)); + // Failed to remove dispute in "Running" state. let mut bright_disputes = create_test_bright_dispute_with_running_dispute(); let result = bright_disputes.remove_dispute(1); @@ -824,7 +832,7 @@ pub mod bright_disputes { .get_dispute(1) .expect("Failed to get dispute!"); dispute - .end_dispute(DisputeResult::Owner, vec![]) + .end_dispute(Some(DisputeResult::Owner), vec![]) .expect("Failed to end dispute!"); bright_disputes.update_dispute(dispute.clone()); let result = bright_disputes.remove_dispute(1); @@ -1375,107 +1383,5 @@ pub mod bright_disputes { let result = bright_disputes.process_dispute_round(dispute_id); assert_eq!(result, Err(BrightDisputesError::JuriesPoolIsToSmall)); } - - // Check deposit distribution - #[ink::test] - fn distribute_deposit() { - mock::register_chain_extensions(()); - - let accounts = ink::env::test::default_accounts::(); - set_caller::(accounts.alice); - - let mut bright_disputes = create_test_bright_dispute_with_running_dispute(); - let dispute_id = 1; - - // Register charlie, eve, frank and django as a juries. - register_valid_juries(&mut bright_disputes); - - // Switch to "PickingJuriesAndJudge" state. - set_caller::(accounts.alice); - bright_disputes - .process_dispute_round(dispute_id) - .expect("Failed to create a dispute!"); - - let dispute = bright_disputes - .get_dispute(dispute_id) - .expect("Failed to get dispute!"); - - // Assign all juries - let assigned_juries = dispute.juries(); - - let mut juror_not_assigned = vec![ - accounts.charlie, - accounts.eve, - accounts.frank, - accounts.django, - ]; - juror_not_assigned.retain(|id| !assigned_juries.contains(id)); - - // Confirm juror participation - set_caller::(assigned_juries[0]); - bright_disputes - .confirm_juror_participation_in_dispute(dispute_id, vec![]) - .expect("Failed confirm juries participation!"); - - set_caller::(assigned_juries[1]); - bright_disputes - .confirm_juror_participation_in_dispute(dispute_id, vec![]) - .expect("Failed confirm juries participation!"); - - set_caller::(assigned_juries[2]); - bright_disputes - .confirm_juror_participation_in_dispute(dispute_id, vec![]) - .expect("Failed confirm juries participation!"); - - // Assign judge - set_caller::(juror_not_assigned[0]); - bright_disputes - .confirm_judge_participation_in_dispute(dispute_id, vec![]) - .expect("Failed to confirm judge participation!"); - - // Switch state to "Voting" state - set_caller::(accounts.alice); - bright_disputes - .process_dispute_round(dispute_id) - .expect("Failed process dispute round!"); - - // Juries voting - set_caller::(assigned_juries[0]); - bright_disputes - .vote(dispute_id, [0u64; 4], [0u64; 4], vec![]) - .expect("Failed to vote"); - set_caller::(assigned_juries[1]); - bright_disputes - .vote(dispute_id, [0u64; 4], [0u64; 4], vec![]) - .expect("Failed to vote"); - set_caller::(assigned_juries[2]); - bright_disputes - .vote(dispute_id, [0u64; 4], [0u64; 4], vec![]) - .expect("Failed to vote"); - - // Switch state to CountingTheVotes - set_caller::(accounts.alice); - bright_disputes - .process_dispute_round(dispute_id) - .expect("Failed process dispute round!"); - - // Count the votes, dispute ends - set_caller::(juror_not_assigned[0]); - bright_disputes - .issue_the_verdict( - dispute_id, - 0, - 0, - Verdict::Positive, - [0u64; 4], - vec![], - vec![], - ) - .expect("Failed process dispute round!"); - - set_caller::(accounts.bob); - let result = bright_disputes.distribute_deposit(dispute_id); - assert_eq!(result, Ok(())); - } } } diff --git a/contract/src/dispute.rs b/contract/src/dispute.rs index db4eaf6..d12c257 100644 --- a/contract/src/dispute.rs +++ b/contract/src/dispute.rs @@ -33,15 +33,6 @@ pub enum DisputeResult { Defendant, } -impl DisputeResult { - pub fn opposite(&self) -> DisputeResult { - if *self == DisputeResult::Owner { - return DisputeResult::Defendant; - } - return DisputeResult::Owner; - } -} - #[derive(Clone, Debug, PartialEq, scale::Decode, scale::Encode)] #[cfg_attr( feature = "std", @@ -69,7 +60,7 @@ pub struct Dispute { } impl Dispute { - const MAX_DISPUTE_ROUNDS: u8 = 3u8; + const MAX_DISPUTE_ROUNDS: u8 = 4u8; const INCREMENT_JURIES_BY: u8 = 2; /// Creates a new dispute @@ -90,7 +81,7 @@ impl Dispute { defendant_link: None, dispute_result: None, dispute_round: None, - dispute_round_counter: 0u8, + dispute_round_counter: 1u8, judge: None, juries: Vec::new(), banned: Vec::new(), @@ -190,7 +181,7 @@ impl Dispute { /// End the dispute and publish result. pub fn end_dispute( &mut self, - result: DisputeResult, + result: Option, juries_to_ban: Vec, ) -> Result<()> { self.assert_state(DisputeState::Running)?; @@ -199,7 +190,7 @@ impl Dispute { } self.state = DisputeState::Ended; - self.dispute_result = Some(result.clone()); + self.dispute_result = result; self.dispute_round = None; // Move juries who wrongly voted to banned list @@ -492,7 +483,7 @@ mod tests { assert_eq!(dispute.defendant, accounts.bob); assert_eq!(dispute.defendant_link, None); assert_eq!(dispute.dispute_result, None); - assert_eq!(dispute.dispute_round_counter, 0u8); + assert_eq!(dispute.dispute_round_counter, 1u8); assert_eq!(dispute.judge, None); assert_eq!(dispute.juries.len(), 0); assert_eq!(dispute.banned.len(), 0); @@ -816,7 +807,7 @@ mod tests { fn next_dispute_round_limit() { let mut dispute = default_test_running_dispute(); - for _ in 0..Dispute::MAX_DISPUTE_ROUNDS { + for _ in 1..Dispute::MAX_DISPUTE_ROUNDS { let result = dispute.next_dispute_round(0u64); assert_eq!(result, Ok(())); } @@ -873,7 +864,7 @@ mod tests { set_caller::(accounts.alice); let mut dispute = Dispute::create(1, "".into(), accounts.bob, 15); - let result = dispute.end_dispute(DisputeResult::Owner, vec![]); + let result = dispute.end_dispute(Some(DisputeResult::Owner), vec![]); assert_eq!(result, Err(BrightDisputesError::InvalidDisputeState)); // Force "Voting" state @@ -898,7 +889,7 @@ mod tests { .vote(Vote::create(accounts.eve, [0u64; 4]), [0u64; 4]) .expect("Failed to vote!"); - let result = dispute.end_dispute(DisputeResult::Owner, vec![accounts.eve]); + let result = dispute.end_dispute(Some(DisputeResult::Owner), vec![accounts.eve]); assert_eq!(result, Ok(())); assert_eq!(dispute.banned().len(), 1); @@ -944,7 +935,7 @@ mod tests { let result = dispute.assert_dispute_ended(); assert_eq!(result, Ok(())); - dispute.dispute_round_counter = 0u8; + dispute.dispute_round_counter = 1u8; dispute.state = DisputeState::Ended; let result = dispute.assert_dispute_ended(); assert_eq!(result, Ok(())); diff --git a/doc/README_CLI.md b/doc/README_CLI.md index adf331d..bd5c353 100644 --- a/doc/README_CLI.md +++ b/doc/README_CLI.md @@ -118,8 +118,6 @@ which moves us to the next phase which is `Counting the Votes`. Now the role of ``` ../cli/target/release/bright_disputes_cli count-the-votes //Juror4 1 25,164,133,151,251,54,205,192,212,173,218,155,210,238,98,4,36,68,162,114,94,30,134,181,187,167,219,131,227,25,202,6 ``` -finally we can finish the dispute and distribute the deposit: -``` -../cli/target/release/bright_disputes_cli distribute-deposit //Owner 1 -``` +Once the judgment is issued, the dispute will automatically end and all funds will be distributed accordingly. + diff --git a/tests/bright_disputes.rs b/tests/bright_disputes.rs index 3103470..5774381 100644 --- a/tests/bright_disputes.rs +++ b/tests/bright_disputes.rs @@ -4,8 +4,8 @@ use scale::Encode as _; #[allow(dead_code)] pub const CODE_HASH: [u8; 32] = [ - 134, 34, 57, 191, 103, 29, 44, 56, 12, 231, 88, 242, 64, 144, 53, 34, 131, 56, 170, 238, 192, - 142, 229, 240, 82, 204, 67, 181, 250, 239, 22, 82, + 30, 161, 18, 63, 166, 82, 11, 106, 86, 19, 164, 51, 127, 97, 138, 215, 222, 180, 237, 3, 161, + 251, 174, 200, 203, 108, 93, 249, 37, 173, 226, 99, ]; #[derive(Debug, Clone, PartialEq, Eq, scale::Encode, scale::Decode)] @@ -172,6 +172,12 @@ impl ink_wrapper_types::EventSource for Instance { type Event = event::Event; } +#[allow(dead_code)] +pub fn upload() -> ink_wrapper_types::UploadCall { + let wasm = include_bytes!("../contract/target/ink/bright_disputes.wasm"); + ink_wrapper_types::UploadCall::new(wasm.to_vec(), CODE_HASH) +} + impl Instance { /// Constructor #[allow(dead_code, clippy::too_many_arguments)] @@ -405,17 +411,6 @@ impl Instance { ink_wrapper_types::ExecCall::new(self.account_id, data) } - /// Judge can confirm his participation in dispute - #[allow(dead_code, clippy::too_many_arguments)] - pub fn distribute_deposit(&self, dispute_id: u32) -> ink_wrapper_types::ExecCall { - let data = { - let mut data = vec![117, 233, 246, 239]; - dispute_id.encode_to(&mut data); - data - }; - ink_wrapper_types::ExecCall::new(self.account_id, data) - } - /// Register a verification key. #[allow(dead_code, clippy::too_many_arguments)] pub fn register_vk(&self, relation: Relation, vk: Vec) -> ink_wrapper_types::ExecCall { diff --git a/tests/bright_disputes_test.rs b/tests/bright_disputes_test.rs index b20ee65..cf5b808 100644 --- a/tests/bright_disputes_test.rs +++ b/tests/bright_disputes_test.rs @@ -704,7 +704,7 @@ async fn test_dispute_rounds() -> Result<()> { .expect("Failed to find judge!"); assert_eq!(juries_conn.len(), 3); assert_eq!(dispute.juries.len(), 3); - assert_eq!(dispute.dispute_round_counter, 0); + assert_eq!(dispute.dispute_round_counter, 1); assert_eq!( dispute.dispute_round.clone().unwrap().state, RoundState::PickingJuriesAndJudge() @@ -764,7 +764,7 @@ async fn test_dispute_rounds() -> Result<()> { .await?? .expect("Unable to get dispute!"); assert_eq!(dispute.juries.len(), 3); - assert_eq!(dispute.dispute_round_counter, 0); + assert_eq!(dispute.dispute_round_counter, 1); assert_eq!( dispute.dispute_round.clone().unwrap().state, RoundState::CountingTheVotes() @@ -818,7 +818,7 @@ async fn test_dispute_rounds() -> Result<()> { .expect("Unable to get dispute!"); assert_eq!(dispute.juries.len(), 3); assert_eq!(dispute.dispute_round.clone().unwrap().number_of_juries, 5); - assert_eq!(dispute.dispute_round_counter, 1); + assert_eq!(dispute.dispute_round_counter, 2); assert_eq!(dispute.state, DisputeState::Running()); assert!(dispute.dispute_result.is_none()); assert!(dispute.judge.is_some());