Repository navigation
feat(market-core): counter-indexed open/category/holder market directories (#25) - #240
Open
achiever2110 wants to merge 3 commits into
Open
achiever2110 wants to merge 3 commits into
achiever2110 wants to merge 3 commits into
Conversation
…ories (OracleDesk#25) Adds on-chain market directories following the reasoning-registry MarketTraceCount/MarketTrace pattern: an open-markets set with O(1) swap-remove, an append-only per-category list, and a per-holder directory maintained inside save_position on empty/non-empty transitions only. Paginated views (open_markets, markets_by_category, markets_of) cap pages at MAX_PAGE=50; admin rebuild_open_index backfills legacy open markets in bounded batches. Error::DirectoryEntryArchived = 27 appends at the current develop tail.
…g example (OracleDesk#25) Documents the counter-indexed open/category/holder directories (keys, O(1) swap-remove, rent-restore requirement, snapshot-free pagination), the per-transition write-count table as authored-test derivations, the admin open-market backfill with its holder-directory limitation, and a frontend paging example for open_markets / markets_by_category / markets_of.
Author
|
Follow-up to the collision disclosure: our own PR #184 (abercrombie04, hot/cold storage split) also appends at the market-core Error tail with MetaUriTooLong = 27 and adds DataKey variants alongside. So discriminant 27 and the DataKey tail collide with #184 as well as #237/#239, per the independent-PR policy (last PR merged rebases). Whichever of #184 / #240 merges second needs a one-hunk re-append: bump DirectoryEntryArchived to the next free discriminant and update the should_panic expectation Error(Contract, #27) in the directory_remove_traps_on_archived_slot test. No rebase done now; both PRs stand against develop@5ede00a8 as authored. |
5 of 7 tasks
anitahhhh
added a commit
to anitahhhh/OracleDesk-SmartContract
that referenced
this pull request
Oct 22, 2026
…ils precisely (OracleDesk#24) ADR 0010 listed the Python mirror as follow-up work it could not do. That was wrong about the constraint: model/fpmm_model.py runs here, so its checks are executed rather than merely authored. Only the TypeScript mirror stays out, because Node on this host cannot strip types. Also drops the claim that resolver's Error 44 collides with OracleDesk#239/OracleDesk#240/OracleDesk#241: it is OracleDesk#241 (QuorumNotReached) and OracleDesk#242 (EventTimeMismatch) that reach 44, verified from each PR's own patch. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR resolves the following assigned issues:
What
Counter-indexed on-chain market directories in
market-core, following thereasoning-registryMarketTraceCount/MarketTracepattern: no directory isever an unbounded
Vecin a single entry, every entry is its own persistentledger key, and removals are O(1) swap-removes backed by reverse-index keys.
Changes made (all in
oracledesk-stellar/contracts/market-core/src/lib.rs+ two docs)Keys (appended after
Band(u64)at theDataKeytail, never reordered):OpenCount,OpenAt(u32),OpenIndex(u64),CategoryCount(Category),CategoryAt(Category, u32),HolderCount(Address),HolderAt(Address, u32),HolderIndex(Address, u64).OpenCountis persistent, not instance, so thehot lifecycle counter does not fatten every call's instance write.
Error:
DirectoryEntryArchived = 27(appended at the currentdeveloptail, after
PendingAdminExpired = 26; uniqueness of 1..=27 verified).Helpers (private): generic
dir_remove<F, G>(swap-remove; no-op when thereverse key is absent — a never-indexed legacy market — and traps with
DirectoryEntryArchivedif the last*Atslot was archived while its reversekey survives),
dir_write/dir_write_u32(set +extend_ttlwith the houseBUMP_THRESHOLD/BUMP_TO),open_add/open_remove,category_add,holder_add/holder_remove, and the shared paginated readerdir_page.Wiring:
create_marketappends to the open + category directories andpasses
was_empty = truefor the creator's seed position (so treasury/creatorseeded positions list under the creator — the issue's explicit edge case).
resolve/void_marketcallopen_remove.save_positiongained a privatewas_empty: boolparameter (it is a privatefn, not an entrypoint, so thefrozen public shape is untouched) and updates the holder directory only on
empty/non-empty transitions; all six call sites are threaded —
buy/sellcompute
was_emptyfrom the position they already loaded (zero extra reads),redeempassesfalse(it always empties → always removes), and the positiontransfer derives
to_was_emptyfrom the pre-mutation position withfromguaranteed non-empty by the balance checks. Directory writes live in the
effects sections, after
require_authand all validation.Views (new
#[contractimpl]entrypoints):open_count(),open_markets(start, limit),category_count(category),markets_by_category(category, start, limit),holder_count(holder),markets_of(holder, start, limit)— each capped atMAX_PAGE = 50, returninga short vector at the end and an empty vector past it, TTL-extending only the
slots they touch. Pagination is snapshot-free: swap-removes reorder
entries, so pages across concurrent transitions can skip or repeat ids;
clients must de-duplicate (documented in both docs files).
Backfill: admin-only
rebuild_open_index(start_id, end_id), bounded toMAX_BACKFILL_BATCH = 200ids per call, idempotent (skips ids that alreadyhave an
OpenIndex), appends only still-Openmarkets. Holder directoriescannot be backfilled on-chain — documented in
docs/architecture.mdanddocs/frontend-integration.mdas required.Docs:
docs/architecture.mdgains an "On-chain market directories"section (key table, swap-remove semantics, rent-restore requirement, cost
table, backfill limits);
docs/frontend-integration.mdgains a paging exampleand the de-duplication caveat.
Tests (authored, NOT run on this host — see verification)
12 new tests inside the existing
mod test: create/resolve/void tracking;swap-remove at first/middle/last with exact slot-shape assertions; append-only
category behaviour; holder add on first buy / remove on full sell; zero
directory writes on an additional buy (
write_entriesdelta pinned to 3 vs afirst buy); redeem removal (winning and losing holders); creator-seed listing;
pagination caps + out-of-range pages; a deterministic-LCG randomized
consistency test over 90 mixed lifecycle steps cross-checking all three
directories against raw
Market/Positionscans (dense slots, exact counts,matching back-pointers);
rebuild_open_index_admin_only(targetedmock_auths,idempotency, batch bounds);
directory_remove_traps_on_archived_slot(
#[should_panic]onError(Contract, #27)); anddirectory_write_costs,which records the per-path
env.cost_estimate()table.Acceptance criteria
no unbounded
Vecper entry.save_positiontouches the holder directory only on empty/non-emptytransitions, verified by
holder_directory_unchanged_on_additional_buy.open_markets/markets_by_category/markets_ofcapped atMAX_PAGE.(
directory_consistency_random_sequences).docs/architecture.md— theentry-count table is documented and derivable from the helpers; the
byte-exact
env.cost_estimate()numbers are authored-test derivationsonly, because no cargo test could run on this machine (below).
make bindings. This hostcannot compile the workspace, so the wasm blobs cannot be rebuilt or
hashed, and bindings codegen requires the built wasm. No
*.wasm,*.wasm.sha256orpackages/bindings/**file is touched by this PR(
git diff --name-only.wasm count = 0). A maintainer with a workingtoolchain needs to run
make build && make bindingsbefore or aftermerge.
Verification actually performed (and its honest limits)
This machine cannot compile the workspace at the default branch: the pinned
rust-toolchain.toml(1.90.0, required for bit-identical wasm per #58) isrejected by
soroban-sdk@27.0.6(MSRV 1.91), socargo check/testfails atdependency resolution, and
cargo +stabledies on pre-existingoracledesk-typeserrors. I did not attempt to repair either, per scope.No test was executed and I claim none was.
What did run:
cargo +stable fmt -p market-core -- --check→ exit 0 at the base commitand exit 0 after this change (rustfmt fully parses the file, which
validates syntax and brace balance of the diff; it does not type-check).
Erroris unique and dense 1..=27; theDataKeyadditions are append-only after
Band(u64).DataKeyvariant is referenced by the helpers/views/tests; notodo!()/unimplemented!(); all sixsave_positioncall sites updated.Collision disclosure (independent-PR policy)
Error::DirectoryEntryArchived = 27and the eightDataKeyvariants append atthe current
developtail (5ede00a8). They will need re-appending (and the#27should_panic expectation bumping) if #237 or #239 — which also append tothese enums — merge first. This branch deliberately does not rebase onto our
other open branches; last-merged rebases.
Generated by an AI coding agent on the
achiever2110account; human-reviewedto the extent described above (fmt + audit only — no compile, no test run).