Skip to content
Open
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
19 changes: 17 additions & 2 deletions contracts/tholos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ pub struct RotationCancelled {
pub new_resolver: Address,
}

#[contractevent]
pub struct RotationVoted {
pub resolver: Address,
pub approve: bool,
pub yes_count: u32,
pub no_count: u32,
}

/// An in-flight single-slot committee rotation proposed by a current resolver.
/// Decided by a strict majority of the live committee via `vote_rotation`. Only
/// one may be open at a time. See `docs/src/ROTATION_DESIGN.md`.
Expand Down Expand Up @@ -534,9 +542,9 @@ impl Tholos {
}

if approve {
proposal.yes.push_back(resolver);
proposal.yes.push_back(resolver.clone());
} else {
proposal.no.push_back(resolver);
proposal.no.push_back(resolver.clone());
}

let n = committee.len();
Expand Down Expand Up @@ -589,6 +597,13 @@ impl Tholos {
env.storage()
.instance()
.set(&DataKey::RotationProposal, &proposal);
RotationVoted {
resolver,
approve,
yes_count: proposal.yes.len(),
no_count: proposal.no.len(),
}
.publish(&env);
Ok(None)
}

Expand Down
28 changes: 26 additions & 2 deletions contracts/tholos/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

use super::*;
use soroban_sdk::testutils::storage::{Instance as _, Persistent as _};
use soroban_sdk::testutils::{Address as _, Ledger, MockAuth, MockAuthInvoke};
use soroban_sdk::IntoVal;
use soroban_sdk::testutils::{Address as _, Events as _, Ledger, MockAuth, MockAuthInvoke};
use soroban_sdk::{Event as _, IntoVal};

const DEFAULT_BOND: i128 = 100;
const DEFAULT_WINDOW: u64 = 3600;
Expand Down Expand Up @@ -1434,6 +1434,30 @@ fn test_rotation_requires_majority_then_executes() {
assert_eq!(f.token.balance(&disputer), 1_100);
}

#[test]
fn test_rotation_vote_still_open_emits_rotation_voted_event() {
let f = Fixture::new();
f.client.propose_rotation(
&f.resolvers.get(0).unwrap(),
&f.resolvers.get(0).unwrap(),
&f.generate(),
);

let voter = f.resolvers.get(1).unwrap();
// One yes of three: not yet a majority (needs 2), proposal stays open.
let r = f.client.try_vote_rotation(&voter, &true);
assert_eq!(r, Ok(Ok(None)));

let expected = RotationVoted {
resolver: voter,
approve: true,
yes_count: 1,
no_count: 0,
}
.to_xdr(&f.env, &f.client.address);
assert_eq!(f.env.events().all().events(), &[expected][..]);
}

#[test]
fn test_rotation_vote_twice_fails() {
let f = Fixture::new();
Expand Down
Loading
Loading