shadow_drive_user_staking/instructions/
initialize_account.rs

1use crate::{constants::*, errors::ErrorCodes, instructions::initialize_config::StorageConfig};
2use anchor_lang::prelude::*;
3use anchor_spl::token::{Mint, Token, TokenAccount};
4use std::convert::TryInto;
5
6// pub const STORAGE_ACCOUNT_SIZE: usize = 
7//    18 // Alignments (NOTE: THIS IS WRONG AND I ABANDONED IT.)
8//  + 1 // bools (idk why we need ≈1 byte per)
9//  + 2*4 // init and del counters u32
10//  + 4 // delete request epoch u32
11//  + 8*2 // storage, storage_available u64
12//  + 32*3 // owner 1-4 + shdw payer pubkeys
13//  + 4 // seed u32
14//  + 8*2 // cost, fees u64
15//  + 3*4 // creation time, epoch u32, last_fee epoch
16//  + 4 + MAX_IDENTIFIER_SIZE; // identifier size, in bytes,
17
18pub(crate) fn calc_v1_storage(identifier: &str) -> usize {
19    std::mem::size_of::<StorageAccount>()
20        .checked_add(identifier.as_bytes().len())
21        .unwrap()
22}
23
24/// This is the function that handles the `initialize_account` ix
25pub fn handler(
26    mut ctx: impl InitializeStorageAccount,
27    identifier: String,
28    storage: u64,
29    owner_2: Option<Pubkey>,
30) -> Result<()> {
31
32
33    // Initialize user_info if needed
34    if ctx.get_account_counter() == 0 {
35        msg!("Initializing UserInfo");
36        ctx.initialize_user_info()?;
37    }
38
39    msg!("Initializing StorageAccount: {}", identifier);
40    {
41        require!(
42            !ctx.check_csam(),
43            ErrorCodes::HasHadBadCsam
44        );
45
46        // Store unique identifier.
47        ctx.set_identifier(validate_identifier(identifier)?)?;
48
49        // Initialize account-wide mutability flag
50        let is_immutable = false;
51        ctx.set_immutable(is_immutable)?;
52
53        // Initialize deletion variables
54        let to_be_deleted = false;
55        let delete_request_epoch = 0;
56        ctx.set_deletion_flag(to_be_deleted, delete_request_epoch)?;
57
58        // Set local storage. Validated in the method
59        ctx.change_storage(storage, Mode::Initialize)?;
60
61        // Initialize file counters, account counter
62        ctx.set_account_counter_seed()?;
63
64        // Populate owners
65        ctx.set_owner()?;
66        ctx.set_owner2(owner_2)?;
67
68        // Store time of creation
69        ctx.record_genesis()?;
70    }
71
72    msg!("Staking user funds");
73    {
74
75        // Compute required stake to store data
76        let shades_per_gib = ctx.get_shades_per_gib().unwrap();
77        let stake_required_to_store: u64 = stake_required(storage, shades_per_gib);
78
79        msg!(
80            "User requires {} shades to store {} bytes",
81            stake_required_to_store,
82            storage
83        );
84
85        // Ensure user has enough funds
86        let user_token_balance = ctx.get_user_token_balance();
87        if user_token_balance.unwrap() < stake_required_to_store {
88            return err!(ErrorCodes::InsufficientFunds);
89        }
90
91        // Transfer funds to stake_account
92        ctx.stake_shades(stake_required_to_store)?;
93    }
94
95    msg!("Updating global storage on StorageConfig account");
96    {
97
98        // Decrease storage available
99        ctx.change_global_storage(storage, Mode::Decrement)?;
100    }
101
102    msg!("Incrementing counter in UserInfo");
103    ctx.increment_account_counter()?;
104
105    Ok(())
106}
107
108#[derive(Accounts)]
109#[instruction(identifier: String)]
110/// This `InitializeStorageAccount` context is used to initialize a `StorageAccount` which stores a user's
111/// storage information including stake token account address, storage requested, and access keys.
112pub struct InitializeStorageAccountV1<'info> {
113    /// This account is a PDA that holds the storage configuration, including current cost per byte,
114    #[account(
115        mut,
116        seeds = [
117            "storage-config".as_bytes()
118        ],
119        bump,
120    )]
121    pub storage_config: Box<Account<'info, StorageConfig>>,
122
123    /// This account is a PDA that holds a user's info (not specific to one storage account).
124    #[account(
125        init_if_needed,
126        payer = owner_1,
127        space = {
128            8 // discriminator
129            + 4 // init counter
130            + 4 // del counter
131            + 2 // bools
132        },
133        seeds = [
134            "user-info".as_bytes(),
135            &owner_1.key().to_bytes(),
136        ],
137        bump,
138    )]
139    pub user_info: Box<Account<'info, UserInfo>>,
140
141    /// This account is a PDA that holds a user's `StorageAccount` information.
142    #[account(
143        init,
144        payer = owner_1,
145        space = calc_v1_storage(&identifier),
146        seeds = [
147            "storage-account".as_bytes(),
148            &owner_1.key().to_bytes(),
149            &user_info.account_counter.to_le_bytes(),
150        ],
151        bump,
152    )]
153    pub storage_account: Box<Account<'info, StorageAccount>>,
154
155    /// This token account serves as the account which holds user's stake for file storage.
156    #[account(
157        init,
158        payer = owner_1,
159        seeds = [
160            "stake-account".as_bytes(),
161            &storage_account.key().to_bytes(),
162        ],
163        bump,
164        token::mint = token_mint,
165        token::authority = storage_config,
166    )]
167    pub stake_account: Box<Account<'info, TokenAccount>>,
168
169    /// This is the token in question for staking.
170    #[account(address=crate::constants::shdw::ID)]
171    pub token_mint: Account<'info, Mint>,
172
173    /// This is the user who is initializing the storage account
174    /// and is automatically added as an admin
175    #[account(mut)]
176    pub owner_1: Signer<'info>,
177
178    /// Uploader needs to sign as this txn
179    /// needs to be fulfilled on the middleman server
180    /// to create the ceph bucket
181    #[account(constraint = uploader.key() == storage_config.uploader)]
182    pub uploader: Signer<'info>,
183
184    /// This is the user's token account with which they are staking
185    #[account(mut, constraint = owner_1_token_account.mint == shdw::ID)]
186    pub owner_1_token_account: Box<Account<'info, TokenAccount>>,
187
188    /// System Program
189    pub system_program: Program<'info, System>,
190
191    /// Token Program
192    pub token_program: Program<'info, Token>,
193
194    /// Rent Program
195    pub rent: Sysvar<'info, Rent>,
196}
197
198
199#[derive(Accounts)]
200#[instruction(identifier: String)]
201/// This `InitializeStorageAccount` context is used to initialize a `StorageAccount` which stores a user's
202/// storage information including stake token account address, storage requested, and access keys.
203pub struct InitializeStorageAccountV2<'info> {
204    /// This account is a PDA that holds the storage configuration, including current cost per byte,
205    #[account(
206        mut,
207        seeds = [
208            "storage-config".as_bytes()
209        ],
210        bump,
211    )]
212    pub storage_config: Box<Account<'info, StorageConfig>>,
213
214    /// This account is a PDA that holds a user's info (not specific to one storage account).
215    #[account(
216        init_if_needed,
217        payer = owner_1,
218        space = {
219            8 // discriminator
220            + 4 // init counter
221            + 4 // del counter
222            + 2 // bools
223        },
224        seeds = [
225            "user-info".as_bytes(),
226            &owner_1.key().to_bytes(),
227        ],
228        bump,
229    )]
230    pub user_info: Box<Account<'info, UserInfo>>,
231
232    /// This account is a PDA that holds a user's storage account information.
233    /// Upgraded to `StorageAccountV2`.
234    #[account(
235        init,
236        payer = owner_1,
237        space = calc_v2_storage(&identifier),
238        seeds = [
239            "storage-account".as_bytes(),
240            &owner_1.key().to_bytes(),
241            &user_info.account_counter.to_le_bytes(),
242        ],
243        bump,
244    )]
245    pub storage_account: Box<Account<'info, StorageAccountV2>>,
246
247    /// This token account serves as the account which holds user's stake for file storage.
248    #[account(
249        init,
250        payer = owner_1,
251        seeds = [
252            "stake-account".as_bytes(),
253            &storage_account.key().to_bytes(),
254        ],
255        bump,
256        token::mint = token_mint,
257        token::authority = storage_config,
258    )]
259    pub stake_account: Box<Account<'info, TokenAccount>>,
260
261    /// This is the token in question for staking.
262    #[account(address=crate::constants::shdw::ID)]
263    pub token_mint: Account<'info, Mint>,
264
265    /// This is the user who is initializing the storage account
266    /// and is automatically added as an admin
267    #[account(mut)]
268    pub owner_1: Signer<'info>,
269
270    /// Uploader needs to sign as this txn
271    /// needs to be fulfilled on the middleman server
272    /// to create the ceph bucket
273    #[account(constraint = uploader.key() == storage_config.uploader)]
274    pub uploader: Signer<'info>,
275
276    /// This is the user's token account with which they are staking
277    #[account(mut, constraint = owner_1_token_account.mint == shdw::ID)]
278    pub owner_1_token_account: Box<Account<'info, TokenAccount>>,
279
280    /// System Program
281    pub system_program: Program<'info, System>,
282
283    /// Token Program
284    pub token_program: Program<'info, Token>,
285
286    /// Rent Program
287    pub rent: Sysvar<'info, Rent>,
288}
289
290#[account]
291pub struct StorageAccount {
292    /// Immutable boolean to track what kind of storage account this is.
293    /// NOTE: Not used in current implementation w/ non-dynamic storage payments
294    pub is_static: bool,
295
296    /// Flag on whether storage account is public (usable by anyone)
297    //pub is_public: bool,
298
299    /// Counter tracking how many files have been initialized
300    pub init_counter: u32,
301
302    /// Counter tracking how many files have been deleted
303    pub del_counter: u32,
304
305    /// Boolean to track whether storage account (and all child File accounts) are immutable
306    pub immutable: bool,
307
308    /// Delete flag
309    pub to_be_deleted: bool,
310
311    /// Delete request epoch
312    pub delete_request_epoch: u32,
313
314    /// Number of bytes of storage associated with this account
315    pub storage: u64,
316
317    /// Bytes available for use
318    pub storage_available: u64,
319
320    /// Primary owner of StorageAccount (immutable)
321    pub owner_1: Pubkey,
322
323    /// Optional owner 2
324    pub owner_2: Pubkey,
325
326    // /// Optional owner 3
327    // pub owner_3: Pubkey,
328
329    // /// Optional owner 4
330    // pub owner_4: Pubkey,
331    /// Pubkey of the token account that staked SHDW
332    pub shdw_payer: Pubkey,
333
334    /// Counter at time of initialization
335    pub account_counter_seed: u32,
336
337    /// Total shades paid for current box size
338    pub total_cost_of_current_storage: u64,
339
340    // Total shades paid for current box size
341    pub total_fees_paid: u64,
342
343    /// Time of storage account creation
344    pub creation_time: u32,
345
346    /// Time of storage account creation
347    pub creation_epoch: u32,
348
349    /// The last epoch through which the user paid
350    pub last_fee_epoch: u32,
351
352    /// Some unique identifier that the user provides.
353    /// Serves as a seed for storage account PDA.
354    pub identifier: String,
355}
356
357
358// pub const STORAGE_ACCOUNT_V2_SIZE: usize = 8 // discriminator
359// + 1*2 // bools
360// + 4 // delete request epoch u32
361// + 8 // storage u64
362// + 32*1 // owner 1 pubkey
363// + 4 // seed u32
364// + 8*1 // fees u64
365// + 4*3 // creation time, epoch u32, last_fee epoch
366// + 4 + MAX_IDENTIFIER_SIZE; // identifier size, in bytes,
367
368pub(crate) fn calc_v2_storage(identifier: &str) -> usize {
369    // STORAGE_ACCOUNT_V2_SIZE
370    //     .checked_sub(MAX_IDENTIFIER_SIZE)
371    //     .unwrap()
372    //     .checked_add(identifier.as_bytes().len())
373    //     .unwrap()
374    std::mem::size_of::<StorageAccountV2>()
375        .checked_add(identifier.as_bytes().len())
376        .unwrap()
377}
378
379
380// #[test]
381// fn print_size_of_v2() {
382//     println!("V1: {STORAGE_ACCOUNT_SIZE}");
383//     println!("V2: {STORAGE_ACCOUNT_V2_SIZE}");
384// }
385
386#[account]
387pub struct StorageAccountV2 {
388    // /// Immutable boolean to track what kind of storage account this is.
389    // pub is_static: bool,
390
391    // /// Flag on whether storage account is public (usable by anyone)
392    // pub is_public: bool,
393
394    // /// Counter tracking how many files have been initialized
395    // pub init_counter: u32,
396
397    // /// Counter tracking how many files have been deleted
398    // pub del_counter: u32,
399
400    /// Boolean to track whether storage account (and all child File accounts) are immutable
401    pub immutable: bool,
402
403    /// Delete flag
404    pub to_be_deleted: bool,
405
406    /// Delete request epoch
407    pub delete_request_epoch: u32,
408
409    /// Number of bytes of storage associated with this account
410    pub storage: u64,
411
412    // /// Bytes available for use
413    // pub storage_available: u64,
414
415    /// Primary owner of StorageAccount (immutable)
416    pub owner_1: Pubkey,
417
418    // /// Optional owner 2
419    // pub owner_2: Pubkey,
420
421    // /// Optional owner 3
422    // pub owner_3: Pubkey,
423
424    // /// Optional owner 4
425    // pub owner_4: Pubkey,
426    /// Pubkey of the token account that staked SHDW
427    // pub shdw_payer: Pubkey,
428
429    /// Counter at time of initialization
430    pub account_counter_seed: u32,
431
432    // /// Total shades paid for current box size
433    // pub total_cost_of_current_storage: u64,
434
435    // Total shades paid for current box size
436    // pub total_fees_paid: u64,
437
438    /// Time of storage account creation
439    pub creation_time: u32,
440
441    /// Time of storage account creation
442    pub creation_epoch: u32,
443
444    /// The last epoch through which the user paid
445    pub last_fee_epoch: u32,
446
447    /// Some unique identifier that the user provides.
448    /// Serves as a seed for storage account PDA.
449    pub identifier: String,
450}
451
452#[account]
453pub struct UserInfo {
454    /// Total number of storage accounts the user has with us
455    pub account_counter: u32,
456
457    /// Total number of storage accounts that have been deleted
458    pub del_counter: u32,
459
460    /// Boolean denoting that the user agreed to terms of service
461    pub agreed_to_tos: bool,
462
463    /// Boolean denoting whether this pubkey has ever had a bad scam scan
464    pub lifetime_bad_csam: bool,
465}
466
467fn validate_identifier(identifier: String) -> Result<String> {
468    if identifier.as_bytes().len() <= MAX_IDENTIFIER_SIZE {
469        Ok(identifier)
470    } else {
471        err!(ErrorCodes::ExceededStorageLimit).into()
472    }
473}
474
475fn stake_required(storage: u64, shades_per_gib: u64) -> u64 {
476    // ((u128 * u128) / u128) --> u64 allows us to multiply u64's without overflow.
477    // Should fail a lot less with nonzero inputs, even with std::u64::MAX.
478    let result_u128: Option<u128> = (storage as u128)
479        .checked_mul(shades_per_gib as u128)
480        .unwrap()
481        .checked_div(BYTES_PER_GIB as u128);
482    result_u128.unwrap().try_into().unwrap()
483}
484
485type Storage = u64;
486type Epoch = u32;
487
488pub trait ShadowDriveStorageAccount {
489    fn check_immutable(&self) -> bool;
490    fn check_delete_flag(&self) -> bool;
491    fn get_identifier(&self) -> String;
492    fn get_storage(&self) -> Storage;
493    fn get_last_fee_epoch(&self) -> Epoch;
494    fn mark_to_delete(&mut self);
495    fn update_last_fee_epoch(&mut self);
496    fn is_owner(&self, owner: Pubkey) -> bool;
497}
498
499impl ShadowDriveStorageAccount for StorageAccount {
500    fn check_immutable(&self) -> bool {
501        self.immutable
502    }
503    fn check_delete_flag(&self) -> bool {
504        self.to_be_deleted
505    }
506    fn get_identifier(&self) -> String {
507        self.identifier.clone()
508    }
509    fn get_storage(&self) -> Storage {
510        self.storage
511    }
512    fn get_last_fee_epoch(&self) -> Epoch {
513        self.last_fee_epoch
514    }
515    fn mark_to_delete(&mut self) {
516        self.to_be_deleted = true;
517        self.delete_request_epoch = Clock::get().unwrap().epoch.try_into().unwrap();
518    }
519    fn update_last_fee_epoch(&mut self) {
520        self.last_fee_epoch = Clock::get().unwrap().epoch.try_into().unwrap();
521    }
522    fn is_owner(&self, owner: Pubkey) -> bool {
523        owner == self.owner_1 || owner == self.owner_2
524    }
525}
526
527impl ShadowDriveStorageAccount for StorageAccountV2 {
528    fn check_immutable(&self) -> bool {
529        self.immutable
530    }
531    fn check_delete_flag(&self) -> bool {
532        self.to_be_deleted
533    }
534    fn get_identifier(&self) -> String {
535        self.identifier.clone()
536    }
537    fn get_storage(&self) -> Storage {
538        self.storage
539    }
540    fn get_last_fee_epoch(&self) -> Epoch {
541        self.last_fee_epoch
542    }
543    fn mark_to_delete(&mut self) {
544        self.to_be_deleted = true;
545        self.delete_request_epoch = Clock::get().unwrap().epoch.try_into().unwrap();
546    }
547    fn update_last_fee_epoch(&mut self) {
548        self.last_fee_epoch = Clock::get().unwrap().epoch.try_into().unwrap();
549    }
550    fn is_owner(&self, owner: Pubkey) -> bool {
551        owner == self.owner_1
552    }
553}
554
555
556pub trait InitializeStorageAccount {
557    fn set_immutable(&mut self, boolean: bool) -> Result<()>;
558    fn set_identifier(&mut self, identifier: String) -> Result<()>;
559    fn set_deletion_flag(&mut self, boolean: bool, epoch: u64) -> Result<()>;
560    fn set_account_counter_seed(&mut self) -> Result<()>; // to be run once only
561    fn change_storage(&mut self, bytes: u64, mode: Mode) -> Result<()>;
562    fn set_owner(&mut self) -> Result<()>; // to be run once only
563    fn set_owner2(&mut self, owner_2: Option<Pubkey>) -> Result<()>;
564    fn record_genesis(&mut self) -> Result<()>; // to be run once only
565    fn get_account_counter(&mut self) -> u32;
566    fn initialize_user_info(&mut self) -> Result<()>; // to be run once only
567    fn check_csam(&mut self) -> bool;
568    fn change_global_storage(&mut self, storage: u64, mode: Mode) -> Result<()>;
569    fn get_shades_per_gib(&mut self) -> Option<u64>;
570    fn get_user_token_balance(&mut self) -> Option<u64>;
571    fn stake_shades(&mut self, shades: u64) -> Result<()>;
572    fn increment_account_counter(&mut self) -> Result<()>;
573}
574
575pub enum Mode {
576    Increment,
577    Decrement,
578    Initialize,
579}
580
581impl InitializeStorageAccount for Context<'_, '_, '_, '_, InitializeStorageAccountV1<'_>> {
582    fn set_immutable(&mut self, boolean: bool) -> Result<()> {
583        self.accounts.storage_account.immutable = boolean;
584        Ok(())
585    }
586    fn set_identifier(&mut self, identifier: String ) -> Result<()> {
587        self.accounts.storage_account.identifier = identifier;
588        Ok(())
589    }
590    fn set_deletion_flag(&mut self, boolean: bool, epoch: u64) -> Result<()> {
591        self.accounts.storage_account.to_be_deleted = boolean;
592        self.accounts.storage_account.delete_request_epoch = epoch.try_into().unwrap();
593        Ok(())
594    }
595    fn set_account_counter_seed(&mut self) -> Result<()> {
596        self.accounts.storage_account.account_counter_seed = self.get_account_counter();
597        Ok(())
598    }
599    fn change_storage(&mut self, bytes: u64, mode: Mode) -> Result<()> {
600        let bytes = self.accounts.storage_config.validate_storage(bytes)?;
601        match mode {
602            Mode::Increment => {
603                self.accounts.storage_account.storage = self.accounts.storage_account.storage.checked_add(bytes).unwrap();
604            },
605            Mode::Decrement => {
606                // The requirement should be that the bytes is less than what is available
607                // but we are planning to no longer tracking this on-chain as of Jun 14.
608                // As such, the uploader should sign off on this.
609                self.accounts.storage_account.storage = self.accounts.storage_account.storage.checked_sub(bytes).unwrap();
610            },
611            Mode::Initialize => {
612                self.accounts.storage_account.storage = bytes;
613            }
614        }
615        Ok(())
616    }
617    fn set_owner(&mut self) -> Result<()> {
618        self.accounts.storage_account.owner_1 = self.accounts.owner_1.key();
619        Ok(())
620    }
621    fn set_owner2(&mut self, owner_2: Option<Pubkey>) -> Result<()>{
622        if let Some(owner_2) = owner_2 {
623            self.accounts.storage_account.owner_2 = owner_2;
624        } 
625        Ok(())
626    }
627    fn record_genesis(&mut self) -> Result<()> {
628        let clock = Clock::get().unwrap();
629        self.accounts.storage_account.creation_time = clock.unix_timestamp.try_into().unwrap();
630        self.accounts.storage_account.creation_epoch = clock.epoch.try_into().unwrap();
631        self.accounts.storage_account.last_fee_epoch = clock.epoch.try_into().unwrap();
632        Ok(())
633    }
634    fn get_account_counter(&mut self) -> u32 {
635        self.accounts.user_info.account_counter
636    }
637    fn initialize_user_info(&mut self) -> Result<()> {
638
639        // Initialize counter values
640        self.accounts.user_info.account_counter = 0;
641        self.accounts.user_info.del_counter = 0;
642
643        // Terms of service, csam tracker
644        self.accounts.user_info.agreed_to_tos = true;
645        self.accounts.user_info.lifetime_bad_csam = false;
646
647        Ok(())
648    }
649    fn check_csam(&mut self) -> bool {
650        self.accounts.user_info.lifetime_bad_csam
651    }
652    fn change_global_storage(&mut self, bytes: u64, mode: Mode) -> Result<()> {
653        let bytes = bytes as u128;
654        match mode {
655            Mode::Increment => {
656                self.accounts.storage_config.storage_available = self.accounts.storage_config.storage_available.checked_add(bytes).unwrap();
657            },
658            Mode::Decrement => {
659                // The requirement should be that the bytes is less than what is available
660                // but we are planning to no longer tracking this on-chain as of Jun 14.
661                // As such, the uploader should sign off on this.
662                self.accounts.storage_config.storage_available = self.accounts.storage_config.storage_available.checked_sub(bytes).unwrap();
663            },
664            Mode::Initialize => {
665                self.accounts.storage_config.storage_available = bytes;
666            }
667        }
668        Ok(())
669    }
670    fn get_shades_per_gib(&mut self) -> Option<u64> {
671        Some(self.accounts.storage_config.shades_per_gib)
672    }
673    fn get_user_token_balance(&mut self) -> Option<u64> {
674        Some(self.accounts.owner_1_token_account.amount)
675    }
676    fn stake_shades(&mut self, shades: u64) -> Result<()> {
677        anchor_spl::token::transfer(
678            CpiContext::new(
679                self.accounts.token_program.to_account_info(),
680                anchor_spl::token::Transfer {
681                    from: self.accounts.owner_1_token_account.to_account_info(),
682                    to: self.accounts.stake_account.to_account_info(),
683                    authority: self.accounts.owner_1.to_account_info(),
684                },
685            ),
686            shades,
687        )
688    }
689    fn increment_account_counter(&mut self) -> Result<()> {
690        self.accounts.user_info.account_counter = self
691            .accounts
692            .user_info
693            .account_counter
694            .checked_add(1)
695            .unwrap();
696        Ok(())
697    }
698}
699
700impl InitializeStorageAccount for Context<'_, '_, '_, '_, InitializeStorageAccountV2<'_>> {
701    fn set_immutable(&mut self, boolean: bool) -> Result<()> {
702        self.accounts.storage_account.immutable = boolean;
703        Ok(())
704    }
705    fn set_identifier(&mut self, identifier: String ) -> Result<()>{
706        self.accounts.storage_account.identifier = identifier;
707        Ok(())
708    }
709    fn set_deletion_flag(&mut self, boolean: bool, epoch: u64) -> Result<()> {
710        self.accounts.storage_account.to_be_deleted = boolean;
711        self.accounts.storage_account.delete_request_epoch = epoch.try_into().unwrap();
712        Ok(())
713    }
714    fn set_account_counter_seed(&mut self) -> Result<()> {
715        self.accounts.storage_account.account_counter_seed = self.get_account_counter();
716        Ok(())
717    }
718    fn change_storage(&mut self, bytes: u64, mode: Mode) -> Result<()> {
719        let bytes = self.accounts.storage_config.validate_storage(bytes)?;
720        match mode {
721            Mode::Increment => {
722                self.accounts.storage_account.storage = self.accounts.storage_account.storage.checked_add(bytes).unwrap();
723            },
724            Mode::Decrement => {
725                // The requirement should be that the bytes is less than what is available
726                // but we are planning to no longer tracking this on-chain as of Jun 14.
727                // As such, the uploader should sign off on this.
728                self.accounts.storage_account.storage = self.accounts.storage_account.storage.checked_sub(bytes).unwrap();
729            },
730            Mode::Initialize => {
731                self.accounts.storage_account.storage = bytes;
732            }
733        }
734        Ok(())
735    }
736    fn set_owner(&mut self) -> Result<()> {
737        self.accounts.storage_account.owner_1 = self.accounts.owner_1.key();
738        Ok(())
739    }
740    fn set_owner2(&mut self, owner_2: Option<Pubkey>) -> Result<()>{
741        if owner_2.is_some() {
742            err!(ErrorCodes::OnlyOneOwnerAllowedInV1_5)
743        } else {
744            Ok(())
745        }
746    }
747    fn record_genesis(&mut self) -> Result<()> {
748        let clock = Clock::get().unwrap();
749        self.accounts.storage_account.creation_time = clock.unix_timestamp.try_into().unwrap();
750        self.accounts.storage_account.creation_epoch = clock.epoch.try_into().unwrap();
751        self.accounts.storage_account.last_fee_epoch = clock.epoch.try_into().unwrap();
752        Ok(())
753
754    }
755    fn get_account_counter(&mut self) -> u32 {
756        self.accounts.user_info.account_counter
757    }
758    fn initialize_user_info(&mut self) -> Result<()> {
759
760        // Initialize counter values
761        self.accounts.user_info.account_counter = 0;
762        self.accounts.user_info.del_counter = 0;
763
764        // Terms of service, csam tracker
765        self.accounts.user_info.agreed_to_tos = true;
766        self.accounts.user_info.lifetime_bad_csam = false;
767
768        Ok(())
769    }
770    fn check_csam(&mut self) -> bool {
771        self.accounts.user_info.lifetime_bad_csam
772    }
773    fn change_global_storage(&mut self, bytes: u64, mode: Mode) -> Result<()> {
774        let bytes = bytes as u128;
775        match mode {
776            Mode::Increment => {
777                self.accounts.storage_config.storage_available = self.accounts.storage_config.storage_available.checked_add(bytes).unwrap();
778            },
779            Mode::Decrement => {
780                // The requirement should be that the bytes is less than what is available
781                // but we are planning to no longer tracking this on-chain as of Jun 14.
782                // As such, the uploader should sign off on this.
783                self.accounts.storage_config.storage_available = self.accounts.storage_config.storage_available.checked_sub(bytes).unwrap();
784            },
785            Mode::Initialize => {
786                self.accounts.storage_config.storage_available = bytes;
787            }
788        }
789        Ok(())
790    }
791    fn get_shades_per_gib(&mut self) -> Option<u64> {
792        Some(self.accounts.storage_config.shades_per_gib)
793    }
794    fn get_user_token_balance(&mut self) -> Option<u64> {
795        Some(self.accounts.owner_1_token_account.amount)
796    }
797    fn stake_shades(&mut self, shades: u64) -> Result<()> {
798        anchor_spl::token::transfer(
799            CpiContext::new(
800                self.accounts.token_program.to_account_info(),
801                anchor_spl::token::Transfer {
802                    from: self.accounts.owner_1_token_account.to_account_info(),
803                    to: self.accounts.stake_account.to_account_info(),
804                    authority: self.accounts.owner_1.to_account_info(),
805                },
806            ),
807            shades,
808        )
809    }
810    fn increment_account_counter(&mut self) -> Result<()> {
811        self.accounts.user_info.account_counter = self
812            .accounts
813            .user_info
814            .account_counter
815            .checked_add(1)
816            .unwrap();
817        Ok(())
818    }
819}
820
821
822
823#[test]
824fn test_v1_base_size(){
825
826    use std::mem::size_of;
827    assert_eq!(
828        size_of::<StorageAccount>(),
829        184
830    );
831}
832
833#[test]
834fn test_v2_base_size(){
835
836    use std::mem::size_of;
837    assert_eq!(
838        size_of::<StorageAccountV2>(),
839        88
840    );
841}