feat(ika_system): expose is_in_committees(System, ID): bool - #1708
Conversation
Adds a public read predicate to `ika_system::system::System` that returns true when a validator is in the current active committee or the next epoch's active committee. Mirrors the internal `is_current_committee || is_next_committee` test already used by `validator_set::request_withdraw_stake` and `validator_set::withdraw_stake` to branch between the two-epoch cooldown path and the inactive-direct-withdraw path. Why External integrations (liquid staking pools, custody integrations, anything wrapping `StakedIka`) need to know when a validator has rotated out of both committees, because `request_withdraw_stake` aborts with `EWithdrawDirectly` in that case. The intended flow per `validator::withdraw_stake` (validator.move:373-388) is to bypass `request_withdraw_stake` and call `system::withdraw_stake` directly on the still-Staked position — but the caller has no clean way to detect when to take that branch. Without this predicate, integrators have three poor options: 1. Speculatively call `request_withdraw_stake` and let it abort — Move has no try/catch, so this kills the whole transaction. 2. Call the existing public `active_committee()` and `next_epoch_active_committee()` accessors and run `.contains()` externally. Works, but copies the full `BlsCommittee` value (BLS pubkeys + voting power per member) on every read just to compute one bool. 3. Add an admin-gated rescue function with elevated trust to call `system::withdraw_stake` directly — expands the trust surface and breaks multisig/timelock-only governance designs. The predicate is observability of state the system already tracks. No behaviour change. Wiring Threads through the same three layers as the existing `active_committee()` getter: - `validator_set::is_in_committees(&ValidatorSet, ID): bool` (public(package)) - `system_inner::is_in_committees(&SystemInner, ID): bool` (public(package)) - `system::is_in_committees(&System, ID): bool` (public) Implementation is two reads on the existing `active_committee` and `next_epoch_active_committee` fields plus `bls_committee::contains` (already public). Zero new state, zero new dependencies. Build status Local `sui move build` on ika_system blocked by a pre-existing upstream stdlib mismatch: `ika_common/sources/bls_committee.move:149` calls `n_members.div_ceil(8)` which isn't on `std::u64` in sui 1.72.1. Same error reproduces on plain `origin/main` without this change — unrelated to this PR. The patch compiles cleanly against the sui version `ika_common` was authored on. Context Originating use case is liquid-staking pool recovery from validator rotation. Spec writeup including the validator.move line references and the alternatives we considered: https://github.com/inkwell-finance/ika-liquid-staking-spec (drafted 2026-05-12; happy to share the markdown if helpful for review). Happy to add a test once the `div_ceil` stdlib drift is resolved upstream, or to split that into a separate prep PR if you'd like.
|
Friendly ping on this one — open since May with no review. Still Recap of why it matters to us, with a concrete case: we run LIKA, a liquid-staking pool that spreads IKA across validators on an admin-set weight distribution. When a validator we hold positions on rotates out of both committees, our keeper's The diff is +35/-0, read-only, no behaviour change: it threads a predicate through the same three layers as the existing Two things to make this easy to land:
Is there anything blocking beyond review bandwidth? |
Adds a public read predicate to
ika_system::system::Systemthat returns true when a validator is in the current active committee or the next epoch's active committee. Mirrors the internalis_current_committee || is_next_committeetest already used byvalidator_set::request_withdraw_stakeandvalidator_set::withdraw_staketo branch between the two-epoch cooldown path and the inactive-direct-withdraw path.Why
External integrations (liquid staking pools, custody integrations, anything wrapping
StakedIka) need to know when a validator has rotated out of both committees, becauserequest_withdraw_stakeaborts withEWithdrawDirectlyin that case. The intended flow pervalidator::withdraw_stake(validator.move:373-388) is to bypassrequest_withdraw_stakeand callsystem::withdraw_stakedirectly on the still-Staked position — but the caller has no clean way to detect when to take that branch.Without this predicate, integrators have three poor options:
request_withdraw_stakeand let it abort — Move has no try/catch, so this kills the whole transaction.active_committee()andnext_epoch_active_committee()accessors and run.contains()externally. Works, but copies the fullBlsCommitteevalue (BLS pubkeys + voting power per member) on every read just to compute one bool.system::withdraw_stakedirectly — expands the trust surface and breaks multisig/timelock-only governance designs.The predicate is observability of state the system already tracks. No behaviour change.
Wiring
Threads through the same three layers as the existing
active_committee()getter:validator_set::is_in_committees(&ValidatorSet, ID): bool(public(package))system_inner::is_in_committees(&SystemInner, ID): bool(public(package))system::is_in_committees(&System, ID): bool(public)Implementation is two reads on the existing
active_committeeandnext_epoch_active_committeefields plusbls_committee::contains(already public). Zero new state, zero new dependencies.Build status
Local
sui move buildon ika_system blocked by a pre-existing upstream stdlib mismatch:ika_common/sources/bls_committee.move:149callsn_members.div_ceil(8)which isn't onstd::u64in sui 1.72.1. Same error reproduces on plainorigin/mainwithout this change — unrelated to this PR. The patch compiles cleanly against the sui versionika_commonwas authored on.Context
Originating use case is liquid-staking pool recovery from validator rotation. Spec writeup including the validator.move line references and the alternatives we considered:
https://github.com/inkwell-finance/ika-liquid-staking-spec (drafted 2026-05-12; happy to share the markdown if helpful for review).
Happy to add a test once the
div_ceilstdlib drift is resolved upstream, or to split that into a separate prep PR if you'd like.Description
Describe the changes or additions included in this PR.
Test plan
How did you test the new or updated feature?
Release notes
Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required.
For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates.