484 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			484 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use assert_matches::assert_matches;
 | |
| use bincode::deserialize;
 | |
| use log::*;
 | |
| use solana_runtime::{
 | |
|     bank::Bank,
 | |
|     bank_client::BankClient,
 | |
|     genesis_utils::{create_genesis_config, GenesisConfigInfo},
 | |
| };
 | |
| use solana_sdk::{
 | |
|     account_utils::StateMut,
 | |
|     client::SyncClient,
 | |
|     clock::{get_segment_from_slot, DEFAULT_SLOTS_PER_SEGMENT, DEFAULT_TICKS_PER_SLOT},
 | |
|     hash::{hash, Hash},
 | |
|     message::Message,
 | |
|     pubkey::Pubkey,
 | |
|     signature::{Keypair, Signature, Signer},
 | |
|     system_instruction,
 | |
|     sysvar::{
 | |
|         rewards::{self, Rewards},
 | |
|         Sysvar,
 | |
|     },
 | |
| };
 | |
| use solana_storage_program::{
 | |
|     id,
 | |
|     storage_contract::{ProofStatus, StorageContract},
 | |
|     storage_instruction::{self, StorageAccountType},
 | |
|     storage_processor::process_instruction,
 | |
| };
 | |
| use std::collections::HashMap;
 | |
| use std::sync::Arc;
 | |
| 
 | |
| const TICKS_IN_SEGMENT: u64 = DEFAULT_SLOTS_PER_SEGMENT * DEFAULT_TICKS_PER_SLOT;
 | |
| 
 | |
| #[test]
 | |
