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
6pub(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
24pub fn handler(
26 mut ctx: impl InitializeStorageAccount,
27 identifier: String,
28 storage: u64,
29 owner_2: Option<Pubkey>,
30) -> Result<()> {
31
32
33 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 ctx.set_identifier(validate_identifier(identifier)?)?;
48
49 let is_immutable = false;
51 ctx.set_immutable(is_immutable)?;
52
53 let to_be_deleted = false;
55 let delete_request_epoch = 0;
56 ctx.set_deletion_flag(to_be_deleted, delete_request_epoch)?;
57
58 ctx.change_storage(storage, Mode::Initialize)?;
60
61 ctx.set_account_counter_seed()?;
63
64 ctx.set_owner()?;
66 ctx.set_owner2(owner_2)?;
67
68 ctx.record_genesis()?;
70 }
71
72 msg!("Staking user funds");
73 {
74
75 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 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 ctx.stake_shades(stake_required_to_store)?;
93 }
94
95 msg!("Updating global storage on StorageConfig account");
96 {
97
98 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)]
110pub struct InitializeStorageAccountV1<'info> {
113 #[account(
115 mut,
116 seeds = [
117 "storage-config".as_bytes()
118 ],
119 bump,
120 )]
121 pub storage_config: Box<Account<'info, StorageConfig>>,
122
123 #[account(
125 init_if_needed,
126 payer = owner_1,
127 space = {
128 8 + 4 + 4 + 2 },
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 #[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 #[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 #[account(address=crate::constants::shdw::ID)]
171 pub token_mint: Account<'info, Mint>,
172
173 #[account(mut)]
176 pub owner_1: Signer<'info>,
177
178 #[account(constraint = uploader.key() == storage_config.uploader)]
182 pub uploader: Signer<'info>,
183
184 #[account(mut, constraint = owner_1_token_account.mint == shdw::ID)]
186 pub owner_1_token_account: Box<Account<'info, TokenAccount>>,
187
188 pub system_program: Program<'info, System>,
190
191 pub token_program: Program<'info, Token>,
193
194 pub rent: Sysvar<'info, Rent>,
196}
197
198
199#[derive(Accounts)]
200#[instruction(identifier: String)]
201pub struct InitializeStorageAccountV2<'info> {
204 #[account(
206 mut,
207 seeds = [
208 "storage-config".as_bytes()
209 ],
210 bump,
211 )]
212 pub storage_config: Box<Account<'info, StorageConfig>>,
213
214 #[account(
216 init_if_needed,
217 payer = owner_1,
218 space = {
219 8 + 4 + 4 + 2 },
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 #[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 #[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 #[account(address=crate::constants::shdw::ID)]
263 pub token_mint: Account<'info, Mint>,
264
265 #[account(mut)]
268 pub owner_1: Signer<'info>,
269
270 #[account(constraint = uploader.key() == storage_config.uploader)]
274 pub uploader: Signer<'info>,
275
276 #[account(mut, constraint = owner_1_token_account.mint == shdw::ID)]
278 pub owner_1_token_account: Box<Account<'info, TokenAccount>>,
279
280 pub system_program: Program<'info, System>,
282
283 pub token_program: Program<'info, Token>,
285
286 pub rent: Sysvar<'info, Rent>,
288}
289
290#[account]
291pub struct StorageAccount {
292 pub is_static: bool,
295
296 pub init_counter: u32,
301
302 pub del_counter: u32,
304
305 pub immutable: bool,
307
308 pub to_be_deleted: bool,
310
311 pub delete_request_epoch: u32,
313
314 pub storage: u64,
316
317 pub storage_available: u64,
319
320 pub owner_1: Pubkey,
322
323 pub owner_2: Pubkey,
325
326 pub shdw_payer: Pubkey,
333
334 pub account_counter_seed: u32,
336
337 pub total_cost_of_current_storage: u64,
339
340 pub total_fees_paid: u64,
342
343 pub creation_time: u32,
345
346 pub creation_epoch: u32,
348
349 pub last_fee_epoch: u32,
351
352 pub identifier: String,
355}
356
357
358pub(crate) fn calc_v2_storage(identifier: &str) -> usize {
369 std::mem::size_of::<StorageAccountV2>()
375 .checked_add(identifier.as_bytes().len())
376 .unwrap()
377}
378
379
380#[account]
387pub struct StorageAccountV2 {
388 pub immutable: bool,
402
403 pub to_be_deleted: bool,
405
406 pub delete_request_epoch: u32,
408
409 pub storage: u64,
411
412 pub owner_1: Pubkey,
417
418 pub account_counter_seed: u32,
431
432 pub creation_time: u32,
440
441 pub creation_epoch: u32,
443
444 pub last_fee_epoch: u32,
446
447 pub identifier: String,
450}
451
452#[account]
453pub struct UserInfo {
454 pub account_counter: u32,
456
457 pub del_counter: u32,
459
460 pub agreed_to_tos: bool,
462
463 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 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<()>; fn change_storage(&mut self, bytes: u64, mode: Mode) -> Result<()>;
562 fn set_owner(&mut self) -> Result<()>; fn set_owner2(&mut self, owner_2: Option<Pubkey>) -> Result<()>;
564 fn record_genesis(&mut self) -> Result<()>; fn get_account_counter(&mut self) -> u32;
566 fn initialize_user_info(&mut self) -> Result<()>; 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 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 self.accounts.user_info.account_counter = 0;
641 self.accounts.user_info.del_counter = 0;
642
643 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 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 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 self.accounts.user_info.account_counter = 0;
762 self.accounts.user_info.del_counter = 0;
763
764 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 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}