Skip to content

refactor(ids)!: shared id/seq machinery and composite ids (1/4)#192

Merged
gregorydemay merged 13 commits into
mainfrom
gdemay/DEFI-2901-id-types
Jun 30, 2026
Merged

refactor(ids)!: shared id/seq machinery and composite ids (1/4)#192
gregorydemay merged 13 commits into
mainfrom
gdemay/DEFI-2901-id-types

Conversation

@gregorydemay

@gregorydemay gregorydemay commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Introduces a reusable id layer and migrates the order types onto it.

  • Adds a crate-level ids module with a generic Seq<M> sequence type (marker-typed so distinct id families can't be mixed) and a CompositeId<A, B> for opaque hex composite ids.
  • Migrates OrderBookId, OrderSeq, and OrderId onto this machinery, with OrderId modeled as a CompositeId.
  • Adds the per-order-book FillSeq and the fill_seq field on Fill, minted during matching and persisted in the order-book snapshot, plus the FillId / TradeId aliases for upcoming trade persistence.
  • Covers the id types with proptest strategies and round-trip tests over the minicbor, fixed-size byte, and hex encode/decode paths, including the nested composite (TradeId) shape.

Breaking change

BREAKING CHANGE: the persisted stable-memory encoding changes. Order ids (OrderBookId/OrderSeq) now encode as a bare CBOR u64 instead of a 1-element array, and OrderBookSnapshot gains a non-Option next_fill field. A canister upgraded with state (event log or order-book snapshot) persisted before this change would fail to decode it. This is acceptable because the canister is pre-launch — there is no deployed state to migrate. The Candid interface is unchanged (oisy_trade.did is byte-identical to main), so this breaks the state format, not the public API.

Performance impact

A pure type/encoding refactor: no new stable-memory writes (the per-fill fill_seq is an in-memory counter increment), and migrating OrderBookId/OrderSeq to a bare-u64 Seq encoding shaves ~1 byte per id in the CBOR event log and snapshot. Net effect is neutral-to-positive — no benchmark regresses by more than 1.55%, while the event-log and upgrade paths get cheaper. Measured with canbench (top-level total instructions).

Benchmark main #192 Δ
upgrade_1000_no_fills 4.66M 4.28M −8.14%
read_events 16.1M 14.9M −7.23%
upgrade_full_depth 60.8M 58.6M −3.62%
write_events 23.1M 22.3M −3.19%
process_pending_orders_1000_no_fills 96.8M 98.4M +1.55%

All other benchmarks move under ±0.5%, including the full-depth fill sweeps (gtc/fok_fill_full_bid_side, ~813M instructions) at +0.20%.

📚 PR stack

(#171 — order-level scalars — already merged to main.)

🤖 Generated with Claude Code

gregorydemay and others added 7 commits June 29, 2026 14:11
Introduce FillSeq and mint a per-order-book fill sequence for each match,
persisting the book's fill counter in its snapshot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the standalone OrderBookId newtype with Seq<OrderBookMarker>, so it
encodes as a bare u64. Update the worst-case event sizes and the snapshot
golden encoding to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move the id/seq machinery out of order:: to a crate-level ids module and
model OrderId as a CompositeId, replacing the bespoke OrderId newtype and
its OrderIdParseError with the shared ParseFixedWithIdError.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add arbitrary strategies for Seq and CompositeId test types and cover the
minicbor, fixed-size byte, and hex encode/decode paths with proptests.
Introduce the FillId and TradeId composite-id aliases.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 29, 2026 15:56
@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

canbench 🏋 (dir: canister) 73d9080 2026-06-30 10:09:20 UTC

canister/canbench_results.yml is up to date
📦 canbench_results_benchmark.csv available in artifacts

---------------------------------------------------

Summary:
  instructions:
    status:   Improvements detected 🟢
    counts:   [total 14 | regressed 0 | improved 8 | new 0 | unchanged 6]
    change:   [max +1.50M | p75 +2.11K | median -557.50K | p25 -25.21M | min -46.59M]
    change %: [max +1.55% | p75 +0.25% | median -3.40% | p25 -4.14% | min -8.14%]

  heap_increase:
    status:   No significant changes 👍
    counts:   [total 14 | regressed 0 | improved 0 | new 0 | unchanged 14]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   No significant changes 👍
    counts:   [total 14 | regressed 0 | improved 0 | new 0 | unchanged 14]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------

Only significant changes:
| status | name                                                                    | calls |     ins |  ins Δ% | HI |  HI Δ% | SMI |  SMI Δ% |
|--------|-------------------------------------------------------------------------|-------|---------|---------|----|--------|-----|---------|
|   +    | bench_fok_killed_full_bid_side::order_history::apply_update             |     1 | 121.17K |  +2.80% |  0 |  0.00% |   0 |   0.00% |
|   +    | bench_fok_killed_full_bid_side::apply_order_updates                     |     1 | 126.81K |  +2.70% |  0 |  0.00% |   0 |   0.00% |
|   +    | bench_process_pending_orders_1000_no_fills::order_history::apply_update | 1.00K |  81.91M |  +2.10% |  0 |  0.00% |   0 |   0.00% |
|   +    | bench_process_pending_orders_1000_no_fills::order_history               | 1.00K |  83.98M |  +2.05% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_write_events::AddLimitOrder                                       |     1 |  12.95K |  -2.17% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_write_events::Settling                                            |     1 |  21.72M |  -2.41% |  6 |  0.00% |   0 |   0.00% |
|   -    | bench_upgrade_full_depth::post_upgrade                                  |     1 |  35.55M |  -3.15% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_write_events                                                      |       |  22.33M |  -3.19% |  7 |  0.00% |   0 |   0.00% |
|   -    | bench_read_events::AddLimitOrder                                        |     1 |  10.30K |  -3.60% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_upgrade_full_depth                                                |       |  58.57M |  -3.62% |  0 |  0.00% | 128 |   0.00% |
|   -    | bench_fok_killed_full_bid_side::settling                                |     1 | 355.98K |  -3.77% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_fok_fill_full_bid_side                                            |       | 779.98M |  -4.04% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_gtc_fill_full_bid_side                                            |       | 779.98M |  -4.04% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_process_pending_orders_1000                                       |       | 994.70M |  -4.17% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_process_pending_orders_1000_with_fees                             |       |   1.02B |  -4.38% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_gtc_fill_full_bid_side::settling                                  |     1 | 677.64M |  -4.70% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_fok_fill_full_bid_side::settling                                  |     1 | 677.64M |  -4.70% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_process_pending_orders_1000::settling                             |     1 | 839.39M |  -4.96% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_upgrade_full_depth::pre_upgrade                                   |     1 |  19.51M |  -5.01% |  0 |  0.00% | 128 |   0.00% |
|   -    | bench_fok_fill_full_bid_side::balances                                  | 1.39K | 607.73M |  -5.20% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_gtc_fill_full_bid_side::balances                                  | 1.39K | 607.73M |  -5.20% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_fok_fill_full_bid_side::balances::transfer                        | 1.39K | 604.87M |  -5.22% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_gtc_fill_full_bid_side::balances::transfer                        | 1.39K | 604.87M |  -5.22% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_process_pending_orders_1000_with_fees::settling                   |     1 | 843.61M |  -5.30% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_process_pending_orders_1000::balances::unreserve                  |   405 |  89.16M |  -5.31% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_process_pending_orders_1000::balances                             | 1.94K | 754.72M |  -5.42% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_process_pending_orders_1000::balances::transfer                   | 1.54K | 663.13M |  -5.45% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_write_events::CancelLimitOrder                                    |     1 |   6.14K |  -5.54% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_fok_killed_full_bid_side::balances                                |     1 | 235.54K |  -5.59% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_fok_killed_full_bid_side::balances::unreserve                     |     1 | 233.72K |  -5.63% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_process_pending_orders_1000_with_fees::balances                   | 1.94K | 758.94M |  -5.79% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_process_pending_orders_1000_with_fees::balances::transfer         | 1.54K | 667.49M |  -5.80% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_process_pending_orders_1000_with_fees::balances::unreserve        |   405 |  89.02M |  -5.87% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_read_events::Settling                                             |     1 |  14.60M |  -6.17% | 12 |  0.00% |   0 |   0.00% |
|   -    | bench_upgrade_full_depth::post_upgrade::load_snapshot                   |     1 |  16.31M |  -6.25% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_upgrade_1000_no_fills::post_upgrade                               |     1 |   2.67M |  -6.90% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_upgrade_full_depth::pre_upgrade::save_snapshot                    |     1 |  13.63M |  -7.01% |  0 |  0.00% | 128 |   0.00% |
|   -    | bench_read_events                                                       |       |  14.94M |  -7.23% | 12 |  0.00% |   0 |   0.00% |
|   -    | bench_read_events::CancelLimitOrder                                     |     1 |   4.34K |  -8.14% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_upgrade_1000_no_fills                                             |       |   4.28M |  -8.14% |  0 |  0.00% | 128 |   0.00% |
|   -    | bench_upgrade_1000_no_fills::pre_upgrade                                |     1 |   1.50M | -10.79% |  0 |  0.00% | 128 |   0.00% |
|   -    | bench_upgrade_1000_no_fills::pre_upgrade::save_snapshot                 |     1 |   1.41M | -11.37% |  0 |  0.00% | 128 |   0.00% |
|   -    | bench_upgrade_1000_no_fills::post_upgrade::load_snapshot                |     1 |   1.43M | -11.83% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_write_events::SetHalt                                             |     1 |  50.66K | -27.27% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_write_events::Matching                                            |     1 | 441.67K | -28.97% |  1 |  0.00% |   0 |   0.00% |
|   -    | bench_read_events::SetHalt                                              |     1 |  28.14K | -39.67% |  0 |  0.00% |   0 |   0.00% |
|   -    | bench_read_events::Matching                                             |     1 | 218.12K | -45.92% |  0 |  0.00% |   0 |   0.00% |

ins = instructions, HI = heap_increase, SMI = stable_memory_increase, Δ% = percent change

---------------------------------------------------
CSV results saved to canbench_results.csv

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a shared identifier layer (ids) for fixed-width sequence IDs and composite IDs, migrates order IDs onto it, and extends order-book matching with a persisted per-book fill sequence (FillSeq) to support future fill/trade persistence.

Changes:

  • Add canister::ids with generic Seq<M> and CompositeId<A, B> plus fixed-width byte + hex conversions and property tests.
  • Migrate OrderBookId, OrderSeq, and OrderId to the shared ids machinery (with OrderId as a CompositeId).
  • Add FillSeq / fill_seq to Fill, mint it during matching, and persist next_fill in OrderBookSnapshot (with related test updates and updated golden encodings / size expectations).

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
canister/src/test_fixtures/mod.rs Updates Fill fixtures to include fill_seq.
canister/src/test_fixtures/event.rs Updates worst-case serialized size expectations after encoding/schema changes.
canister/src/state/snapshot/tests.rs Extends snapshot fixture/schema stability coverage for the new fill sequence state and updates golden hex.
canister/src/order/tests.rs Updates order ID parse error expectations and adjusts fill assertions to include fill_seq.
canister/src/order/mod.rs Migrates OrderBookId/OrderSeq/OrderId to the new ids module abstractions.
canister/src/order/fill.rs Introduces FillSeq and adds fill_seq to Fill; adds FillId/TradeId aliases.
canister/src/order/book.rs Mints FillSeq during matching and persists next_fill in OrderBookSnapshot.
canister/src/lib.rs Wires in ids module and updates order-id parsing/formatting paths.
canister/src/ids/tests.rs Adds proptest coverage for encoding/decoding and hex/byte round-trips for the new id types.
canister/src/ids/mod.rs Implements Seq, CompositeId, fixed-width serialization, and parsing error type.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread canister/src/ids/mod.rs
Comment thread canister/src/ids/mod.rs Outdated
Comment thread canister/src/order/book.rs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per-match fill_seq minting shifts instruction counts; regenerate
canbench_results.yml via canbench --persist. Also fix "fix number of
bytes" -> "fixed number of bytes" in FixedWidthId doc comment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 29, 2026 16:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread canister/src/order/mod.rs
@gregorydemay gregorydemay changed the title refactor(ids): shared id/seq machinery and composite ids (1/4) refactor(ids)!: shared id/seq machinery and composite ids (1/4) Jun 29, 2026
@gregorydemay gregorydemay marked this pull request as ready for review June 29, 2026 18:29
@gregorydemay gregorydemay requested a review from a team as a code owner June 29, 2026 18:29
@gregorydemay gregorydemay requested a review from mbjorkqvist June 29, 2026 18:29
@zeropath-ai

zeropath-ai Bot commented Jun 29, 2026

Copy link
Copy Markdown

No security or compliance issues detected. Reviewed everything up to cafb9ce.

Security Overview
Detected Code Changes
Change Type Relevant files
Enhancement ► canister/src/ids/mod.rs
    Add new ID system implementations and types for Seq, FixedWidthId, CompositeId, and related traits and methods
► canister/src/ids/tests.rs
    Add extensive tests for Seq, CompositeId, and nested ID structures using proptest and minicbor encode/decode roundtrips
Enhancement ► canister/src/lib.rs
    Expose and import the new ids module (pub mod ids)
Enhancement ► canister/src/order/book.rs
    Introduce next_fill sequence and mint_fill_seq mechanism; include FillSeq in Fill; update related fields and struct initializations; reflect new per-book fill sequencing in snapshot and transfer logic
Enhancement ► canister/src/order/fill.rs
    Add FillSeq, FillId, TradeId, and associated SeqMarker for Fill sequencing; adjust Fill struct to include fill_seq; update imports to reference new id types
Refactor ► canister/src/order/mod.rs
    Replace and centralize ID-related types: OrderId becomes CompositeId<OrderBookId, OrderSeq> and related types updated to use SeqMarker-based patterns; expose FillSeq and adjust uses
Bug Fix ► canister/src/order/book.rs
    Fix interdependent field handling to accommodate new fill sequencing and ensure correct minting and propagation of FillSeq in fills and snapshots
Bug Fix ► canister/src/lib.rs
    Adjust GetMyOrdersError variant to reference ids::ParseFixedWithIdError instead of a previous order::OrderIdParseError; adjust mapping of id to string in get_my_orders
Other ► canister/canbench_results.yml
    Update benchmarked instruction counts across many benches (general diffs of instruction counts)

Replace the standalone UserId newtype with Seq<UserIdMarker>, mirroring how
OrderSeq and FillSeq are declared. Seq gains a Storable impl producing the same
8-byte big-endian fixed layout the registry map relies on, plus Hash, so all
UserId call sites keep working unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@mbjorkqvist mbjorkqvist left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @gregorydemay, just a couple of testing-related nits.

On the per-book FillSeq (resolved downstream - no change needed here): This PR makes FillSeq per-order-book (minted in OrderBook::mint_fill_seq, persisted per book via OrderBookSnapshot.next_fill), whereas the DEFI-2901 spec as of this PR still describes a single canister-global fill sequence. I checked how the account-wide feed (R4 ByAccount / R8) actually orders across books, since a per-book sequence can't by itself form a globally-consistent newest-first cursor: #179 introduces a separate canister-global insertion sequence (TradeGlobalSeq, len()-derived) that keys the by_user index, while this per-book FillSeq is used only for the per-order identity (TradeId = (OrderId, FillSeq)) and the per-order prefix scan. #179 also updates the spec to describe both sequences. So the design is sound and the spec/implementation agree once the whole stack is considered — the per-book vs. global split is intentional, just spread across #192 (per-book FillSeq) and #179 (global global_seq + spec edit). Noting it here only as a cross-reference, not as a concern.

Comment thread canister/src/ids/mod.rs
Comment thread canister/src/ids/tests.rs
Tighten the from_hex guard to canonical lowercase hex only, rejecting
uppercase, sign characters and non-ASCII so it strictly inverts write_hex,
and add negative parsing tests for Seq and CompositeId.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 30, 2026 09:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Comment thread canister/src/ids/mod.rs Outdated
Comment thread canister/src/ids/mod.rs Outdated
gregorydemay and others added 2 commits June 30, 2026 09:58
Addresses Copilot id 3497750038: fold the duplicated length/charset check
in Seq::from_hex and CompositeId::from_hex into a single is_canonical_hex
helper. Addresses Copilot id 3497750001: document OrderId as a lowercase hex
string to match the strict canonical-lowercase parser.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 30, 2026 10:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

@gregorydemay gregorydemay enabled auto-merge June 30, 2026 11:06
@gregorydemay gregorydemay added this pull request to the merge queue Jun 30, 2026
Merged via the queue into main with commit ab02f6b Jun 30, 2026
17 checks passed
@gregorydemay gregorydemay deleted the gdemay/DEFI-2901-id-types branch June 30, 2026 11:13
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.

3 participants