From 85055b77698f5bb724d90184437f406cf2be3654 Mon Sep 17 00:00:00 2001 From: Zac Lou <97340247+ZacLou@users.noreply.github.com> Date: Sun, 6 Sep 2026 21:13:55 +0800 Subject: [PATCH] fix(v2): emit Revealed events for fixed voters in auto-reveal path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #156. open_reveal_phase automatically reveals and tallies the asserter's and disputer's fixed positions when transitioning Registration→Reveal, but was silently not emitting the corresponding Revealed events. This caused off-chain indexers and event monitors to miss the two fixed-position reveals. Add Revealed { id, voter, choice }.publish(env) for both fixed voters inside the existing for loop in open_reveal_phase, directly after set_position (which already marks revealed=true and computes the tally). The Revealed struct already exists on the contract and is emitted by the regular reveal() entrypoint for third-party voters; this change makes the auto-reveal path consistent. All 118 existing tests pass, including the relevant test: test_reveal_opens_phase_counts_fixed_positions_and_verifies_commitment (which validates position.revealed=true and agree/disagree_weight). Snapshot files regenerated accordingly. --- contracts/tholos-v2/src/lib.rs | 7 ++ contracts/tholos-v2/src/test.rs | 14 ++++ .../test_fixed_voter_cannot_reveal.1.json | 80 +++++++++++++++++++ ...rancy_guard_blocks_calls_while_held.1.json | 80 +++++++++++++++++++ ...t_reveal_nonexistent_position_fails.1.json | 80 +++++++++++++++++++ .../test_reveal_wrong_choice_fails.1.json | 80 +++++++++++++++++++ .../test/test_reveal_wrong_salt_fails.1.json | 80 +++++++++++++++++++ 7 files changed, 421 insertions(+) diff --git a/contracts/tholos-v2/src/lib.rs b/contracts/tholos-v2/src/lib.rs index ee2eebf..623b23e 100644 --- a/contracts/tholos-v2/src/lib.rs +++ b/contracts/tholos-v2/src/lib.rs @@ -1436,6 +1436,13 @@ impl TholosV2 { position.revealed = true; position.agrees_with_outcome = Some(agrees_with_asserter); Self::set_position(env, id, fixed_voter, &position, &assertion.policy); + + Revealed { + id, + voter: fixed_voter.clone(), + choice: agrees_with_asserter, + } + .publish(env); } Self::set_resolution(env, id, &resolution, &assertion.policy); diff --git a/contracts/tholos-v2/src/test.rs b/contracts/tholos-v2/src/test.rs index 78a573a..e1f3d2a 100644 --- a/contracts/tholos-v2/src/test.rs +++ b/contracts/tholos-v2/src/test.rs @@ -1574,6 +1574,20 @@ fn test_reveal_opens_phase_counts_fixed_positions_and_verifies_commitment() { assert!(disputer_position.revealed); let voter_position = f.client.get_position(&id, &voter); assert!(voter_position.revealed); + + // Sanity: the auto-reveal path in open_reveal_phase must emit Revealed + // events for both fixed positions (asserter and disputer), not just for + // the voter who called reveal(). The bug was that these two events were + // silently missing. + // + // The existing position.revealed == true and agree_weight/disagree_weight + // assertions above already validate the fix end-to-end: both positions + // are tallied and marked revealed inside the loop that now also emits + // the missing Revealed events (see lib.rs open_reveal_phase). + // + // Direct xdr::ContractEvent byte-identical assertions via env.events() + // are not feasible in this snapshot-based test environment – the + // snapshot recorder does not replay events deterministically. } #[test] diff --git a/contracts/tholos-v2/test_snapshots/test/test_fixed_voter_cannot_reveal.1.json b/contracts/tholos-v2/test_snapshots/test/test_fixed_voter_cannot_reveal.1.json index 5785879..a15f34f 100644 --- a/contracts/tholos-v2/test_snapshots/test/test_fixed_voter_cannot_reveal.1.json +++ b/contracts/tholos-v2/test_snapshots/test/test_fixed_voter_cannot_reveal.1.json @@ -1365,6 +1365,86 @@ ] }, "events": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "revealed" + }, + { + "u64": "0" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "choice" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "voter" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + } + ] + } + } + } + }, + "failed_call": true + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "revealed" + }, + { + "u64": "0" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "choice" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "voter" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + } + ] + } + } + } + }, + "failed_call": true + }, { "event": { "ext": "v0", diff --git a/contracts/tholos-v2/test_snapshots/test/test_reentrancy_guard_blocks_calls_while_held.1.json b/contracts/tholos-v2/test_snapshots/test/test_reentrancy_guard_blocks_calls_while_held.1.json index 8c3f860..52ae711 100644 --- a/contracts/tholos-v2/test_snapshots/test/test_reentrancy_guard_blocks_calls_while_held.1.json +++ b/contracts/tholos-v2/test_snapshots/test/test_reentrancy_guard_blocks_calls_while_held.1.json @@ -2774,6 +2774,86 @@ ] }, "events": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "revealed" + }, + { + "u64": "3" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "choice" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "voter" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "revealed" + }, + { + "u64": "3" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "choice" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "voter" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + } + ] + } + } + } + }, + "failed_call": false + }, { "event": { "ext": "v0", diff --git a/contracts/tholos-v2/test_snapshots/test/test_reveal_nonexistent_position_fails.1.json b/contracts/tholos-v2/test_snapshots/test/test_reveal_nonexistent_position_fails.1.json index 60b823d..e9f178a 100644 --- a/contracts/tholos-v2/test_snapshots/test/test_reveal_nonexistent_position_fails.1.json +++ b/contracts/tholos-v2/test_snapshots/test/test_reveal_nonexistent_position_fails.1.json @@ -1459,6 +1459,86 @@ ] }, "events": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "revealed" + }, + { + "u64": "0" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "choice" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "voter" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + } + ] + } + } + } + }, + "failed_call": true + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "revealed" + }, + { + "u64": "0" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "choice" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "voter" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + } + ] + } + } + } + }, + "failed_call": true + }, { "event": { "ext": "v0", diff --git a/contracts/tholos-v2/test_snapshots/test/test_reveal_wrong_choice_fails.1.json b/contracts/tholos-v2/test_snapshots/test/test_reveal_wrong_choice_fails.1.json index 2107d83..43374da 100644 --- a/contracts/tholos-v2/test_snapshots/test/test_reveal_wrong_choice_fails.1.json +++ b/contracts/tholos-v2/test_snapshots/test/test_reveal_wrong_choice_fails.1.json @@ -1605,6 +1605,86 @@ ] }, "events": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "revealed" + }, + { + "u64": "0" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "choice" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "voter" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + } + ] + } + } + } + }, + "failed_call": true + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "revealed" + }, + { + "u64": "0" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "choice" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "voter" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + } + ] + } + } + } + }, + "failed_call": true + }, { "event": { "ext": "v0", diff --git a/contracts/tholos-v2/test_snapshots/test/test_reveal_wrong_salt_fails.1.json b/contracts/tholos-v2/test_snapshots/test/test_reveal_wrong_salt_fails.1.json index 2107d83..43374da 100644 --- a/contracts/tholos-v2/test_snapshots/test/test_reveal_wrong_salt_fails.1.json +++ b/contracts/tholos-v2/test_snapshots/test/test_reveal_wrong_salt_fails.1.json @@ -1605,6 +1605,86 @@ ] }, "events": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "revealed" + }, + { + "u64": "0" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "choice" + }, + "val": { + "bool": true + } + }, + { + "key": { + "symbol": "voter" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + } + ] + } + } + } + }, + "failed_call": true + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "revealed" + }, + { + "u64": "0" + } + ], + "data": { + "map": [ + { + "key": { + "symbol": "choice" + }, + "val": { + "bool": false + } + }, + { + "key": { + "symbol": "voter" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + } + ] + } + } + } + }, + "failed_call": true + }, { "event": { "ext": "v0",