11#![ cfg( feature = "test-sbf" ) ]
22
3+ mod common;
4+
35use {
4- common:: {
5- add_lookup_table_account, assert_ix_error, new_address_lookup_table,
6- overwrite_slot_hashes_with_slots, setup_test_context,
7- } ,
6+ common:: { lookup_table_account, new_address_lookup_table, setup} ,
7+ mollusk_svm:: result:: Check ,
88 solana_address_lookup_table_program:: instruction:: close_lookup_table,
9- solana_program_test:: * ,
109 solana_sdk:: {
11- clock:: Clock ,
12- instruction:: InstructionError ,
13- pubkey:: Pubkey ,
14- signature:: { Keypair , Signer } ,
15- transaction:: Transaction ,
10+ account:: AccountSharedData , program_error:: ProgramError , pubkey:: Pubkey ,
11+ slot_hashes:: MAX_ENTRIES ,
1612 } ,
1713} ;
1814
19- mod common;
20-
21- #[ tokio:: test]
22- async fn test_close_lookup_table ( ) {
15+ #[ test]
16+ fn test_close_lookup_table ( ) {
2317 // Succesfully close a deactived lookup table.
24- let mut context = setup_test_context ( ) . await ;
25-
26- context. warp_to_slot ( 2 ) . unwrap ( ) ;
27- overwrite_slot_hashes_with_slots ( & context, & [ ] ) ;
18+ let mut mollusk = setup ( ) ;
19+ mollusk. warp_to_slot ( MAX_ENTRIES as u64 + 1 ) ;
2820
29- let lookup_table_address = Pubkey :: new_unique ( ) ;
30- let authority_keypair = Keypair :: new ( ) ;
21+ let recipient = Pubkey :: new_unique ( ) ;
22+ let authority = Pubkey :: new_unique ( ) ;
3123 let initialized_table = {
32- let mut table = new_address_lookup_table ( Some ( authority_keypair . pubkey ( ) ) , 0 ) ;
33- table. meta . deactivation_slot = 1 ;
24+ let mut table = new_address_lookup_table ( Some ( authority ) , 0 ) ;
25+ table. meta . deactivation_slot = 0 ;
3426 table
3527 } ;
36- add_lookup_table_account ( & mut context, lookup_table_address, initialized_table) . await ;
37-
38- let client = & mut context. banks_client ;
39- let payer = & context. payer ;
40- let recent_blockhash = context. last_blockhash ;
41- let transaction = Transaction :: new_signed_with_payer (
42- & [ close_lookup_table (
43- lookup_table_address,
44- authority_keypair. pubkey ( ) ,
45- context. payer . pubkey ( ) ,
46- ) ] ,
47- Some ( & payer. pubkey ( ) ) ,
48- & [ payer, & authority_keypair] ,
49- recent_blockhash,
50- ) ;
5128
52- assert ! ( matches!(
53- client. process_transaction( transaction) . await ,
54- Ok ( ( ) )
55- ) ) ;
56- assert ! ( client
57- . get_account( lookup_table_address)
58- . await
59- . unwrap( )
60- . is_none( ) ) ;
29+ let lookup_table_address = Pubkey :: new_unique ( ) ;
30+ let lookup_table_account = lookup_table_account ( initialized_table) ;
31+
32+ mollusk. process_and_validate_instruction (
33+ & close_lookup_table ( lookup_table_address, authority, recipient) ,
34+ & [
35+ ( lookup_table_address, lookup_table_account) ,
36+ ( authority, AccountSharedData :: default ( ) ) ,
37+ ( recipient, AccountSharedData :: default ( ) ) ,
38+ ] ,
39+ & [
40+ Check :: success ( ) ,
41+ // Because lookup tables are not reassigned to the System program,
42+ // we can't just check for the canonical "closed" here.
43+ Check :: account ( & lookup_table_address)
44+ . data ( & [ ] )
45+ . lamports ( 0 )
46+ . owner ( & solana_address_lookup_table_program:: id ( ) )
47+ . build ( ) ,
48+ ] ,
49+ ) ;
6150}
6251
63- #[ tokio :: test]
64- async fn test_close_lookup_table_not_deactivated ( ) {
52+ #[ test]
53+ fn test_close_lookup_table_not_deactivated ( ) {
6554 // Try to close a lookup table that hasn't first been deactivated.
6655 // No matter the slot, this will fail, since the lookup table must first
6756 // be deactived before it can be closed.
68- let mut context = setup_test_context ( ) . await ;
57+ let mollusk = setup ( ) ;
6958
70- let authority_keypair = Keypair :: new ( ) ;
71- let initialized_table = new_address_lookup_table ( Some ( authority_keypair. pubkey ( ) ) , 0 ) ;
72- let lookup_table_address = Pubkey :: new_unique ( ) ;
73- add_lookup_table_account ( & mut context, lookup_table_address, initialized_table) . await ;
59+ let recipient = Pubkey :: new_unique ( ) ;
60+ let authority = Pubkey :: new_unique ( ) ;
61+ let initialized_table = new_address_lookup_table ( Some ( authority) , 0 ) ;
7462
75- let ix = close_lookup_table (
76- lookup_table_address,
77- authority_keypair. pubkey ( ) ,
78- context. payer . pubkey ( ) ,
63+ let lookup_table_address = Pubkey :: new_unique ( ) ;
64+ let lookup_table_account = lookup_table_account ( initialized_table) ;
65+
66+ mollusk. process_and_validate_instruction (
67+ & close_lookup_table ( lookup_table_address, authority, recipient) ,
68+ & [
69+ ( lookup_table_address, lookup_table_account) ,
70+ ( authority, AccountSharedData :: default ( ) ) ,
71+ ( recipient, AccountSharedData :: default ( ) ) ,
72+ ] ,
73+ & [
74+ // The ix should fail because the table hasn't been deactivated yet
75+ Check :: err ( ProgramError :: InvalidArgument ) ,
76+ ] ,
7977 ) ;
80-
81- // The ix should fail because the table hasn't been deactivated yet
82- assert_ix_error (
83- & mut context,
84- ix. clone ( ) ,
85- Some ( & authority_keypair) ,
86- InstructionError :: InvalidArgument ,
87- )
88- . await ;
8978}
9079
91- #[ tokio :: test]
92- async fn test_close_lookup_table_deactivated ( ) {
80+ #[ test]
81+ fn test_close_lookup_table_deactivated ( ) {
9382 // Try to close a lookup table that was deactivated, but the cooldown
9483 // period hasn't expired yet.
9584 // This should fail because the table must be deactivated in a previous
9685 // slot and the cooldown period must expire before it can be closed.
97- let mut context = setup_test_context ( ) . await ;
86+ let mut mollusk = setup ( ) ;
9887
99- let authority_keypair = Keypair :: new ( ) ;
88+ let recipient = Pubkey :: new_unique ( ) ;
89+ let authority = Pubkey :: new_unique ( ) ;
10090
91+ // [Core BPF]: The original builtin implementation was relying on the fact
92+ // that the `SlotHashes` sysvar is initialized to have an entry for slot 0.
93+ // Program-Test does this to provide a more realistic test environment.
94+ // That means this test was running with the `Clock` current slot at 1.
95+ // In this implementation, we adapt the deactivation slot as well as the
96+ // current slot into tweakable test case values.
10197 for ( deactivation_slot, current_slot) in [
10298 ( 1 , 1 ) , // Deactivated in the same slot
10399 ( 1 , 2 ) , // Deactivated one slot earlier
@@ -112,110 +108,124 @@ async fn test_close_lookup_table_deactivated() {
112108 ( 10_000 , 10_000 + 115 ) , // Arbitrary number within cooldown.
113109 ( 10_000 , 10_000 + 511 ) , // At the very edge of cooldown.
114110 ] {
115- // Unfortunately, Program-Test's `warp_to_slot` causes an accounts hash
116- // mismatch if you try to warp after setting an account, so we have to just
117- // manipulate the `Clock` directly here.
118- let mut clock = context. banks_client . get_sysvar :: < Clock > ( ) . await . unwrap ( ) ;
119- clock. slot = current_slot;
120- context. set_sysvar :: < Clock > ( & clock) ;
121- overwrite_slot_hashes_with_slots ( & context, & [ deactivation_slot] ) ;
111+ mollusk. warp_to_slot ( current_slot) ;
122112
123113 let initialized_table = {
124- let mut table = new_address_lookup_table ( Some ( authority_keypair . pubkey ( ) ) , 0 ) ;
114+ let mut table = new_address_lookup_table ( Some ( authority ) , 0 ) ;
125115 table. meta . deactivation_slot = deactivation_slot;
126116 table
127117 } ;
128- let lookup_table_address = Pubkey :: new_unique ( ) ;
129- add_lookup_table_account ( & mut context, lookup_table_address, initialized_table) . await ;
130-
131- let ix = close_lookup_table (
132- lookup_table_address,
133- authority_keypair. pubkey ( ) ,
134- context. payer . pubkey ( ) ,
135- ) ;
136118
119+ let lookup_table_address = Pubkey :: new_unique ( ) ;
120+ let lookup_table_account = lookup_table_account ( initialized_table) ;
121+
122+ // [Core BPF]: This still holds true while using `Clock`.
123+ // Context sets up the slot hashes sysvar to _not_ have an entry for
124+ // the current slot, which is when the table was deactivated.
125+ //
126+ // When the curent slot from `Clock` is the same as the deactivation
127+ // slot, `LookupTableMeta::status()` should evaluate to this branch:
128+ //
129+ // ```rust
130+ // else if self.deactivation_slot == current_slot {
131+ // LookupTableStatus::Deactivating {
132+ // remaining_blocks: MAX_ENTRIES.saturating_add(1),
133+ // }
134+ // ````
135+ //
136+ // When the deactivation slot is a prior slot, but the cooldown period
137+ // hasn't expired yet,`LookupTableMeta::status()` should evaluate to
138+ // this branch:
139+ //
140+ // ```rust
141+ // else if let Some(slot_position) =
142+ // calculate_slot_position(&self.deactivation_slot, ¤t_slot)
143+ // {
144+ // LookupTableStatus::Deactivating {
145+ // remaining_blocks: MAX_ENTRIES.saturating_sub(slot_position),
146+ // }
147+ // ````
148+ //
137149 // Because the response is not `LookupTableStatus::Deactivated`, the ix
138150 // should fail.
139- assert_ix_error (
140- & mut context,
141- ix,
142- Some ( & authority_keypair) ,
143- InstructionError :: InvalidArgument ,
144- )
145- . await ;
151+ mollusk. process_and_validate_instruction (
152+ & close_lookup_table ( lookup_table_address, authority, recipient) ,
153+ & [
154+ ( lookup_table_address, lookup_table_account) ,
155+ ( authority, AccountSharedData :: default ( ) ) ,
156+ ( recipient, AccountSharedData :: default ( ) ) ,
157+ ] ,
158+ & [ Check :: err ( ProgramError :: InvalidArgument ) ] ,
159+ ) ;
146160 }
147161}
148162
149- #[ tokio :: test]
150- async fn test_close_immutable_lookup_table ( ) {
151- let mut context = setup_test_context ( ) . await ;
163+ #[ test]
164+ fn test_close_immutable_lookup_table ( ) {
165+ let mollusk = setup ( ) ;
152166
153- let initialized_table = new_address_lookup_table ( None , 10 ) ;
154- let lookup_table_address = Pubkey :: new_unique ( ) ;
155- add_lookup_table_account ( & mut context , lookup_table_address , initialized_table ) . await ;
167+ let recipient = Pubkey :: new_unique ( ) ;
168+ let authority = Pubkey :: new_unique ( ) ;
169+ let initialized_table = new_address_lookup_table ( None , 0 ) ;
156170
157- let authority = Keypair :: new ( ) ;
158- let ix = close_lookup_table (
159- lookup_table_address,
160- authority. pubkey ( ) ,
161- Pubkey :: new_unique ( ) ,
171+ let lookup_table_address = Pubkey :: new_unique ( ) ;
172+ let lookup_table_account = lookup_table_account ( initialized_table) ;
173+
174+ mollusk. process_and_validate_instruction (
175+ & close_lookup_table ( lookup_table_address, authority, recipient) ,
176+ & [
177+ ( lookup_table_address, lookup_table_account) ,
178+ ( authority, AccountSharedData :: default ( ) ) ,
179+ ( recipient, AccountSharedData :: default ( ) ) ,
180+ ] ,
181+ & [ Check :: err ( ProgramError :: Immutable ) ] ,
162182 ) ;
163-
164- assert_ix_error (
165- & mut context,
166- ix,
167- Some ( & authority) ,
168- InstructionError :: Immutable ,
169- )
170- . await ;
171183}
172184
173- #[ tokio :: test]
174- async fn test_close_lookup_table_with_wrong_authority ( ) {
175- let mut context = setup_test_context ( ) . await ;
185+ #[ test]
186+ fn test_close_lookup_table_with_wrong_authority ( ) {
187+ let mollusk = setup ( ) ;
176188
177- let authority = Keypair :: new ( ) ;
178- let wrong_authority = Keypair :: new ( ) ;
179- let initialized_table = new_address_lookup_table ( Some ( authority. pubkey ( ) ) , 10 ) ;
180- let lookup_table_address = Pubkey :: new_unique ( ) ;
181- add_lookup_table_account ( & mut context, lookup_table_address, initialized_table) . await ;
189+ let recipient = Pubkey :: new_unique ( ) ;
190+ let authority = Pubkey :: new_unique ( ) ;
191+ let wrong_authority = Pubkey :: new_unique ( ) ;
192+ let initialized_table = new_address_lookup_table ( Some ( authority) , 10 ) ;
182193
183- let ix = close_lookup_table (
184- lookup_table_address,
185- wrong_authority. pubkey ( ) ,
186- Pubkey :: new_unique ( ) ,
194+ let lookup_table_address = Pubkey :: new_unique ( ) ;
195+ let lookup_table_account = lookup_table_account ( initialized_table. clone ( ) ) ;
196+
197+ mollusk. process_and_validate_instruction (
198+ & close_lookup_table ( lookup_table_address, wrong_authority, recipient) ,
199+ & [
200+ ( lookup_table_address, lookup_table_account) ,
201+ ( wrong_authority, AccountSharedData :: default ( ) ) ,
202+ ( recipient, AccountSharedData :: default ( ) ) ,
203+ ] ,
204+ & [ Check :: err ( ProgramError :: IncorrectAuthority ) ] ,
187205 ) ;
188-
189- assert_ix_error (
190- & mut context,
191- ix,
192- Some ( & wrong_authority) ,
193- InstructionError :: IncorrectAuthority ,
194- )
195- . await ;
196206}
197207
198- #[ tokio :: test]
199- async fn test_close_lookup_table_without_signing ( ) {
200- let mut context = setup_test_context ( ) . await ;
208+ #[ test]
209+ fn test_close_lookup_table_without_signing ( ) {
210+ let mollusk = setup ( ) ;
201211
202- let authority = Keypair :: new ( ) ;
203- let initialized_table = new_address_lookup_table ( Some ( authority. pubkey ( ) ) , 10 ) ;
204- let lookup_table_address = Pubkey :: new_unique ( ) ;
205- add_lookup_table_account ( & mut context, lookup_table_address, initialized_table) . await ;
212+ let recipient = Pubkey :: new_unique ( ) ;
213+ let authority = Pubkey :: new_unique ( ) ;
214+ let initialized_table = new_address_lookup_table ( Some ( authority) , 10 ) ;
206215
207- let mut ix = close_lookup_table (
208- lookup_table_address,
209- authority. pubkey ( ) ,
210- Pubkey :: new_unique ( ) ,
216+ let lookup_table_address = Pubkey :: new_unique ( ) ;
217+ let lookup_table_account = lookup_table_account ( initialized_table. clone ( ) ) ;
218+
219+ let mut instruction = close_lookup_table ( lookup_table_address, authority, recipient) ;
220+ instruction. accounts [ 1 ] . is_signer = false ;
221+
222+ mollusk. process_and_validate_instruction (
223+ & instruction,
224+ & [
225+ ( lookup_table_address, lookup_table_account) ,
226+ ( authority, AccountSharedData :: default ( ) ) ,
227+ ( recipient, AccountSharedData :: default ( ) ) ,
228+ ] ,
229+ & [ Check :: err ( ProgramError :: MissingRequiredSignature ) ] ,
211230 ) ;
212- ix. accounts [ 1 ] . is_signer = false ;
213-
214- assert_ix_error (
215- & mut context,
216- ix,
217- None ,
218- InstructionError :: MissingRequiredSignature ,
219- )
220- . await ;
221231}
0 commit comments