diff --git a/.claude/skills/docs-review/SKILL.md b/.claude/skills/docs-review/SKILL.md index c744a49..c750571 100644 --- a/.claude/skills/docs-review/SKILL.md +++ b/.claude/skills/docs-review/SKILL.md @@ -47,12 +47,25 @@ gaps, outdated content, and stale comments. should be valid. - Cross-page markdown links must use reference-style definitions (`[text][ref]` or `[text]` with a - `[text]: url` at the bottom of the file). Inline - links (`[text](url)`) are only acceptable for - same-page anchors (e.g. `[label](#anchor)`). + `[text]: url` at the bottom of the file). + Reference definitions must be placed at the end + of the file, not inline near their first usage. + Inline links (`[text](url)`) are only acceptable + for same-page anchors (e.g. `[label](#anchor)`). - `` and `` component attributes should resolve correctly. +1. Verify that sector layout diagrams in + `docs/src/program/sectors.md` are consistent with + their data structures. For each node type (Order, + Seat, StackNode), compare the fields shown in the + ASCII diagram against the fields in the corresponding + struct definition (in `interface/src/order/mod.rs`, + `interface/src/seat/mod.rs`, + `interface/src/stack/mod.rs`). Flag any field that + is missing from the diagram, present in the diagram + but not the struct, or in the wrong order. + 1. Verify that all directory trees in the docs are current. For each tree, list the actual files on disk and compare against the rendered tree. Flag diff --git a/CLAUDE.md b/CLAUDE.md index 2d095a2..0f16d12 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -50,6 +50,9 @@ development topics: - `docs/src/program/layout.md` program memory layout - `docs/src/program/inputs.md` input format specs - `docs/src/program/markets.md` market structure +- `docs/src/program/sectors.md` market memory sectors +- `docs/src/program/seats.md` seat data structure +- `docs/src/program/orders.md` order data structure - `docs/src/program/algorithm-index.md` algorithm documentation diff --git a/Cargo.lock b/Cargo.lock index 03e3128..f908f51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -893,6 +893,7 @@ dependencies = [ "pinocchio", "pinocchio-token", "pinocchio-token-2022", + "solana-sbpf 0.16.0", ] [[package]] diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index 7e501e7..9ca11c8 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -115,6 +115,9 @@ export default { { text: "Layout", link: "/program/layout" }, { text: "Inputs", link: "/program/inputs" }, { text: "Markets", link: "/program/markets" }, + { text: "Sectors", link: "/program/sectors" }, + { text: "Seats", link: "/program/seats" }, + { text: "Orders", link: "/program/orders" }, { text: "Algorithm Index", link: "/program/algorithm-index" }, ], }, diff --git a/docs/algorithms/market/INIT-BASE-VAULT.tex b/docs/algorithms/market/INIT-BASE-VAULT.tex index 82ce44e..d3b01f0 100644 --- a/docs/algorithms/market/INIT-BASE-VAULT.tex +++ b/docs/algorithms/market/INIT-BASE-VAULT.tex @@ -23,7 +23,7 @@ \ENSURE $r_9$ = acct \ENSURE frame.token\_program\_id \ENSURE frame.token\_program\_is\_2022 - \ENSURE input.market.data.base\_vault\_bump = frame.bump + \ENSURE input.market\_header.base\_vault\_bump = frame.bump \PROCEDURE{INIT-BASE-VAULT}{acct, frame} \COMMENT{Retrieve input buffer pointers and advance to base token program account.} \STATE input = frame.input @@ -64,7 +64,7 @@ \RETURN result \ENDIF \COMMENT{Store derived bump in market account data.} - \STATE input.market.data.base\_vault\_bump = frame.bump + \STATE input.market\_header.base\_vault\_bump = frame.bump \COMMENT{Advance to quote token program account.} \STATE acct += \texttt{EmptyAccount.size} \ENDPROCEDURE diff --git a/docs/algorithms/market/INIT-QUOTE-VAULT.tex b/docs/algorithms/market/INIT-QUOTE-VAULT.tex index 1889804..127e62c 100644 --- a/docs/algorithms/market/INIT-QUOTE-VAULT.tex +++ b/docs/algorithms/market/INIT-QUOTE-VAULT.tex @@ -17,7 +17,7 @@ \REQUIRE frame.pda\_seeds[2].addr = \&frame.bump \REQUIRE frame.pda\_seeds[2].len = \texttt{u8.size} \REQUIRE frame.rent - \ENSURE input.market.data.quote\_vault\_bump = frame.bump + \ENSURE input.market\_header.quote\_vault\_bump = frame.bump \PROCEDURE{INIT-QUOTE-VAULT}{acct, frame, input, input\_shifted} \COMMENT{Check quote token program account.} \IF{acct.duplicate == \texttt{common::account::NON\_DUP\_MARKER}} @@ -62,7 +62,7 @@ \STATE frame.mint = \&input\_shifted.quote\_mint \STATE \CALL{INIT-VAULT}{acct, frame} \COMMENT{Store derived bump in market account data.} - \STATE input.market.data.quote\_vault\_bump = frame.bump + \STATE input.market\_header.quote\_vault\_bump = frame.bump \ENDPROCEDURE \end{algorithmic} \end{algorithm} diff --git a/docs/algorithms/market/init-market-pda/CREATE-MARKET-ACCOUNT.tex b/docs/algorithms/market/init-market-pda/CREATE-MARKET-ACCOUNT.tex index d4be91e..a58c61f 100644 --- a/docs/algorithms/market/init-market-pda/CREATE-MARKET-ACCOUNT.tex +++ b/docs/algorithms/market/init-market-pda/CREATE-MARKET-ACCOUNT.tex @@ -13,8 +13,8 @@ \REQUIRE frame.cpi[1].info.data\_len = \texttt{common::memory::LEN\_ZERO} \ENSURE frame.signers\_seeds.addr = \&frame.pda\_seeds \ENSURE frame.signers\_seeds.len = \texttt{frame.pda\_seeds.n\_seeds} - \ENSURE input.market.data.next = input + \texttt{entrypoint::input\_buffer::MARKET\_DATA\_BYTES} - \ENSURE input.market.data.bump = frame.bump + \ENSURE input.market\_header.next = \texttt{\&entrypoint::input\_buffer::MARKET\_SECTORS\_START} + \ENSURE input.market\_header.bump = frame.bump \PROCEDURE{CREATE-MARKET-ACCOUNT}{frame} \COMMENT{Assign CPI account fields via immediates.} \STATE frame.cpi[0].info.is\_signer = \texttt{true} @@ -55,8 +55,8 @@ \STATE syscall.seeds\_len = \texttt{market::register::N\_PDA\_SIGNERS} \STATE \CALL{sol-invoke-signed-c}{system-program::CreateAccount} \COMMENT{Initialize market header next pointer and store derived bump.} - \STATE input.market.data.next = input + \texttt{entrypoint::input\_buffer::MARKET\_DATA\_BYTES} - \STATE input.market.data.bump = frame.bump + \STATE input.market\_header.next = \texttt{\&entrypoint::input\_buffer::MARKET\_SECTORS\_START} + \STATE input.market\_header.bump = frame.bump \ENDPROCEDURE \end{algorithmic} \end{algorithm} diff --git a/docs/src/program/markets.md b/docs/src/program/markets.md index 6246096..78c70fb 100644 --- a/docs/src/program/markets.md +++ b/docs/src/program/markets.md @@ -21,10 +21,32 @@ All relevant information is derived from the accounts: -The entrypoint dispatches to -[REGISTER-MARKET](#register-market), which validates the provided accounts, -derives and creates the market PDA, then initializes the base and quote -token vaults. +### Input buffer + +The registration `InputBuffer` extends the base +[input buffer] header with the base and quote mint +accounts: + + + +All fields through `Market` sit at compile-time-known +offsets from `r1` (see [`InputBufferHeader`]). +`BaseMint` and `QuoteMint` have variable-length data, +so their positions cannot be determined statically. +[MARKET-PDA-PRELUDE](#market-pda-prelude) handles this +with a dynamic offset: after reading +`input.base_mint.data_len`, it computes + +```rs +frame.input_shifted = input + padded(input.base_mint.data_len) +``` + +From `frame.input_shifted`, all `QuoteMint` fields are +accessible at static offsets. Accounts after `QuoteMint` +(`SystemProgram`, `RentSysvar`, `BaseTokenProgram`, +`BaseVault`, `QuoteTokenProgram`, `QuoteVault`) are located +by walking forward from `frame.input_shifted` using each +account's padded data length. ## REGISTER-MARKET @@ -119,6 +141,7 @@ account for the given mint, with the market as the account owner. [input buffer]: inputs#input-buffer +[`InputBufferHeader`]: inputs#input-buffer [System Program]: https://solana.com/docs/core/programs/builtin-programs#the-system-program [Rent]: https://docs.rs/pinocchio/0.11.0/pinocchio/sysvars/rent/struct.Rent.html [Token Program]: https://github.com/solana-program/token diff --git a/docs/src/program/orders.md b/docs/src/program/orders.md new file mode 100644 index 0000000..ce4d4af --- /dev/null +++ b/docs/src/program/orders.md @@ -0,0 +1,5 @@ +# Orders + +An `Order` represents an entry on the order book. + + diff --git a/docs/src/program/seats.md b/docs/src/program/seats.md new file mode 100644 index 0000000..af708bb --- /dev/null +++ b/docs/src/program/seats.md @@ -0,0 +1,8 @@ +# Seats + +A `Seat` is a market maker's trading account within a +market. +It holds tree pointers for lookup, the user's address, +token balances, and per-side order arrays. + + diff --git a/docs/src/program/sectors.md b/docs/src/program/sectors.md new file mode 100644 index 0000000..ae59255 --- /dev/null +++ b/docs/src/program/sectors.md @@ -0,0 +1,81 @@ +# Sectors + +Market account data begins with a [`MarketHeader`] +followed by a contiguous array of fixed-size sectors. Each +sector holds one of three node types ([`Order`], +[`Seat`], or [`StackNode`]), but a node does not +necessarily occupy the entire sector. + +Because the market account is at a [fixed position] in the +input buffer, its data offsets are persisted across +transactions. `MarketHeader` stores absolute SBPF pointers +into the sector array (`seats`, `asks`, `bids`, `top`, +`next`) that remain valid without recomputation. + +```txt ++----------------+----------+----------+----------+-----+ +| MarketHeader | Sector 0 | Sector 1 | Sector 2 | ... | ++----------------+----------+----------+----------+-----+ +``` + +A `Sector` is a byte buffer sized to the largest node type: + + + +The first byte of every sector is a `NodeTag` discriminant +that identifies its contents: + + + +## Order + +A sector holding an [`Order`] is an active node in +one of the market's order trees. + +```txt ++-----+--------------------------------------------------+ +| tag | (unused) | ++-----+--------------------------------------------------+ +``` + + + +## Seat + +A sector holding a [`Seat`] is an active node in +the market's seat tree. + +```txt ++-----+--------+------+-------+------+-----+------+------+ +| tag | parent | left | right | user | ... | asks | bids | ++-----+--------+------+-------+------+-----+------+------+ +``` + + + +## StackNode + +A sector holding a `StackNode` is a freed sector on the +free sector stack. + +```txt ++-----+------+-------------------------------------------+ +| tag | next | (unused) | ++-----+------+-------------------------------------------+ +``` + + + +## Allocation + +`MarketHeader.next` points to the next unallocated sector +in the memory map. `MarketHeader.top` points to the top of +the free sector stack. When a sector is freed, it becomes a +`StackNode` pushed onto the stack. New allocations pop from +the stack first; when the stack is empty, `next` advances. + +[`MarketHeader`]: markets +[`Order`]: orders +[`Seat`]: seats +[`StackNode`]: #stacknode +[fixed position]: inputs#input-buffer diff --git a/interface/Cargo.toml b/interface/Cargo.toml index e08c17a..b2eb907 100644 --- a/interface/Cargo.toml +++ b/interface/Cargo.toml @@ -4,6 +4,7 @@ dropset-macros = {path = "../macros"} pinocchio = {workspace = true} pinocchio-token = {workspace = true} pinocchio-token-2022 = {workspace = true} +solana-sbpf = {workspace = true} [package] name = "dropset-interface" diff --git a/interface/src/common/memory.rs b/interface/src/common/memory.rs index 4b83132..bac13ee 100644 --- a/interface/src/common/memory.rs +++ b/interface/src/common/memory.rs @@ -2,9 +2,25 @@ use crate::common::account::EmptyAccount; use crate::common::token::InitializeAccount2; use crate::market::MarketHeader; use crate::market::register::CreateAccountData; -use dropset_macros::{constant_group, size_of_group}; +use crate::order::Order; +use crate::seat::Seat; +use crate::stack::StackNode; +use dropset_macros::{constant_group, discriminant_enum, size_of_group, svm_data}; use pinocchio::Address as Pubkey; +// region: node_tag +/// Discriminant tag for nodes in the market memory map. +#[discriminant_enum("common/memory", "NODE_TAG")] +pub enum NodeTag { + /// Seat node. + Seat, + /// Order node. + Order, + /// Stack node. + Stack, +} +// endregion: node_tag + constant_group! { #[prefix("DATA")] #[inject("common/memory")] @@ -23,12 +39,41 @@ constant_group! { BOOL_FALSE = immediate!(0), /// Boolean true value. BOOL_TRUE = immediate!(1), + } } +// region: sector +/// Sector-sized byte buffer (largest of Order, Seat, StackNode). +#[svm_data] +pub struct Sector( + [u8; { + const ORDER: usize = core::mem::size_of::(); + const SEAT: usize = core::mem::size_of::(); + const STACK: usize = core::mem::size_of::(); + if ORDER >= SEAT && ORDER >= STACK { + ORDER + } else if SEAT >= STACK { + SEAT + } else { + STACK + } + }], +); +// endregion: sector + // region: size_of_group_example size_of_group! { #[inject("common/memory")] - [u8, u64, Pubkey, EmptyAccount, MarketHeader, CreateAccountData, InitializeAccount2] + [ + u8, + u64, + Pubkey, + EmptyAccount, + MarketHeader, + CreateAccountData, + InitializeAccount2, + Sector, + ] } // endregion: size_of_group_example diff --git a/interface/src/entrypoint.rs b/interface/src/entrypoint.rs index 7bc4beb..e087dd2 100644 --- a/interface/src/entrypoint.rs +++ b/interface/src/entrypoint.rs @@ -2,6 +2,7 @@ use crate::common::account::EmptyAccount; use crate::market::MarketHeader; use dropset_macros::{constant_group, discriminant_enum, svm_data}; use pinocchio::account::RuntimeAccount; +use solana_sbpf::ebpf::MM_INPUT_START; // region: discriminant_enum /// Instruction discriminants. @@ -34,9 +35,9 @@ pub struct InputBufferHeader { pub n_accounts: u64, pub user: EmptyAccount, pub market: RuntimeAccount, - pub market_data_header: MarketHeader, - /// MarketHeader.next initializes to this offset. - pub market_data_bytes: u8, + pub market_header: MarketHeader, + /// MarketHeader.next initializes to an absolute pointer to this byte. + pub market_sectors_start: u8, } // endregion: input_buffer_header @@ -66,20 +67,25 @@ constant_group! { USER_DATA_TO_MARKET_ADDRESS = relative_offset!( InputBufferHeader, user.data, market.address ), - /// From input buffer to market data next pointer. - MARKET_DATA_NEXT = offset!(InputBufferHeader.market_data_header.next), - /// From input buffer to market data bump. - MARKET_DATA_BUMP = offset!(InputBufferHeader.market_data_header.bump), - /// From input buffer to market data base vault bump. - MARKET_DATA_BASE_VAULT_BUMP = offset!( - InputBufferHeader.market_data_header.base_vault_bump + /// From input buffer to market header next pointer. + MARKET_HEADER_NEXT = offset!(InputBufferHeader.market_header.next), + /// From input buffer to market header bump. + MARKET_HEADER_BUMP = offset!(InputBufferHeader.market_header.bump), + /// From input buffer to market header base vault bump. + MARKET_HEADER_BASE_VAULT_BUMP = offset!( + InputBufferHeader.market_header.base_vault_bump ), - /// From input buffer to market data quote vault bump. - MARKET_DATA_QUOTE_VAULT_BUMP = offset!( - InputBufferHeader.market_data_header.quote_vault_bump + /// From input buffer to market header quote vault bump. + MARKET_HEADER_QUOTE_VAULT_BUMP = offset!( + InputBufferHeader.market_header.quote_vault_bump + ), + /// From input buffer to first sector in market memory map. + MARKET_SECTORS_START = offset!(InputBufferHeader.market_sectors_start), + /// Absolute SBPF pointer to first sector in market memory map. + MARKET_SECTORS_START_PTR = wide!( + MM_INPUT_START as i64 + + core::mem::offset_of!(InputBufferHeader, market_sectors_start) as i64 ), - /// From input buffer to first byte after market data header. - MARKET_DATA_BYTES = offset!(InputBufferHeader.market_data_bytes), } } // endregion: constant_group_example diff --git a/interface/src/groups.rs b/interface/src/groups.rs index 54f2f35..60045f4 100644 --- a/interface/src/groups.rs +++ b/interface/src/groups.rs @@ -12,6 +12,7 @@ pub const INJECTION_GROUPS: &[&dropset_build::ConstantGroup] = &[ &crate::common::account::cpi::GROUP, &crate::common::memory::size_of::GROUP, &crate::common::memory::constants::GROUP, + &crate::common::memory::node_tag::GROUP, &crate::common::pubkey::constants::GROUP, &crate::common::token::constants::GROUP, ]; diff --git a/interface/src/market/register.rs b/interface/src/market/register.rs index 235e3d4..3498379 100644 --- a/interface/src/market/register.rs +++ b/interface/src/market/register.rs @@ -38,6 +38,7 @@ pub struct Data { } // endregion: register_market_data +// region: register_input_buffer #[svm_data] pub struct InputBuffer { pub n_accounts: u64, @@ -47,6 +48,7 @@ pub struct InputBuffer { pub base_mint: EmptyAccount, pub quote_mint: EmptyAccount, } +// endregion: register_input_buffer constant_group! { #[prefix("RM")] diff --git a/interface/src/order/mod.rs b/interface/src/order/mod.rs index c0ce667..9cc6437 100644 --- a/interface/src/order/mod.rs +++ b/interface/src/order/mod.rs @@ -1,4 +1,8 @@ use dropset_macros::svm_data; +// region: order #[svm_data] -pub struct Order {} +pub struct Order { + pub tag: u8, +} +// endregion: order diff --git a/interface/src/seat/mod.rs b/interface/src/seat/mod.rs index 57e7b09..1cde948 100644 --- a/interface/src/seat/mod.rs +++ b/interface/src/seat/mod.rs @@ -1,4 +1,21 @@ +use crate::order::Order; use dropset_macros::svm_data; +use pinocchio::Address as Pubkey; + +// region: seat +pub const MAX_ORDERS_PER_SIDE: usize = 5; #[svm_data] -pub struct Seat {} +pub struct Seat { + pub tag: u8, + pub parent: *mut Seat, + pub left: *mut Seat, + pub right: *mut Seat, + pub user: Pubkey, + pub base_available: u64, + pub quote_available: u64, + pub lamports_available: u64, + pub asks: [*mut Order; MAX_ORDERS_PER_SIDE], + pub bids: [*mut Order; MAX_ORDERS_PER_SIDE], +} +// endregion: seat diff --git a/interface/src/stack/mod.rs b/interface/src/stack/mod.rs index 6727aaf..be2e044 100644 --- a/interface/src/stack/mod.rs +++ b/interface/src/stack/mod.rs @@ -1,6 +1,9 @@ use dropset_macros::svm_data; +// region: stack_node #[svm_data] pub struct StackNode { + pub tag: u8, pub next: *mut StackNode, } +// endregion: stack_node diff --git a/macros/src/constant_group/expand/immediate.rs b/macros/src/constant_group/expand/immediate.rs index 31bb7d5..aec2af9 100644 --- a/macros/src/constant_group/expand/immediate.rs +++ b/macros/src/constant_group/expand/immediate.rs @@ -27,3 +27,29 @@ pub fn expand_immediate( (def, meta_ident) } + +/// Expand `wide!(expr)` into an i64 constant for `lddw`. Name gets `_WD` suffix. +pub fn expand_wide( + base_name: &Ident, + asm_name: &str, + doc: &str, + expr: &syn::Expr, +) -> (proc_macro2::TokenStream, Ident) { + let rust_name = Ident::new(&format!("{}_WD", base_name), base_name.span()); + let asm_name = format!("{}_WD", asm_name); + let meta_ident = codegen::meta_ident(&asm_name, base_name.span()); + + let meta = codegen::wide_meta(&meta_ident, &asm_name, doc, quote! { #rust_name }); + + let def = quote! { + #[doc = #doc] + pub const #rust_name: i64 = { + use super::*; + (#expr) as i64 + }; + + #meta + }; + + (def, meta_ident) +} diff --git a/macros/src/constant_group/expand/mod.rs b/macros/src/constant_group/expand/mod.rs index 4124e6d..ef3b4f5 100644 --- a/macros/src/constant_group/expand/mod.rs +++ b/macros/src/constant_group/expand/mod.rs @@ -93,6 +93,11 @@ pub fn expand(input: &ConstantGroupInput) -> proc_macro2::TokenStream { const_defs.push(def); meta_idents.push(meta); } + ConstantKind::Wide { expr } => { + let (def, meta) = immediate::expand_wide(base_name, &asm_name, doc, expr); + const_defs.push(def); + meta_idents.push(meta); + } ConstantKind::Pubkey { expr } => { pubkey::expand_pubkey(&asm_name, doc, expr, &mut const_defs, &mut meta_idents); } diff --git a/macros/src/constant_group/mod.rs b/macros/src/constant_group/mod.rs index e452f68..c2b2eaa 100644 --- a/macros/src/constant_group/mod.rs +++ b/macros/src/constant_group/mod.rs @@ -23,6 +23,8 @@ pub(crate) enum ConstantKind { }, /// `immediate!(expr)`: value must fit i32, exposed as i32 in Rust. Immediate { expr: Expr }, + /// `wide!(expr)`: 64-bit immediate for `lddw`, exposed as i64 in Rust. + Wide { expr: Expr }, /// `pubkey!(expr)`: splits a pubkey into four 8-byte chunks, emitting /// `_CHUNK_{0..3}_LO` and `_CHUNK_{0..3}_HI` i32 immediates. Pubkey { expr: Expr }, diff --git a/macros/src/constant_group/parse/mod.rs b/macros/src/constant_group/parse/mod.rs index 9f267da..ef711ec 100644 --- a/macros/src/constant_group/parse/mod.rs +++ b/macros/src/constant_group/parse/mod.rs @@ -72,6 +72,12 @@ impl Parse for ConstantGroupInput { let expr: Expr = inner.parse()?; ConstantKind::Immediate { expr } } + "wide" => { + let inner; + syn::parenthesized!(inner in content); + let expr: Expr = inner.parse()?; + ConstantKind::Wide { expr } + } "pubkey" => { let inner; syn::parenthesized!(inner in content); diff --git a/macros/src/lib.rs b/macros/src/lib.rs index b7f7ca2..ba695c5 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -86,30 +86,42 @@ pub fn cpi_accounts(input: TokenStream) -> TokenStream { TokenStream::from(cpi_accounts::expand(&input)) } -/// Attribute macro for instruction discriminant enums. +/// Attribute macro for discriminant enums. /// /// Re-emits the enum with `#[repr(u8)]` and explicit discriminant values, /// numbered from 0. Generates a `From for u8` impl and a hidden module -/// with `DISC_`-prefixed assembly constants. +/// with assembly constants prefixed by the given prefix (defaults to `DISC`). /// /// ```ignore -/// #[discriminant_enum("discriminant")] +/// #[discriminant_enum("entrypoint")] /// pub enum Discriminant { /// /// Register a new market. /// RegisterMarket, /// } +/// +/// #[discriminant_enum("market", "DISC_NODE")] +/// pub enum NodeTag { +/// /// Seat node. +/// Seat, +/// } /// ``` #[proc_macro_attribute] pub fn discriminant_enum(attr: TokenStream, item: TokenStream) -> TokenStream { - let target = parse_macro_input!(attr as LitStr); + let parser = |input: syn::parse::ParseStream| { + let target: LitStr = input.parse()?; + let prefix = if input.peek(syn::Token![,]) { + let _: syn::Token![,] = input.parse()?; + let p: LitStr = input.parse()?; + p.value() + } else { + "DISC".to_string() + }; + Ok((target.value(), prefix)) + }; + let (target, prefix) = parse_macro_input!(attr with parser); let input = parse_macro_input!(item as syn::ItemEnum); TokenStream::from(enum_to_asm::expand( - &target.value(), - "DISC", - 0, - "u8", - &input, - false, + &target, &prefix, 0, "u8", &input, false, )) } diff --git a/program/src/dropset/common/memory.s b/program/src/dropset/common/memory.s index 5b8fb3d..a131e84 100644 --- a/program/src/dropset/common/memory.s +++ b/program/src/dropset/common/memory.s @@ -5,6 +5,7 @@ .equ SIZE_OF_MARKET_HEADER, 43 # Size of MarketHeader in bytes. .equ SIZE_OF_CREATE_ACCOUNT_DATA, 52 # Size of CreateAccountData in bytes. .equ SIZE_OF_INITIALIZE_ACCOUNT2, 33 # Size of InitializeAccount2 in bytes. +.equ SIZE_OF_SECTOR, 161 # Size of Sector in bytes. # Common data-related constants. # ------------------------------------------------------------------------- @@ -16,3 +17,10 @@ .equ DATA_BOOL_FALSE, 0 # Boolean false value. .equ DATA_BOOL_TRUE, 1 # Boolean true value. # ------------------------------------------------------------------------- + +# Discriminant tag for nodes in the market memory map. +# ------------------------------------------------------------------------- +.equ NODE_TAG_SEAT, 0 # Seat node. +.equ NODE_TAG_ORDER, 1 # Order node. +.equ NODE_TAG_STACK, 2 # Stack node. +# ------------------------------------------------------------------------- diff --git a/program/src/dropset/entrypoint.s b/program/src/dropset/entrypoint.s index ce965a6..bc242f9 100644 --- a/program/src/dropset/entrypoint.s +++ b/program/src/dropset/entrypoint.s @@ -46,16 +46,18 @@ .equ IB_LAMPORTS_TO_DATA_REL_OFF_IMM, 16 # From user data to market address in the input buffer. .equ IB_USER_DATA_TO_MARKET_ADDRESS_REL_OFF_IMM, 10256 -# From input buffer to market data next pointer. -.equ IB_MARKET_DATA_NEXT_OFF, 10464 -# From input buffer to market data bump. -.equ IB_MARKET_DATA_BUMP_OFF, 10472 -# From input buffer to market data base vault bump. -.equ IB_MARKET_DATA_BASE_VAULT_BUMP_OFF, 10473 -# From input buffer to market data quote vault bump. -.equ IB_MARKET_DATA_QUOTE_VAULT_BUMP_OFF, 10474 -# From input buffer to first byte after market data header. -.equ IB_MARKET_DATA_BYTES_OFF, 10475 +# From input buffer to market header next pointer. +.equ IB_MARKET_HEADER_NEXT_OFF, 10464 +# From input buffer to market header bump. +.equ IB_MARKET_HEADER_BUMP_OFF, 10472 +# From input buffer to market header base vault bump. +.equ IB_MARKET_HEADER_BASE_VAULT_BUMP_OFF, 10473 +# From input buffer to market header quote vault bump. +.equ IB_MARKET_HEADER_QUOTE_VAULT_BUMP_OFF, 10474 +# From input buffer to first sector in market memory map. +.equ IB_MARKET_SECTORS_START_OFF, 10475 +# Absolute SBPF pointer to first sector in market memory map. +.equ IB_MARKET_SECTORS_START_PTR_WD, 17179879659 # ------------------------------------------------------------------------- entrypoint: diff --git a/program/src/dropset/market/init_base_vault.s b/program/src/dropset/market/init_base_vault.s index 5ed71b1..4c1c046 100644 --- a/program/src/dropset/market/init_base_vault.s +++ b/program/src/dropset/market/init_base_vault.s @@ -91,9 +91,9 @@ init_base_vault_advance: # if result != entrypoint::RETURN_SUCCESS # return result jne r0, RETURN_SUCCESS, init_base_vault_failed - # input.market.data.base_vault_bump = frame.bump + # input.market_header.base_vault_bump = frame.bump ldxb r7, [r10 + RM_FM_BUMP_OFF] - stxb [r8 + IB_MARKET_DATA_BASE_VAULT_BUMP_OFF], r7 + stxb [r8 + IB_MARKET_HEADER_BASE_VAULT_BUMP_OFF], r7 # acct += EmptyAccount.size add64 r9, SIZE_OF_EMPTY_ACCOUNT ja init_base_vault_return diff --git a/program/src/dropset/market/init_market_pda/create_market_account.s b/program/src/dropset/market/init_market_pda/create_market_account.s index 9f48a23..e1272ab 100644 --- a/program/src/dropset/market/init_market_pda/create_market_account.s +++ b/program/src/dropset/market/init_market_pda/create_market_account.s @@ -72,12 +72,11 @@ create_market_account: # syscall.seeds_len = market::register::N_PDA_SIGNERS mov64 r5, RM_N_PDA_SIGNERS call sol_invoke_signed_c - # input.market.data.next = input + entrypoint::input_buffer::MARKET_DATA_BYTES + # input.market_header.next = &entrypoint::input_buffer::MARKET_SECTORS_START ldxdw r6, [r10 + RM_FM_INPUT_OFF] - mov64 r7, r6 - add64 r7, IB_MARKET_DATA_BYTES_OFF - stxdw [r6 + IB_MARKET_DATA_NEXT_OFF], r7 - # input.market.data.bump = frame.bump + lddw r7, IB_MARKET_SECTORS_START_PTR_WD + stxdw [r6 + IB_MARKET_HEADER_NEXT_OFF], r7 + # input.market_header.bump = frame.bump ldxb r7, [r10 + RM_FM_BUMP_OFF] - stxb [r6 + IB_MARKET_DATA_BUMP_OFF], r7 + stxb [r6 + IB_MARKET_HEADER_BUMP_OFF], r7 ja create_market_account_return diff --git a/program/src/dropset/market/init_quote_vault.s b/program/src/dropset/market/init_quote_vault.s index 6bc60e7..de6843e 100644 --- a/program/src/dropset/market/init_quote_vault.s +++ b/program/src/dropset/market/init_quote_vault.s @@ -102,7 +102,7 @@ init_quote_vault_done_token_program: mov64 r1, r9 mov64 r2, r10 call init_vault - # input.market.data.quote_vault_bump = frame.bump + # input.market_header.quote_vault_bump = frame.bump ldxb r7, [r10 + RM_FM_BUMP_OFF] - stxb [r8 + IB_MARKET_DATA_QUOTE_VAULT_BUMP_OFF], r7 + stxb [r8 + IB_MARKET_HEADER_QUOTE_VAULT_BUMP_OFF], r7 ja init_quote_vault_return diff --git a/tests/tests/cases/register_market.rs b/tests/tests/cases/register_market.rs index 80b559f..2e1f554 100644 --- a/tests/tests/cases/register_market.rs +++ b/tests/tests/cases/register_market.rs @@ -1,6 +1,6 @@ use dropset_interface::common::pubkey::constants::CHUNK_3_OFF; use dropset_interface::common::pubkey::{TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID}; -use dropset_interface::entrypoint::input_buffer::MARKET_DATA_BYTES_OFF; +use dropset_interface::entrypoint::input_buffer::MARKET_SECTORS_START_OFF; use dropset_interface::market::MarketHeader; use dropset_interface::market::constants::{VAULT_INDEX_BASE, VAULT_INDEX_QUOTE}; use dropset_interface::market::register::Accounts; @@ -251,7 +251,7 @@ fn check_market_header_bumps( ) { let header: &MarketHeader = unsafe { &*(data.as_ptr() as *const MarketHeader) }; - let expected_next = MM_INPUT_START + MARKET_DATA_BYTES_OFF as u64; + let expected_next = MM_INPUT_START + MARKET_SECTORS_START_OFF as u64; let actual_next = header.next as u64; if actual_next != expected_next { errors.push(format!(