| fn test_account_owner() {
 | |
|     let account_owner = Pubkey::new_rand();
 | |
|     let validator_storage_keypair = Keypair::new();
 | |
|     let validator_storage_pubkey = validator_storage_keypair.pubkey();
 | |
|     let archiver_storage_keypair = Keypair::new();
 | |
|     let archiver_storage_pubkey = archiver_storage_keypair.pubkey();
 | |
| 
 | |
|     let GenesisConfigInfo {
 | |
|         genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(1000);
 | |
|     let mut bank = Bank::new(&genesis_config);
 | |
|     let mint_pubkey = mint_keypair.pubkey();
 | |
|     bank.add_instruction_processor(id(), process_instruction);
 | |
|     let bank = Arc::new(bank);
 | |
|     let bank_client = BankClient::new_shared(&bank);
 | |
| 
 | |
|     let message = Message::new(storage_instruction::create_storage_account(
 | |
|         &mint_pubkey,
 | |
|         &account_owner,
 | |
|         &validator_storage_pubkey,
 | |
|         1,
 | |
|         StorageAccountType::Validator,
 | |
|     ));
 | |
|     bank_client
 | |
|         .send_message(&[&mint_keypair, &validator_storage_keypair], message)
 | |
|         .expect("failed to create account");
 | |
|     let account = bank
 | |
|         .get_account(&validator_storage_pubkey)
 | |
|         .expect("account not found");
 | |
|     let storage_contract = account.state().expect("couldn't unpack account data");
 | |
|     if let StorageContract::ValidatorStorage { owner, .. } = storage_contract {
 | |
|         assert_eq!(owner, account_owner);
 | |
|     } else {
 | |
|         assert!(false, "wrong account type found")
 | |
|     }
 | |
| 
 | |
|     let message = Message::new(storage_instruction::create_storage_account(
 | |
|         &mint_pubkey,
 | |
|         &account_owner,
 | |
|         &archiver_storage_pubkey,
 | |
|         1,
 | |
|         StorageAccountType::Archiver,
 | |
|     ));
 | |
|     bank_client
 | |
|         .send_message(&[&mint_keypair, &archiver_storage_keypair], message)
 | |
|         .expect("failed to create account");
 | |
|     let account = bank
 | |
|         .get_account(&archiver_storage_pubkey)
 | |
|         .expect("account not found");
 | |
|     let storage_contract = account.state().expect("couldn't unpack account data");
 | |
|     if let StorageContract::ArchiverStorage { owner, .. } = storage_contract {
 | |
|         assert_eq!(owner, account_owner);
 | |
|     } else {
 | |
|         assert!(false, "wrong account type found")
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn test_validate_mining() {
 | |
|     solana_logger::setup();
 | |
|     let GenesisConfigInfo {
 | |
|         mut genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(100_000_000_000);
 | |
|     genesis_config
 | |
|         .native_instruction_processors
 | |
|         .push(solana_storage_program::solana_storage_program!());
 | |
|     let mint_pubkey = mint_keypair.pubkey();
 | |
|     // 1 owner for all archiver and validator accounts for the test
 | |
|     let owner_pubkey = Pubkey::new_rand();
 | |
| 
 | |
|     let archiver_1_storage_keypair = Keypair::new();
 | |
|     let archiver_1_storage_id = archiver_1_storage_keypair.pubkey();
 | |
| 
 | |
|     let archiver_2_storage_keypair = Keypair::new();
 | |
|     let archiver_2_storage_id = archiver_2_storage_keypair.pubkey();
 | |
| 
 | |
|     let validator_storage_keypair = Keypair::new();
 | |
|     let validator_storage_id = validator_storage_keypair.pubkey();
 | |
| 
 | |
|     let bank = Bank::new(&genesis_config);
 | |
|     let bank = Arc::new(bank);
 | |
|     let bank_client = BankClient::new_shared(&bank);
 | |
| 
 | |
|     init_storage_accounts(
 | |
|         &owner_pubkey,
 | |
|         &bank_client,
 | |
|         &mint_keypair,
 | |
|         &[&validator_storage_keypair],
 | |
|         &[&archiver_1_storage_keypair, &archiver_2_storage_keypair],
 | |
|         10,
 | |
|     );
 | |
| 
 | |
|     // create a new bank in segment 2
 | |
|     let bank = Arc::new(Bank::new_from_parent(
 | |
|         &bank,
 | |
|         &Pubkey::default(),
 | |
|         DEFAULT_SLOTS_PER_SEGMENT * 2,
 | |
|     ));
 | |
|     let bank_client = BankClient::new_shared(&bank);
 | |
| 
 | |
|     // advertise for storage segment 1
 | |
|     let message = Message::new_with_payer(
 | |
|         vec![storage_instruction::advertise_recent_blockhash(
 | |
|             &validator_storage_id,
 | |
|             Hash::default(),
 | |
|             1,
 | |
|         )],
 | |
|         Some(&mint_pubkey),
 | |
|     );
 | |
|     assert_matches!(
 | |
|         bank_client.send_message(&[&mint_keypair, &validator_storage_keypair], message),
 | |
|         Ok(_)
 | |
|     );
 | |
| 
 | |
|     // submit proofs 5 proofs for each archiver for segment 0
 | |
|     let mut checked_proofs: HashMap<_, Vec<_>> = HashMap::new();
 | |
|     for _ in 0..5 {
 | |
|         checked_proofs
 | |
|             .entry(archiver_1_storage_id)
 | |
|             .or_default()
 | |
|             .push(submit_proof(
 | |
|                 &mint_keypair,
 | |
|                 &archiver_1_storage_keypair,
 | |
|                 &bank_client,
 | |
|                 0,
 | |
|             ));
 | |
|         checked_proofs
 | |
|             .entry(archiver_2_storage_id)
 | |
|             .or_default()
 | |
|             .push(submit_proof(
 | |
|                 &mint_keypair,
 | |
|                 &archiver_2_storage_keypair,
 | |
|                 &bank_client,
 | |
|                 0,
 | |
|             ));
 | |
|     }
 | |
|     let message = Message::new_with_payer(
 | |
|         vec![storage_instruction::advertise_recent_blockhash(
 | |
|             &validator_storage_id,
 | |
|             Hash::default(),
 | |
|             2,
 | |
|         )],
 | |
|         Some(&mint_pubkey),
 | |
|     );
 | |
| 
 | |
|     // move banks into the next segment
 | |
|     let proof_segment = get_segment_from_slot(bank.slot(), bank.slots_per_segment());
 | |
|     let bank = Arc::new(Bank::new_from_parent(
 | |
|         &bank,
 | |
|         &Pubkey::default(),
 | |
|         DEFAULT_SLOTS_PER_SEGMENT + bank.slot(),
 | |
|     ));
 | |
|     let bank_client = BankClient::new_shared(&bank);
 | |
| 
 | |
|     assert_matches!(
 | |
|         bank_client.send_message(&[&mint_keypair, &validator_storage_keypair], message),
 | |
|         Ok(_)
 | |
|     );
 | |
| 
 | |
|     let message = Message::new_with_payer(
 | |
|         vec![storage_instruction::proof_validation(
 | |
|             &validator_storage_id,
 | |
|             proof_segment as u64,
 | |
|             checked_proofs.into_iter().map(|entry| entry).collect(),
 | |
|         )],
 | |
|         Some(&mint_pubkey),
 | |
|     );
 | |
| 
 | |
|     assert_matches!(
 | |
|         bank_client.send_message(&[&mint_keypair, &validator_storage_keypair], message),
 | |
|         Ok(_)
 | |
|     );
 | |
| 
 | |
|     let message = Message::new_with_payer(
 | |
|         vec![storage_instruction::advertise_recent_blockhash(
 | |
|             &validator_storage_id,
 | |
|             Hash::default(),
 | |
|             3,
 | |
|         )],
 | |
|         Some(&mint_pubkey),
 | |
|     );
 | |
| 
 | |
|     // move banks into the next segment
 | |
|     let bank = Arc::new(Bank::new_from_parent(
 | |
|         &bank,
 | |
|         &Pubkey::default(),
 | |
|         DEFAULT_SLOTS_PER_SEGMENT + bank.slot(),
 | |
|     ));
 | |
|     let bank_client = BankClient::new_shared(&bank);
 | |
| 
 | |
|     assert_matches!(
 | |
|         bank_client.send_message(&[&mint_keypair, &validator_storage_keypair], message),
 | |
|         Ok(_)
 | |
|     );
 | |
| 
 | |
|     assert_eq!(bank_client.get_balance(&validator_storage_id).unwrap(), 10);
 | |
| 
 | |
|     let bank = Arc::new(Bank::new_from_parent(
 | |
|         &bank,
 | |
|         &Pubkey::default(),
 | |
|         bank.slot() + bank.epoch_schedule().slots_per_epoch,
 | |
|     ));
 | |
|     let bank_client = BankClient::new_shared(&bank);
 | |
| 
 | |
|     let rewards = bank
 | |
|         .get_account(&rewards::id())
 | |
|         .map(|account| Rewards::from_account(&account).unwrap())
 | |
|         .unwrap();
 | |
|     let message = Message::new_with_payer(
 | |
|         vec![storage_instruction::claim_reward(
 | |
|             &owner_pubkey,
 | |
|             &validator_storage_id,
 | |
|         )],
 | |
|         Some(&mint_pubkey),
 | |
|     );
 | |
|     assert_matches!(bank_client.send_message(&[&mint_keypair], message), Ok(_));
 | |
|     assert_eq!(
 | |
|         bank_client.get_balance(&owner_pubkey).unwrap(),
 | |
|         1 + ((rewards.storage_point_value * 10_f64) as u64)
 | |
|     );
 | |
| 
 | |
|     // tick the bank into the next storage epoch so that rewards can be claimed
 | |
|     for _ in 0..=TICKS_IN_SEGMENT {
 | |
|         bank.register_tick(&bank.last_blockhash());
 | |
|     }
 | |
| 
 | |
|     assert_eq!(bank_client.get_balance(&archiver_1_storage_id).unwrap(), 10);
 | |
| 
 | |
|     let message = Message::new_with_payer(
 | |
|         vec![storage_instruction::claim_reward(
 | |
|             &owner_pubkey,
 | |
|             &archiver_1_storage_id,
 | |
|         )],
 | |
|         Some(&mint_pubkey),
 | |
|     );
 | |
|     assert_matches!(bank_client.send_message(&[&mint_keypair], message), Ok(_));
 | |
|     assert_eq!(
 | |
|         bank_client.get_balance(&owner_pubkey).unwrap(),
 | |
|         1 + ((rewards.storage_point_value * 10_f64) as u64)
 | |
|             + (rewards.storage_point_value * 5_f64) as u64
 | |
|     );
 | |
| 
 | |
|     let message = Message::new_with_payer(
 | |
|         vec![storage_instruction::claim_reward(
 | |
|             &owner_pubkey,
 | |
|             &archiver_2_storage_id,
 | |
|         )],
 | |
|         Some(&mint_pubkey),
 | |
|     );
 | |
|     assert_matches!(bank_client.send_message(&[&mint_keypair], message), Ok(_));
 | |
|     assert_eq!(
 | |
|         bank_client.get_balance(&owner_pubkey).unwrap(),
 | |
|         1 + (rewards.storage_point_value * 10_f64) as u64
 | |
|             + (rewards.storage_point_value * 5_f64) as u64
 | |
|             + (rewards.storage_point_value * 5_f64) as u64
 | |
|     );
 | |
| }
 | |
| 
 | |
| fn init_storage_accounts(
 | |
|     owner: &Pubkey,
 | |
|     client: &BankClient,
 | |
|     mint: &Keypair,
 | |
|     validator_accounts_to_create: &[&Keypair],
 | |
|     archiver_accounts_to_create: &[&Keypair],
 | |
|     lamports: u64,
 | |
| ) {
 | |
|     let mut signers = vec![mint];
 | |
|     let mut ixs: Vec<_> = vec![system_instruction::transfer(&mint.pubkey(), owner, 1)];
 | |
|     ixs.append(
 | |
|         &mut validator_accounts_to_create
 | |
|             .into_iter()
 | |
|             .flat_map(|account| {
 | |
|                 signers.push(&account);
 | |
|                 storage_instruction::create_storage_account(
 | |
|                     &mint.pubkey(),
 | |
|                     owner,
 | |
|                     &account.pubkey(),
 | |
|                     lamports,
 | |
|                     StorageAccountType::Validator,
 | |
|                 )
 | |
|             })
 | |
|             .collect(),
 | |
|     );
 | |
|     archiver_accounts_to_create.into_iter().for_each(|account| {
 | |
|         signers.push(&account);
 | |
|         ixs.append(&mut storage_instruction::create_storage_account(
 | |
|             &mint.pubkey(),
 | |
|             owner,
 | |
|             &account.pubkey(),
 | |
|             lamports,
 | |
|             StorageAccountType::Archiver,
 | |
|         ))
 | |
|     });
 | |
|     let message = Message::new(ixs);
 | |
|     client.send_message(&signers, message).unwrap();
 | |
| }
 | |
| 
 | |
| fn get_storage_segment<C: SyncClient>(client: &C, account: &Pubkey) -> u64 {
 | |
|     match client.get_account_data(&account).unwrap() {
 | |
|         Some(storage_system_account_data) => {
 | |
|             let contract = deserialize(&storage_system_account_data);
 | |
|             if let Ok(contract) = contract {
 | |
|                 match contract {
 | |
|                     StorageContract::ValidatorStorage { segment, .. } => {
 | |
|                         return segment;
 | |
|                     }
 | |
|                     _ => info!("error in reading segment"),
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         None => {
 | |
|             info!("error in reading segment");
 | |
|         }
 | |
|     }
 | |
|     0
 | |
| }
 | |
| 
 | |
| fn submit_proof(
 | |
|     mint_keypair: &Keypair,
 | |
|     storage_keypair: &Keypair,
 | |
|     bank_client: &BankClient,
 | |
|     segment_index: u64,
 | |
| ) -> ProofStatus {
 | |
|     let sha_state = Hash::new(Pubkey::new_rand().as_ref());
 | |
|     let message = Message::new_with_payer(
 | |
|         vec![storage_instruction::mining_proof(
 | |
|             &storage_keypair.pubkey(),
 | |
|             sha_state,
 | |
|             segment_index,
 | |
|             Signature::default(),
 | |
|             bank_client.get_recent_blockhash().unwrap().0,
 | |
|         )],
 | |
|         Some(&mint_keypair.pubkey()),
 | |
|     );
 | |
| 
 | |
|     assert_matches!(
 | |
|         bank_client.send_message(&[mint_keypair, storage_keypair], message),
 | |
|         Ok(_)
 | |
|     );
 | |
|     ProofStatus::Valid
 | |
| }
 | |
| 
 | |
| fn get_storage_blockhash<C: SyncClient>(client: &C, account: &Pubkey) -> Hash {
 | |
|     if let Some(storage_system_account_data) = client.get_account_data(&account).unwrap() {
 | |
|         let contract = deserialize(&storage_system_account_data);
 | |
|         if let Ok(contract) = contract {
 | |
|             match contract {
 | |
|                 StorageContract::ValidatorStorage { hash, .. } => {
 | |
|                     return hash;
 | |
|                 }
 | |
|                 _ => (),
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     Hash::default()
 | |
| }
 | |
| 
 | |
| #[test]
 | |
| fn test_bank_storage() {
 | |
|     let GenesisConfigInfo {
 | |
|         mut genesis_config,
 | |
|         mint_keypair,
 | |
|         ..
 | |
|     } = create_genesis_config(1000);
 | |
|     genesis_config
 | |
|         .native_instruction_processors
 | |
|         .push(solana_storage_program::solana_storage_program!());
 | |
|     let mint_pubkey = mint_keypair.pubkey();
 | |
|     let archiver_keypair = Keypair::new();
 | |
|     let archiver_pubkey = archiver_keypair.pubkey();
 | |
|     let validator_keypair = Keypair::new();
 | |
|     let validator_pubkey = validator_keypair.pubkey();
 | |
| 
 | |
|     let bank = Bank::new(&genesis_config);
 | |
|     // tick the bank up until it's moved into storage segment 2
 | |
|     // create a new bank in storage segment 2
 | |
|     let bank = Bank::new_from_parent(
 | |
|         &Arc::new(bank),
 | |
|         &Pubkey::new_rand(),
 | |
|         DEFAULT_SLOTS_PER_SEGMENT * 2,
 | |
|     );
 | |
|     let bank_client = BankClient::new(bank);
 | |
| 
 | |
|     let x = 42;
 | |
|     let x2 = x * 2;
 | |
|     let storage_blockhash = hash(&[x2]);
 | |
| 
 | |
|     let message = Message::new(storage_instruction::create_storage_account(
 | |
|         &mint_pubkey,
 | |
|         &Pubkey::default(),
 | |
|         &archiver_pubkey,
 | |
|         11,
 | |
|         StorageAccountType::Archiver,
 | |
|     ));
 | |
|     bank_client
 | |
|         .send_message(&[&mint_keypair, &archiver_keypair], message)
 | |
|         .unwrap();
 | |
| 
 | |
|     let message = Message::new(storage_instruction::create_storage_account(
 | |
|         &mint_pubkey,
 | |
|         &Pubkey::default(),
 | |
|         &validator_pubkey,
 | |
|         1,
 | |
|         StorageAccountType::Validator,
 | |
|     ));
 | |
|     bank_client
 | |
|         .send_message(&[&mint_keypair, &validator_keypair], message)
 | |
|         .unwrap();
 | |
| 
 | |
|     let message = Message::new_with_payer(
 | |
|         vec![storage_instruction::advertise_recent_blockhash(
 | |
|             &validator_pubkey,
 | |
|             storage_blockhash,
 | |
|             1,
 | |
|         )],
 | |
|         Some(&mint_pubkey),
 | |
|     );
 | |
| 
 | |
|     assert_matches!(
 | |
|         bank_client.send_message(&[&mint_keypair, &validator_keypair], message),
 | |
|         Ok(_)
 | |
|     );
 | |
| 
 | |
|     let slot = 0;
 | |
|     let message = Message::new_with_payer(
 | |
|         vec![storage_instruction::mining_proof(
 | |
|             &archiver_pubkey,
 | |
|             Hash::default(),
 | |
|             slot,
 | |
|             Signature::default(),
 | |
|             bank_client.get_recent_blockhash().unwrap().0,
 | |
|         )],
 | |
|         Some(&mint_pubkey),
 | |
|     );
 | |
|     assert_matches!(
 | |
|         bank_client.send_message(&[&mint_keypair, &archiver_keypair], message),
 | |
|         Ok(_)
 | |
|     );
 | |
| 
 | |
|     assert_eq!(get_storage_segment(&bank_client, &validator_pubkey), 1);
 | |
|     assert_eq!(
 | |
|         get_storage_blockhash(&bank_client, &validator_pubkey),
 | |
|         storage_blockhash
 | |
|     );
 | |
| }
 |