Skip to content

[Bug] register() has no Sybil resistance in tholos-v2 #168

Closed
devkryssie/tholos
#1

Description

@collinsezedike

Summary

register enforces policy.max_position (a per-position cap) and policy.max_total_weight (a cap on the frozen eligible total W). Both bounds exist purely as arithmetic overflow guards for settlement's forfeiture-distribution math (max_position's own doc comment: "checked at deposit time so settlement's forfeiture-distribution arithmetic can't overflow"), not as a concentration or anti-Sybil control.

Nothing prevents a single funded actor from splitting capital across many addresses, each individually staying under max_position, to collectively acquire far more effective voting weight than any single position could hold, up to the full max_total_weight. docs/src/V2_RESOLUTION.md explicitly and deliberately rejects per-address caps as a concentration control for exactly this reason ("per-address caps... all increase combined influence when a participant splits capital across pseudonymous addresses... making address splitting profitable"), so this isn't an oversight in register's logic, it's a known consequence of the linear-weight design that isn't yet bounded by anything else.

Impact: a single well-funded actor can dominate a dispute's outcome by splitting into enough addresses, up to max_total_weight, the same plutocratic risk V2_RESOLUTION.md's own "Non-goals and trust assumption" section already states plainly ("an address or coalition controlling more than half of the eligible bonded capital can determine the result"), but with no bound at all today on how cheaply one actor can approach that threshold via splitting.

Scope

  • Evaluate bounding the ratio between max_total_weight and max_position at initialize (e.g. requiring max_total_weight be no more than some multiple of max_position), so a deployment can at least cap how many effective "seats" a single split actor could occupy, even without solving Sybil resistance outright.
  • Document the residual plutocratic risk explicitly wherever policy parameters are described (CONTRACT_V2.md), since this isn't fully solvable without an external identity system, per V2_RESOLUTION.md's own analysis.
  • This is a design trade-off, not a pure bug fix; the assignee should present options rather than assuming a specific ratio is correct.

Proposed approach

No single fix is prescribed. A bounded max_total_weight/max_position ratio is the most direct lever available without a larger redesign; the assignee's first deliverable should be a short written analysis of what ratio (if any) meaningfully raises the cost of address-splitting without over-constraining legitimate deployments, confirmed with the maintainer before implementation.

