Skip to content

feat(distribute): define atomic batch-distribution semantics (#1032) - #1093

Open
Phantomcall wants to merge 1 commit into
CalloraOrg:mainfrom
Phantomcall:fix/atomic-batch-distribution-1032
Open

feat(distribute): define atomic batch-distribution semantics (#1032)#1093
Phantomcall wants to merge 1 commit into
CalloraOrg:mainfrom
Phantomcall:fix/atomic-batch-distribution-1032

Conversation

@Phantomcall

Copy link
Copy Markdown

Define atomic batch-distribution semantics (#1032)

Closes #1032

Summary

Defines and enforces all-or-nothing (atomic) semantics for the
batch_distribute entrypoint in contracts/distribute, so a single invalid
recipient or transfer failure can never leave hidden partial accounting. The
entire batch is validated before any USDC is moved, and any failure reverts the
whole call.

Why the signature changed

The old batch_distribute took Vec<(Address, i128)>, which Soroban contract
macros reject ("generics unsupported on user-defined types in contract
functions"), so the function never compiled as a contract entrypoint. It now
takes two parallel built-in lists — recipients: Vec<Address> and
amounts: Vec<i128> (equal length) — which is a valid contract-facing ABI and
still preserves duplicate recipients so they can be rejected explicitly.

Atomicity model: all-or-nothing

  • Validate before mutate. Phase 1 validates every leg (positive amount,
    per-leg cap, valid recipient, no duplicate recipient); Phase 2 checks the
    batch total against the contract's USDC balance — all before any transfer.
  • Fail atomicity. If any leg is invalid, or the total exceeds the balance,
    or a transfer fails, the entire batch reverts (no partial distribution).
  • Value conservation. The batch is an overflow-checked sum of per-leg
    amounts; after a success the contract's balance is exactly
    prior_balance - total. No rounding, fees, or mint/burn.

Acceptance criteria → coverage

  • Duplicates and invalid recipients are rejected before state changes
    DuplicateRecipient rejection in Phase 1 (lib.rs), validate_recipient
    for the contract-self case.
    • test_batch::duplicates_are_rejected_before_any_transfer
    • test_batch::invalid_recipient_contract_self_is_rejected
  • The selected atomicity model is enforced on transfer failure
    → all-or-nothing, fail-early before any transfer.
    • test_batch::mid_batch_failure_reverts_entire_batch
  • Successful batches conserve total value
    → exact strict sum.
    • test_batch::successful_batch_conserves_total_value
  • Tests cover empty, maximum, duplicate, invalid, and mid-batch failures
    → net:
    • test_batch::empty_batch_is_rejected_without_mutation
    • test_batch::max_batch_size_is_enforced
    • test_batch::leg_count_mismatch_is_rejected
    • plus the above

Security / failure-mode considerations

A batch can only be sent by the admin (existing require_admin +
caller.require_auth). Duplicates, non-positive amounts, over-cap amounts,
the contract itself, and balance shortfalls are all rejected before mutation,
so an attacker can neither cause a partial disbursement nor mutate state on a
rejected batch.

Verification

cargo test -p callora-distribute --lib test_batch7 passed, 0 failed.

Note: the broader crate has pre-existing, unrelated failures in a few
event-emission tests (e.g. init_event_structure_validation asserts a 2-topic
event that the existing init code emits as 3 topics) and an integration test
file (tests/auth_snap.rs) referencing a stale CalloraDistribute API. These
predate and are orthogonal to this batch change; they are called out for a
separate cleanup.

…Org#1032)

- Replace broken tuple/list arg with parallel Vec<Address> + Vec<i128>
  (Soroban contract methods disallow custom-type / tuple generic args).
- Reject duplicate recipients in Phase 1 before any state change.
- Document all-or-nothing + value-conservation semantics.
- Add test_batch module covering empty, max-size, duplicate, invalid,
  mid-batch-failure, and value-conservation.
- Fix invalid 'callora.v1' event Symbol (dots not allowed) -> callora_v1.
@Phantomcall

Copy link
Copy Markdown
Author

i was able to solve this issue in an ample amount of time
i believe i can tackle the rest of the issues i applied to in similar fashion
Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[GrantFox][High] Define atomic batch-distribution semantics

1 participant