diff --git a/docs/algorithms/ENTRYPOINT.tex b/docs/algorithms/ENTRYPOINT.tex index 5c20040..a097be3 100644 --- a/docs/algorithms/ENTRYPOINT.tex +++ b/docs/algorithms/ENTRYPOINT.tex @@ -5,12 +5,14 @@ \INPUT $r_1$ = input \COMMENT{Pointer to instruction data.} \INPUT $r_2$ = insn + \COMMENT{Pointer to stack frame.} + \INPUT $r_{10}$ = frame \PROCEDURE{ENTRYPOINT}{input, insn} \STATE n\_accounts = input.n\_accounts \STATE insn\_len = insn.length \STATE insn\_disc = insn.discriminant \IF{insn\_disc == \texttt{Discriminant::RegisterMarket}} - \RETURN \CALL{REGISTER-MARKET}{input, insn, n\_accounts, insn\_len} + \RETURN \CALL{REGISTER-MARKET}{input, insn, n\_accounts, insn\_len, frame} \ENDIF \RETURN \texttt{ErrorCode::InvalidDiscriminant} \ENDPROCEDURE diff --git a/docs/algorithms/INIT-VAULT.tex b/docs/algorithms/INIT-VAULT.tex new file mode 100644 index 0000000..a1f3844 --- /dev/null +++ b/docs/algorithms/INIT-VAULT.tex @@ -0,0 +1,44 @@ +\begin{algorithm} +\caption{INIT-VAULT} +\begin{algorithmic} + \COMMENT{Pointer to vault account.} + \INPUT $r_1$ = acct + \COMMENT{Pointer to REGISTER-MARKET stack frame.} + \INPUT $r_2$ = frame + \REQUIRE frame.vault\_index $\in$ \{ + \texttt{register\_misc.VAULT\_INDEX\_BASE}, + \texttt{register\_misc.VAULT\_INDEX\_QUOTE} + \} + \REQUIRE frame.token\_program\_is\_2022 $\in$ \{ + \texttt{true}, + \texttt{false} + \} + \COMMENT{Pointer to owning token program address.} + \REQUIRE frame.token\_program\_id + \COMMENT{Pointer to input buffer.} + \REQUIRE frame.input + \REQUIRE frame.pda\_seeds[0].len = \texttt{Address.size} +\FUNCTION{INIT-VAULT}{acct, frame} + \STATE \CALL{Store}{frame} + \STATE \CALL{Store}{acct} + \COMMENT{Derive vault PDA.} + \STATE frame.pda\_seeds[0].addr = \&input.market.address + \STATE frame.pda\_seeds[1].addr = \&frame.vault\_index + \STATE frame.pda\_seeds[1].len = \texttt{u8.size} + \STATE syscall.seeds = \&frame.pda\_seeds + \STATE syscall.seeds\_len = \texttt{register\_misc.TRY\_FIND\_VAULT\_PDA\_SEEDS\_LEN} + \STATE syscall.program\_id = frame.token\_program\_id + \STATE syscall.program\_address = \&frame.pda + \STATE syscall.bump\_seed = \&frame.bump + \STATE \CALL{sol-try-find-program-address}{} + \COMMENT{Verify vault address matches derived PDA.} + \IF{acct.address $\neq$ frame.pda} + \IF{frame.vault\_index == \texttt{register\_misc.VAULT\_INDEX\_BASE}} + \RETURN \texttt{ErrorCode::InvalidBaseVaultPubkey} + \ELSE + \RETURN \texttt{ErrorCode::InvalidQuoteVaultPubkey} + \ENDIF + \ENDIF +\ENDFUNCTION +\end{algorithmic} +\end{algorithm} diff --git a/docs/algorithms/REGISTER-MARKET.tex b/docs/algorithms/REGISTER-MARKET.tex index d257278..c22b373 100644 --- a/docs/algorithms/REGISTER-MARKET.tex +++ b/docs/algorithms/REGISTER-MARKET.tex @@ -9,8 +9,10 @@ \INPUT $r_3$ = n\_accounts \COMMENT{Instruction data length.} \INPUT $r_4$ = insn\_len + \COMMENT{Pointer to stack frame.} + \INPUT $r_{10}$ = frame \REQUIRE insn.discriminant == \texttt{Discriminant::RegisterMarket} -\PROCEDURE{REGISTER-MARKET}{input, insn, n\_accounts, insn\_len} +\PROCEDURE{REGISTER-MARKET}{input, insn, n\_accounts, insn\_len, frame} \COMMENT{Check number of accounts and instruction data length.} \IF{n\_accounts $<$ \texttt{RegisterMarketAccounts.LEN}} \RETURN \texttt{ErrorCode::InvalidNumberOfAccounts} @@ -32,7 +34,7 @@ \IF{input.base\_mint.duplicate $\neq$ \texttt{account.NON\_DUP\_MARKER}} \RETURN \texttt{ErrorCode::BaseMintIsDuplicate} \ENDIF - \STATE frame.pda\_seeds[0].addr = input.base\_mint.pubkey + \STATE frame.pda\_seeds[0].addr = input.base\_mint.address \STATE frame.pda\_seeds[0].len = \texttt{Address.size} \COMMENT{Increment input buffer by base mint padded data length for quote offsets.} \STATE input\_shifted = input + input.base\_mint.padded\_data\_len @@ -41,7 +43,7 @@ \IF{input\_shifted.quote\_mint.duplicate $\neq$ \texttt{account.NON\_DUP\_MARKER}} \RETURN \texttt{ErrorCode::QuoteMintIsDuplicate} \ENDIF - \STATE frame.pda\_seeds[1].addr = input\_shifted.quote\_mint.pubkey + \STATE frame.pda\_seeds[1].addr = input\_shifted.quote\_mint.address \STATE frame.pda\_seeds[1].len = \texttt{Address.size} \COMMENT{Advance to System Program account.} \STATE quote\_mint\_padded\_data\_len = input\_shifted.quote\_mint.padded\_data\_len @@ -55,9 +57,9 @@ \STATE syscall.program\_address = \&frame.pda \STATE syscall.bump\_seed = \&frame.bump \STATE \CALL{sol-try-find-program-address}{} - \COMMENT{Verify derived market PDA matches market account pubkey.} + \COMMENT{Verify derived market PDA matches market account address.} \STATE input = frame.input - \IF{input.market.pubkey $\neq$ frame.market\_pda} + \IF{input.market.address $\neq$ frame.market\_pda} \RETURN \texttt{ErrorCode::InvalidMarketPubkey} \ENDIF \COMMENT{Populate bump signer seed from derived bump.} @@ -69,7 +71,7 @@ \IF{acct.duplicate $\neq$ \texttt{account.NON\_DUP\_MARKER}} \RETURN \texttt{ErrorCode::SystemProgramIsDuplicate} \ENDIF - \IF{acct.pubkey $\neq$ frame.system\_program\_pubkey} + \IF{acct.address $\neq$ frame.system\_program\_pubkey} \RETURN \texttt{ErrorCode::InvalidSystemProgramPubkey} \ENDIF \COMMENT{Populate CPI program ID field.} @@ -81,7 +83,7 @@ \IF{acct.duplicate $\neq$ \texttt{account.NON\_DUP\_MARKER}} \RETURN \texttt{ErrorCode::RentSysvarIsDuplicate} \ENDIF - \IF{acct.pubkey $\neq$ \texttt{pubkey.RENT}} + \IF{acct.address $\neq$ \texttt{pubkey.RENT}} \RETURN \texttt{ErrorCode::InvalidRentSysvarPubkey} \ENDIF \COMMENT{Prepare CreateAccount instruction lamports, space fields.} @@ -137,32 +139,25 @@ \RETURN \texttt{ErrorCode::BaseTokenProgramIsDuplicate} \ENDIF \COMMENT{Verify base token program owns the base mint.} - \IF{acct.pubkey $\neq$ input.base\_mint.owner} + \IF{acct.address $\neq$ input.base\_mint.owner} \RETURN \texttt{ErrorCode::BaseTokenProgramNotBaseMintOwner} \ENDIF \COMMENT{Verify base token program is Token Program or Token 2022.} - \IF{acct.pubkey $\neq$ \texttt{pubkey.TOKEN\_PROGRAM}} - \IF{acct.pubkey $\neq$ \texttt{pubkey.TOKEN\_2022\_PROGRAM}} + \IF{acct.address $\neq$ \texttt{pubkey.TOKEN\_PROGRAM}} + \IF{acct.address $\neq$ \texttt{pubkey.TOKEN\_2022\_PROGRAM}} \RETURN \texttt{ErrorCode::BaseTokenProgramNotTokenProgram} \ENDIF + \STATE frame.token\_program\_is\_2022 = \texttt{true} \ENDIF - \COMMENT{Derive base vault PDA from base token program.} - \STATE frame.pda\_seeds[0].addr = \&input.market.address - \STATE frame.vault\_index = \texttt{register\_misc.VAULT\_INDEX\_BASE} - \STATE frame.pda\_seeds[1].addr = \&frame.vault\_index - \STATE frame.pda\_seeds[1].len = \texttt{u8.size} - \STATE syscall.seeds = \&frame.pda\_seeds - \STATE syscall.seeds\_len = \texttt{register\_misc.TRY\_FIND\_VAULT\_PDA\_SEEDS\_LEN} - \STATE syscall.program\_id = \&acct.address - \STATE syscall.program\_address = \&frame.pda - \STATE syscall.bump\_seed = \&frame.bump - \STATE \CALL{sol-try-find-program-address}{} + \STATE frame.token\_program\_id = \&acct.address \COMMENT{Advance to base vault account.} \STATE base\_token\_program\_padded\_data\_len = acct.padded\_data\_len \STATE acct += base\_token\_program\_padded\_data\_len + \texttt{EmptyAccount.size} - \COMMENT{Verify base vault address matches derived PDA.} - \IF{acct.pubkey $\neq$ frame.pda} - \RETURN \texttt{ErrorCode::InvalidBaseVaultPubkey} + \COMMENT{Initialize base vault account.} + \STATE frame.vault\_index = \texttt{register\_misc.VAULT\_INDEX\_BASE} + \STATE result = \CALL{INIT-VAULT}{acct, frame} + \IF{result $\neq$ \texttt{entrypoint.RETURN\_SUCCESS}} + \RETURN result \ENDIF \COMMENT{Advance to quote token program account.} \STATE base\_vault\_padded\_data\_len = acct.padded\_data\_len @@ -170,18 +165,20 @@ \COMMENT{Check quote token program account.} \IF{acct.duplicate == \texttt{account.NON\_DUP\_MARKER}} \COMMENT{Non-duplicate; verify quote token program owns the quote mint.} - \IF{acct.pubkey $\neq$ input\_shifted.quote\_mint.owner} + \IF{acct.address $\neq$ input\_shifted.quote\_mint.owner} \RETURN \texttt{ErrorCode::NonDupQuoteTokenProgramNotQuoteMintOwner} \ENDIF \COMMENT{Verify quote token program is Token Program or Token 2022.} - \IF{acct.pubkey $\neq$ \texttt{pubkey.TOKEN\_PROGRAM}} - \IF{acct.pubkey $\neq$ \texttt{pubkey.TOKEN\_2022\_PROGRAM}} + \IF{acct.address $\neq$ \texttt{pubkey.TOKEN\_PROGRAM}} + \IF{acct.address $\neq$ \texttt{pubkey.TOKEN\_2022\_PROGRAM}} \RETURN \texttt{ErrorCode::QuoteTokenProgramNotTokenProgram} \ENDIF + \STATE frame.token\_program\_is\_2022 = \texttt{true} + \ELSE + \STATE frame.token\_program\_is\_2022 = \texttt{false} \ENDIF - \COMMENT{Update syscall program ID from non-duplicate quote token program.} - \STATE syscall.program\_id = \&acct.address - \COMMENT{Advance past non-duplicate quote token program account.} + \STATE frame.token\_program\_id = \&acct.address + \COMMENT{Advance to quote vault account.} \STATE quote\_token\_program\_padded\_data\_len = acct.padded\_data\_len \STATE acct += quote\_token\_program\_padded\_data\_len + \texttt{EmptyAccount.size} \ELSE @@ -193,16 +190,12 @@ \IF{input.base\_mint.owner $\neq$ input\_shifted.quote\_mint.owner} \RETURN \texttt{ErrorCode::DupQuoteTokenProgramNotQuoteMintOwner} \ENDIF - \COMMENT{Advance past duplicate quote token program account.} + \COMMENT{Advance to quote vault account.} \STATE acct += \texttt{u64.size} \ENDIF - \COMMENT{Derive quote vault PDA.} + \COMMENT{Initialize quote vault account.} \STATE frame.vault\_index = \texttt{register\_misc.VAULT\_INDEX\_QUOTE} - \STATE \CALL{sol-try-find-program-address}{} - \COMMENT{Verify quote vault address matches derived PDA.} - \IF{acct.pubkey $\neq$ frame.pda} - \RETURN \texttt{ErrorCode::InvalidQuoteVaultPubkey} - \ENDIF + \STATE \CALL{INIT-VAULT}{acct, frame} \ENDPROCEDURE \end{algorithmic} \end{algorithm} diff --git a/docs/src/program/layout.md b/docs/src/program/layout.md index 91063bb..913000d 100644 --- a/docs/src/program/layout.md +++ b/docs/src/program/layout.md @@ -16,6 +16,7 @@ program/src/dropset/ │ ├── memory.s # Memory layout constants │ └── pubkey.s # Pubkey chunk offsets and known addresses └── market/ + ├── init_vault.s # InitVault function └── register.s # RegisterMarket handler ``` diff --git a/docs/src/program/markets.md b/docs/src/program/markets.md index dfa3611..60b714a 100644 --- a/docs/src/program/markets.md +++ b/docs/src/program/markets.md @@ -22,4 +22,8 @@ The instruction requires the following accounts: +## Vault initialization + + + [input buffer]: inputs#input-buffer diff --git a/interface/src/lib.rs b/interface/src/lib.rs index c325f17..a135690 100644 --- a/interface/src/lib.rs +++ b/interface/src/lib.rs @@ -74,6 +74,8 @@ constant_group! { INSN_LEN = offset!(-size_of::()), /// Offset from instruction data to discriminant, in input buffer. INSN_DISC = offset!(0), + /// Successful return code. + RETURN_SUCCESS = immediate!(0), } } diff --git a/interface/src/market.rs b/interface/src/market.rs index 9745500..0af45b0 100644 --- a/interface/src/market.rs +++ b/interface/src/market.rs @@ -147,6 +147,10 @@ signer_seeds! { #[frame] /// Stack frame for REGISTER-MARKET. pub struct RegisterMarketFrame { + /// Saved acct pointer across INIT-VAULT syscall. + pub acct: u64, + /// Pointer to owning token program address. + pub token_program_id: u64, /// Saved input buffer pointer. pub input: u64, /// Saved input_shifted pointer. @@ -169,6 +173,8 @@ pub struct RegisterMarketFrame { pub bump: u8, /// Vault index for PDA derivation. pub vault_index: u8, + /// Whether the current token program is Token 2022. + pub token_program_is_2022: u8, } // endregion: frame_example @@ -177,6 +183,10 @@ constant_group! { #[inject("market/register")] #[frame(RegisterMarketFrame)] frame { + /// Saved acct pointer across INIT-VAULT syscall. + ACCT = offset!(acct), + /// Pointer to owning token program address. + TOKEN_PROGRAM_ID = offset!(token_program_id), /// Saved input buffer pointer. INPUT = offset!(input), /// Saved input_shifted pointer. @@ -207,6 +217,8 @@ constant_group! { BUMP = offset!(bump), /// Vault index for PDA derivation. VAULT_INDEX = unaligned_offset!(vault_index), + /// Whether the current token program is Token 2022. + TOKEN_PROGRAM_IS_2022 = unaligned_offset!(token_program_is_2022), /// From pda_seeds to sol_instruction. PDA_SEEDS_TO_SOL_INSN = relative_offset!(pda_seeds, sol_instruction), /// From pda to signers_seeds. diff --git a/interface/src/memory.rs b/interface/src/memory.rs index aac1bb3..9c8fb7e 100644 --- a/interface/src/memory.rs +++ b/interface/src/memory.rs @@ -24,6 +24,10 @@ constant_group! { LEN_MAX_PAD = immediate!(7), /// And mask for data length alignment. LEN_AND_MASK = immediate!(-8), + /// Boolean false value. + BOOL_FALSE = immediate!(0), + /// Boolean true value. + BOOL_TRUE = immediate!(1), } } diff --git a/program/src/dropset/common/error.s b/program/src/dropset/common/error.s index 7951c64..4e78e23 100644 --- a/program/src/dropset/common/error.s +++ b/program/src/dropset/common/error.s @@ -116,7 +116,3 @@ e_quote_token_program_not_token_program: e_invalid_base_vault_pubkey: mov32 r0, E_INVALID_BASE_VAULT_PUBKEY exit - -e_invalid_quote_vault_pubkey: - mov32 r0, E_INVALID_QUOTE_VAULT_PUBKEY - exit diff --git a/program/src/dropset/common/memory.s b/program/src/dropset/common/memory.s index a244370..394483e 100644 --- a/program/src/dropset/common/memory.s +++ b/program/src/dropset/common/memory.s @@ -35,6 +35,8 @@ # Maximum possible data length padding for a runtime account. .equ DATA_LEN_MAX_PAD, 7 .equ DATA_LEN_AND_MASK, -8 # And mask for data length alignment. +.equ DATA_BOOL_FALSE, 0 # Boolean false value. +.equ DATA_BOOL_TRUE, 1 # Boolean true value. # ------------------------------------------------------------------------- # Input buffer constants for static header. diff --git a/program/src/dropset/dropset.s b/program/src/dropset/dropset.s index 70d6e2f..5575b3e 100644 --- a/program/src/dropset/dropset.s +++ b/program/src/dropset/dropset.s @@ -7,3 +7,4 @@ .include "common/pubkey.s" .include "entrypoint.s" .include "market/register.s" +.include "market/init_vault.s" diff --git a/program/src/dropset/entrypoint.s b/program/src/dropset/entrypoint.s index b868306..b9d6f24 100644 --- a/program/src/dropset/entrypoint.s +++ b/program/src/dropset/entrypoint.s @@ -4,6 +4,7 @@ .equ INSN_LEN_OFF, -8 # Offset from instruction data to discriminant, in input buffer. .equ INSN_DISC_OFF, 0 +.equ RETURN_SUCCESS, 0 # Successful return code. entrypoint: # n_accounts = input.n_accounts diff --git a/program/src/dropset/market/init_vault.s b/program/src/dropset/market/init_vault.s new file mode 100644 index 0000000..d868784 --- /dev/null +++ b/program/src/dropset/market/init_vault.s @@ -0,0 +1,53 @@ +init_vault: + # Store(frame) + mov64 r6, r2 + # Store(acct) + mov64 r7, r1 + # frame.pda_seeds[0].addr = &input.market.address + ldxdw r8, [r6 + RM_FM_INPUT_OFF] + add64 r8, IB_MARKET_PUBKEY_OFF + stxdw [r6 + RM_FM_PDA_SEEDS_IDX_0_ADDR_OFF], r8 + # frame.pda_seeds[1].addr = &frame.vault_index + mov64 r8, r6 + add64 r8, RM_FM_VAULT_INDEX_UOFF + stxdw [r6 + RM_FM_PDA_SEEDS_IDX_1_ADDR_OFF], r8 + # frame.pda_seeds[1].len = u8.size + mov64 r8, SIZE_OF_U8 + stxdw [r6 + RM_FM_PDA_SEEDS_IDX_1_LEN_OFF], r8 + # syscall.seeds = &frame.pda_seeds + mov64 r1, r6 + add64 r1, RM_FM_PDA_SEEDS_OFF + # syscall.seeds_len = register_misc.TRY_FIND_VAULT_PDA_SEEDS_LEN + mov64 r2, RM_MISC_TRY_FIND_VAULT_PDA_SEEDS_LEN + # syscall.program_id = frame.token_program_id + ldxdw r3, [r6 + RM_FM_TOKEN_PROGRAM_ID_OFF] + # syscall.program_address = &frame.pda + mov64 r4, r6 + add64 r4, RM_FM_PDA_OFF + # syscall.bump_seed = &frame.bump + mov64 r5, r6 + add64 r5, RM_FM_BUMP_OFF + call sol_try_find_program_address + # if acct.address != frame.pda + ldxdw r1, [r7 + ACCT_ADDRESS_CHUNK_0_OFF] + ldxdw r2, [r6 + RM_FM_PDA_CHUNK_0_OFF] + jne r1, r2, init_vault_invalid_pda + ldxdw r1, [r7 + ACCT_ADDRESS_CHUNK_1_OFF] + ldxdw r2, [r6 + RM_FM_PDA_CHUNK_1_OFF] + jne r1, r2, init_vault_invalid_pda + ldxdw r1, [r7 + ACCT_ADDRESS_CHUNK_2_OFF] + ldxdw r2, [r6 + RM_FM_PDA_CHUNK_2_OFF] + jne r1, r2, init_vault_invalid_pda + ldxdw r1, [r7 + ACCT_ADDRESS_CHUNK_3_OFF] + ldxdw r2, [r6 + RM_FM_PDA_CHUNK_3_OFF] + jne r1, r2, init_vault_invalid_pda + exit +init_vault_invalid_pda: + # if frame.vault_index == register_misc.VAULT_INDEX_BASE + # return ErrorCode::InvalidBaseVaultPubkey + # else + # return ErrorCode::InvalidQuoteVaultPubkey + ldxb r1, [r6 + RM_FM_VAULT_INDEX_UOFF] + jeq r1, RM_MISC_VAULT_INDEX_BASE, e_invalid_base_vault_pubkey + mov32 r0, E_INVALID_QUOTE_VAULT_PUBKEY + exit diff --git a/program/src/dropset/market/register.s b/program/src/dropset/market/register.s index 8f243e6..1334a86 100644 --- a/program/src/dropset/market/register.s +++ b/program/src/dropset/market/register.s @@ -24,6 +24,9 @@ # Stack frame for REGISTER-MARKET. # ------------------------------------------------------------------------- +.equ RM_FM_ACCT_OFF, -480 # Saved acct pointer across INIT-VAULT syscall. +# Pointer to owning token program address. +.equ RM_FM_TOKEN_PROGRAM_ID_OFF, -472 .equ RM_FM_INPUT_OFF, -464 # Saved input buffer pointer. .equ RM_FM_INPUT_SHIFTED_OFF, -456 # Saved input_shifted pointer. .equ RM_FM_PDA_SEEDS_OFF, -448 # Signer seeds offset. @@ -139,6 +142,8 @@ .equ RM_FM_SOL_INSN_DATA_LEN_UOFF, -16 # SolInstruction data length. .equ RM_FM_BUMP_OFF, -8 # Bump seed. .equ RM_FM_VAULT_INDEX_UOFF, -7 # Vault index for PDA derivation. +# Whether the current token program is Token 2022. +.equ RM_FM_TOKEN_PROGRAM_IS_2022_UOFF, -6 # From pda_seeds to sol_instruction. .equ RM_FM_PDA_SEEDS_TO_SOL_INSN_REL_OFF_IMM, 400 # From pda to signers_seeds. @@ -483,35 +488,13 @@ register_market_check_base_token_2022: ldxdw r7, [r9 + ACCT_ADDRESS_CHUNK_3_OFF] lddw r1, PUBKEY_TOKEN_2022_PROGRAM_CHUNK_3 jne r7, r1, e_base_token_program_not_token_program + # frame.token_program_is_2022 = true + stb [r10 + RM_FM_TOKEN_PROGRAM_IS_2022_UOFF], DATA_BOOL_TRUE register_market_base_vault: - # frame.pda_seeds[0].addr = &input.market.address - mov64 r7, r8 - add64 r7, IB_MARKET_PUBKEY_OFF - stxdw [r10 + RM_FM_PDA_SEEDS_IDX_0_ADDR_OFF], r7 - # frame.vault_index = register_misc.VAULT_INDEX_BASE - stb [r10 + RM_FM_VAULT_INDEX_UOFF], RM_MISC_VAULT_INDEX_BASE - # frame.pda_seeds[1].addr = &frame.vault_index - mov64 r7, r10 - add64 r7, RM_FM_VAULT_INDEX_UOFF - stxdw [r10 + RM_FM_PDA_SEEDS_IDX_1_ADDR_OFF], r7 - # frame.pda_seeds[1].len = u8.size - mov64 r7, SIZE_OF_U8 - stxdw [r10 + RM_FM_PDA_SEEDS_IDX_1_LEN_OFF], r7 - # syscall.seeds = &frame.pda_seeds - mov64 r1, r10 - add64 r1, RM_FM_PDA_SEEDS_OFF - # syscall.seeds_len = register_misc.TRY_FIND_VAULT_PDA_SEEDS_LEN - mov64 r2, RM_MISC_TRY_FIND_VAULT_PDA_SEEDS_LEN - # syscall.program_id = &acct.address - mov64 r3, r9 - add64 r3, ACCT_ADDRESS_OFF - # syscall.program_address = &frame.pda - mov64 r4, r10 - add64 r4, RM_FM_PDA_OFF - # syscall.bump_seed = &frame.bump - mov64 r5, r10 - add64 r5, RM_FM_BUMP_OFF - call sol_try_find_program_address + # frame.token_program_id = &acct.address + mov64 r7, r9 + add64 r7, ACCT_ADDRESS_OFF + stxdw [r10 + RM_FM_TOKEN_PROGRAM_ID_OFF], r7 # base_token_program_padded_data_len = acct.padded_data_len ldxdw r7, [r9 + ACCT_DATA_LEN_OFF] add64 r7, DATA_LEN_MAX_PAD @@ -519,20 +502,17 @@ register_market_base_vault: # acct += base_token_program_padded_data_len + EmptyAccount.size add64 r9, r7 add64 r9, SIZE_OF_EMPTY_ACCOUNT - # if acct.pubkey != frame.pda - # return ErrorCode::InvalidBaseVaultPubkey - ldxdw r7, [r9 + ACCT_ADDRESS_CHUNK_0_OFF] - ldxdw r2, [r10 + RM_FM_PDA_CHUNK_0_OFF] - jne r7, r2, e_invalid_base_vault_pubkey - ldxdw r7, [r9 + ACCT_ADDRESS_CHUNK_1_OFF] - ldxdw r2, [r10 + RM_FM_PDA_CHUNK_1_OFF] - jne r7, r2, e_invalid_base_vault_pubkey - ldxdw r7, [r9 + ACCT_ADDRESS_CHUNK_2_OFF] - ldxdw r2, [r10 + RM_FM_PDA_CHUNK_2_OFF] - jne r7, r2, e_invalid_base_vault_pubkey - ldxdw r7, [r9 + ACCT_ADDRESS_CHUNK_3_OFF] - ldxdw r2, [r10 + RM_FM_PDA_CHUNK_3_OFF] - jne r7, r2, e_invalid_base_vault_pubkey + # frame.vault_index = register_misc.VAULT_INDEX_BASE + stb [r10 + RM_FM_VAULT_INDEX_UOFF], RM_MISC_VAULT_INDEX_BASE + # result = INIT-VAULT(acct, frame) + mov64 r1, r9 + mov64 r2, r10 + call init_vault + # if result != entrypoint.RETURN_SUCCESS + # return result + jeq r0, RETURN_SUCCESS, register_market_quote_token_program + exit +register_market_quote_token_program: # base_vault_padded_data_len = acct.padded_data_len ldxdw r7, [r9 + ACCT_DATA_LEN_OFF] add64 r7, DATA_LEN_MAX_PAD @@ -571,7 +551,7 @@ register_market_base_vault: jne r7, r2, register_market_check_quote_token_2022 ldxdw r7, [r9 + ACCT_ADDRESS_CHUNK_3_OFF] lddw r2, PUBKEY_TOKEN_PROGRAM_CHUNK_3 - jeq r7, r2, register_market_advance_quote_non_dup + jeq r7, r2, register_market_quote_is_token_program register_market_check_quote_token_2022: ldxdw r7, [r9 + ACCT_ADDRESS_CHUNK_0_OFF] lddw r2, PUBKEY_TOKEN_2022_PROGRAM_CHUNK_0 @@ -585,10 +565,17 @@ register_market_check_quote_token_2022: ldxdw r7, [r9 + ACCT_ADDRESS_CHUNK_3_OFF] lddw r2, PUBKEY_TOKEN_2022_PROGRAM_CHUNK_3 jne r7, r2, e_quote_token_program_not_token_program + # frame.token_program_is_2022 = true + stb [r10 + RM_FM_TOKEN_PROGRAM_IS_2022_UOFF], DATA_BOOL_TRUE + ja register_market_advance_quote_non_dup +register_market_quote_is_token_program: + # frame.token_program_is_2022 = false + stb [r10 + RM_FM_TOKEN_PROGRAM_IS_2022_UOFF], DATA_BOOL_FALSE register_market_advance_quote_non_dup: - # syscall.program_id = &acct.address - mov64 r3, r9 - add64 r3, ACCT_ADDRESS_OFF + # frame.token_program_id = &acct.address + mov64 r7, r9 + add64 r7, ACCT_ADDRESS_OFF + stxdw [r10 + RM_FM_TOKEN_PROGRAM_ID_OFF], r7 # quote_token_program_padded_data_len = acct.padded_data_len ldxdw r7, [r9 + ACCT_DATA_LEN_OFF] add64 r7, DATA_LEN_MAX_PAD @@ -615,29 +602,13 @@ register_market_base_vault_dup: ldxdw r7, [r8 + RM_MISC_BASE_OWNER_CHUNK_3_OFF] ldxdw r2, [r6 + RM_MISC_QUOTE_OWNER_CHUNK_3_OFF] jne r7, r2, e_dup_quote_token_program_not_quote_mint_owner - # syscall.program_id = &input.base_mint.owner - mov64 r3, r8 - add64 r3, RM_MISC_BASE_OWNER_OFF # acct += u64.size add64 r9, SIZE_OF_U64 register_market_done_token_programs: # frame.vault_index = register_misc.VAULT_INDEX_QUOTE stb [r10 + RM_FM_VAULT_INDEX_UOFF], RM_MISC_VAULT_INDEX_QUOTE - # syscall.seeds_len = register_misc.TRY_FIND_VAULT_PDA_SEEDS_LEN - mov64 r2, RM_MISC_TRY_FIND_VAULT_PDA_SEEDS_LEN - call sol_try_find_program_address - # if acct.pubkey != frame.pda - # return ErrorCode::InvalidQuoteVaultPubkey - ldxdw r7, [r9 + ACCT_ADDRESS_CHUNK_0_OFF] - ldxdw r1, [r10 + RM_FM_PDA_CHUNK_0_OFF] - jne r7, r1, e_invalid_quote_vault_pubkey - ldxdw r7, [r9 + ACCT_ADDRESS_CHUNK_1_OFF] - ldxdw r1, [r10 + RM_FM_PDA_CHUNK_1_OFF] - jne r7, r1, e_invalid_quote_vault_pubkey - ldxdw r7, [r9 + ACCT_ADDRESS_CHUNK_2_OFF] - ldxdw r1, [r10 + RM_FM_PDA_CHUNK_2_OFF] - jne r7, r1, e_invalid_quote_vault_pubkey - ldxdw r7, [r9 + ACCT_ADDRESS_CHUNK_3_OFF] - ldxdw r1, [r10 + RM_FM_PDA_CHUNK_3_OFF] - jne r7, r1, e_invalid_quote_vault_pubkey + # INIT-VAULT(acct, frame) + mov64 r1, r9 + mov64 r2, r10 + call init_vault exit diff --git a/tests/tests/cases/register_market.rs b/tests/tests/cases/register_market.rs index d74e2a5..c0ec192 100644 --- a/tests/tests/cases/register_market.rs +++ b/tests/tests/cases/register_market.rs @@ -77,6 +77,8 @@ test_cases! { InvalidQuoteVaultPubkeyNonDupChunk3, CreateAccountHappyPathQuoteDup, CreateAccountHappyPathQuoteNonDup, + CreateAccountHappyPathToken2022QuoteDup, + CreateAccountHappyPathToken2022QuoteNonDup, } } @@ -1028,6 +1030,7 @@ impl TestCase for Case { ) } // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT Self::InvalidBaseVaultPubkeyChunk0 => { let (metas, accounts) = base_vault_mismatch_accounts(setup, CHUNK_0_OFF as usize); check_custom( @@ -1039,6 +1042,7 @@ impl TestCase for Case { ) } // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT Self::InvalidBaseVaultPubkeyChunk1 => { let (metas, accounts) = base_vault_mismatch_accounts(setup, CHUNK_1_OFF as usize); check_custom( @@ -1050,6 +1054,7 @@ impl TestCase for Case { ) } // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT Self::InvalidBaseVaultPubkeyChunk2 => { let (metas, accounts) = base_vault_mismatch_accounts(setup, CHUNK_2_OFF as usize); check_custom( @@ -1061,6 +1066,7 @@ impl TestCase for Case { ) } // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT Self::InvalidBaseVaultPubkeyChunk3 => { let (metas, accounts) = base_vault_mismatch_accounts(setup, CHUNK_3_OFF as usize); check_custom( @@ -1072,6 +1078,7 @@ impl TestCase for Case { ) } // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT Self::InvalidQuoteVaultPubkeyDupChunk0 => { let (metas, accounts) = quote_vault_mismatch_accounts(setup, CHUNK_0_OFF as usize, true); @@ -1084,6 +1091,7 @@ impl TestCase for Case { ) } // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT Self::InvalidQuoteVaultPubkeyDupChunk1 => { let (metas, accounts) = quote_vault_mismatch_accounts(setup, CHUNK_1_OFF as usize, true); @@ -1096,6 +1104,7 @@ impl TestCase for Case { ) } // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT Self::InvalidQuoteVaultPubkeyDupChunk2 => { let (metas, accounts) = quote_vault_mismatch_accounts(setup, CHUNK_2_OFF as usize, true); @@ -1108,6 +1117,7 @@ impl TestCase for Case { ) } // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT Self::InvalidQuoteVaultPubkeyDupChunk3 => { let (metas, accounts) = quote_vault_mismatch_accounts(setup, CHUNK_3_OFF as usize, true); @@ -1120,6 +1130,7 @@ impl TestCase for Case { ) } // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT Self::InvalidQuoteVaultPubkeyNonDupChunk0 => { let (metas, accounts) = quote_vault_mismatch_accounts(setup, CHUNK_0_OFF as usize, false); @@ -1132,6 +1143,7 @@ impl TestCase for Case { ) } // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT Self::InvalidQuoteVaultPubkeyNonDupChunk1 => { let (metas, accounts) = quote_vault_mismatch_accounts(setup, CHUNK_1_OFF as usize, false); @@ -1144,6 +1156,7 @@ impl TestCase for Case { ) } // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT Self::InvalidQuoteVaultPubkeyNonDupChunk2 => { let (metas, accounts) = quote_vault_mismatch_accounts(setup, CHUNK_2_OFF as usize, false); @@ -1156,6 +1169,7 @@ impl TestCase for Case { ) } // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT Self::InvalidQuoteVaultPubkeyNonDupChunk3 => { let (metas, accounts) = quote_vault_mismatch_accounts(setup, CHUNK_3_OFF as usize, false); @@ -1167,7 +1181,8 @@ impl TestCase for Case { Some(ErrorCode::InvalidQuoteVaultPubkey), ) } - // Verifies: REGISTER-MARKET (happy path, quote token program is duplicate) + // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT Self::CreateAccountHappyPathQuoteDup => { let token_program_id = Pubkey::from(TOKEN_PROGRAM_ID); let (metas, accounts) = @@ -1217,7 +1232,8 @@ impl TestCase for Case { }, } } - // Verifies: REGISTER-MARKET (happy path, quote token program is non-duplicate) + // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT Self::CreateAccountHappyPathQuoteNonDup => { let token_program_id = Pubkey::from(TOKEN_PROGRAM_ID); let token_2022_id = Pubkey::from(TOKEN_2022_PROGRAM_ID); @@ -1258,6 +1274,107 @@ impl TestCase for Case { } } + CaseResult { + cu: result.compute_units_consumed, + error: if errors.is_empty() { + None + } else { + Some(errors.join("; ")) + }, + } + } + // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT + Self::CreateAccountHappyPathToken2022QuoteDup => { + let token_2022_id = Pubkey::from(TOKEN_2022_PROGRAM_ID); + let (metas, accounts) = happy_path_accounts(setup, token_2022_id, token_2022_id); + let instruction = Instruction::new_with_bytes(setup.program_id, insn, metas); + let result = setup.mollusk.process_instruction(&instruction, &accounts); + + let mut errors = Vec::new(); + match &result.program_result { + MolluskResult::Success => { + let market = + &result.resulting_accounts[RegisterMarketAccounts::Market as usize].1; + + if market.owner != setup.program_id { + errors.push(format!( + "owner: expected {:?}, got {:?}", + setup.program_id, market.owner + )); + } + if market.data.len() != MARKET_HEADER_SIZE { + errors.push(format!( + "data len: expected {}, got {}", + MARKET_HEADER_SIZE, + market.data.len() + )); + } + let rent = &setup.mollusk.sysvars.rent; + if !rent.is_exempt(market.lamports, market.data.len()) { + errors.push(format!( + "market not rent exempt: {} lamports for {} bytes", + market.lamports, + market.data.len() + )); + } + } + other => { + errors.push(format!("expected success, got {:?}", other)); + } + } + + CaseResult { + cu: result.compute_units_consumed, + error: if errors.is_empty() { + None + } else { + Some(errors.join("; ")) + }, + } + } + // Verifies: REGISTER-MARKET + // Verifies: INIT-VAULT + Self::CreateAccountHappyPathToken2022QuoteNonDup => { + let token_program_id = Pubkey::from(TOKEN_PROGRAM_ID); + let token_2022_id = Pubkey::from(TOKEN_2022_PROGRAM_ID); + let (metas, accounts) = happy_path_accounts(setup, token_2022_id, token_program_id); + let instruction = Instruction::new_with_bytes(setup.program_id, insn, metas); + let result = setup.mollusk.process_instruction(&instruction, &accounts); + + let mut errors = Vec::new(); + match &result.program_result { + MolluskResult::Success => { + let market = + &result.resulting_accounts[RegisterMarketAccounts::Market as usize].1; + + if market.owner != setup.program_id { + errors.push(format!( + "owner: expected {:?}, got {:?}", + setup.program_id, market.owner + )); + } + if market.data.len() != MARKET_HEADER_SIZE { + errors.push(format!( + "data len: expected {}, got {}", + MARKET_HEADER_SIZE, + market.data.len() + )); + } + let rent = &setup.mollusk.sysvars.rent; + if !rent.is_exempt(market.lamports, market.data.len()) { + errors.push(format!( + "market not rent exempt: {} lamports for {} bytes", + market.lamports, + market.data.len() + )); + } + } + other => { + errors.push(format!("expected success, got {:?}", other)); + } + } + CaseResult { cu: result.compute_units_consumed, error: if errors.is_empty() {