Activity

  1. added
    bugSomething isn't working
    Maybe RewardedIssue may be eligible for a GrantFox reward
    GrantFox OSSIssue tracked in GrantFox OSS
    hardSecurity-sensitive or high-complexity change
    on Sep 2, 2026
  2. ZacLou commented on Sep 3, 2026

    @ZacLou
    Contributor

    I'd like to work on this. Before opening a PR, here is a design proposal for maintainer feedback:

    Proposed mitigation: bound the max_total_weight / max_position ratio at initialize

    1. Add a validation in initialize (and in any admin setter that changes either bound) requiring:
      max_total_weight <= max_position * MAX_POSITION_RATIO`n2. Introduce a constant MAX_POSITION_RATIO (e.g. 10) so a single actor must control at least max_total_weight / max_position distinct positions to approach the plutocratic threshold.
    2. Keep max_position as the per-position economic unit and max_total_weight as the system-wide cap; the ratio simply raises the address-splitting cost without introducing an external identity system.
    3. Document the residual risk explicitly in CONTRACT_V2.md: even with the ratio, a coalition controlling >50% of �ligible_total can determine the result; the ratio only bounds how cheaply one actor can approach that via Sybil splitting.

    Rationale for ratio = 10

    • It is high enough to avoid constraining legitimate deployments that want many small participants.
    • It is low enough to require a 10x capital fragmentation to dominate, which materially raises griefing cost compared to an unbounded ratio.

    Tests

    • initialize rejects max_total_weight > max_position * MAX_POSITION_RATIO.
    • initialize accepts the boundary exactly.
    • Existing behavior is unchanged when the ratio is satisfied.

    Please confirm whether this ratio and approach look right, or if you'd prefer a different bound or a runtime (rather than initialization-time) check.

  3. ZacLou commented on Sep 3, 2026

    @ZacLou
    Contributor

    Opened a PR implementing the ratio-bound approach proposed above: #174. Happy to adjust the ratio or switch to a different mechanism based on maintainer feedback.

  4. devkryssie commented on Sep 3, 2026

    @devkryssie

    hi maintainer i'll like to work on this issue and submit pr within 24hous

  5. Stealth-cloud-droid commented on Sep 3, 2026

    @Stealth-cloud-droid

    hi maintainer i'll like to work on this issue and submit pr within 24hous

  6. aojomo commented on Sep 3, 2026

    @aojomo

    Hi maintainer, Please I would like to work on this issue

  7. JohnArayaE commented on Sep 3, 2026

    @JohnArayaE
    Contributor

    I would like to work on this issue because the current relationship between max_total_weight and max_position leaves a meaningful concentration risk when a single funded actor can split capital across multiple addresses. I can first provide the requested short analysis comparing reasonable ratio bounds and their trade-offs, including how each option affects legitimate deployments versus the cost of address splitting, and confirm the preferred direction with the maintainer before making any implementation changes. I can also update the relevant V2 contract documentation to make the residual plutocratic risk and limitations of this mitigation explicit.

  8. grantfox-oss commented on Sep 4, 2026

    @grantfox-oss

    🦊 GrantFox — @devkryssie has been assigned to this issue!

    Next steps:

    1. Open a Pull Request referencing this issue (e.g., Closes #168)
    2. Your PR will be reviewed by the drydocs maintainers

    Good luck! Track your progress on GrantFox.

  9. Ranjeet2063 commented on Sep 5, 2026

    @Ranjeet2063
    Contributor

    Hi maintainers, I would like to take up this issue.

    I specialize in Soroban smart contract architecture, cryptographic game theory, and sybil resistance mechanisms.

    Implementation Plan:

    1. Inspect contracts/tholos-v2/src/lib.rs register() implementation to identify missing sybil resistance checks.
    2. Introduce verifiable identity / bonded stake requirements or non-transferable participation tickets to prevent split-identity multi-voting.
    3. Add contract invariants and authorization checks ensuring one registered identity per bonded principal.
    4. Write exhaustive unit tests in contracts/tholos-v2/src/tests/ covering sybil attempts, bonded constraints, and honest participant flows.
    5. Ready to deliver a clean PR within 12–24 hours.
  10. grantfox-oss commented on Sep 11, 2026

    @grantfox-oss

    🦊 GrantFox — @Ranjeet2063 has been assigned to this issue!

    Next steps:

    1. Open a Pull Request referencing this issue (e.g., Closes #168)
    2. Your PR will be reviewed by the drydocs maintainers

    Good luck! Track your progress on GrantFox.

  11. collinsezedike commented on Sep 11, 2026

    @collinsezedike
    CollaboratorAuthor

    @Ranjeet2063 You're assigned. @devkryssie was unassigned after going silent for two check-ins on an open PR (#182), now closed.

    Please open a PR referencing this issue (Closes #168).

  12. Ranjeet2063 commented on Sep 11, 2026

    @Ranjeet2063
    Contributor

    Thank you @collinsezedike for assigning this! I am actively on it. Analyzing the ratio bounds between max_total_weight and max_position and the V2_RESOLUTION design trade-offs right now. Will deliver the clean PR with unit tests shortly.

  13. Ranjeet2063 commented on Sep 11, 2026

    @Ranjeet2063
    Contributor

    Hey @collinsezedike, I have opened PR #215 resolving this issue.

    Summary of work done:

    • Added constant MAX_TOTAL_WEIGHT_TO_POSITION_RATIO = 10 and enforced max_total_weight <= max_position * 10 in initialize() with custom Error::InvalidWeightRatio = 37.
    • Verified no overflow possibility (max_position <= 10^19 on i128).
    • Updated sizing profiles in docs/src/V2_BOND_SIZING.md and load script scripts/testnet-load-v2.sh.
    • Added ratio boundary tests in contracts/tholos-v2/src/test.rs.
    • Local verification: all 124 tests passing green, cargo clippy and cargo fmt clean.

    Ready for your review!

  14. added a commit that references this issue on Sep 12, 2026
    2414ac5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinghardSecurity-sensitive or high-complexity change

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions