Reduce remaining program crates to boilerplate crates
This commit is contained in:
		
							
								
								
									
										19
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										19
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -2445,8 +2445,11 @@ name = "solana-storage-api"
 | 
				
			|||||||
version = "0.13.0"
 | 
					version = "0.13.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bincode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "bincode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 | 
					 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 | 
					 "solana-logger 0.13.0",
 | 
				
			||||||
 | 
					 "solana-runtime 0.13.0",
 | 
				
			||||||
 "solana-sdk 0.13.0",
 | 
					 "solana-sdk 0.13.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2454,12 +2457,8 @@ dependencies = [
 | 
				
			|||||||
name = "solana-storage-program"
 | 
					name = "solana-storage-program"
 | 
				
			||||||
version = "0.13.0"
 | 
					version = "0.13.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bincode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 | 
				
			||||||
 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 | 
				
			||||||
 "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 | 
				
			||||||
 "solana-logger 0.13.0",
 | 
					 "solana-logger 0.13.0",
 | 
				
			||||||
 "solana-runtime 0.13.0",
 | 
					 | 
				
			||||||
 "solana-sdk 0.13.0",
 | 
					 "solana-sdk 0.13.0",
 | 
				
			||||||
 "solana-storage-api 0.13.0",
 | 
					 "solana-storage-api 0.13.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -2469,8 +2468,10 @@ name = "solana-token-api"
 | 
				
			|||||||
version = "0.13.0"
 | 
					version = "0.13.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bincode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "bincode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 | 
					 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 | 
					 "solana-logger 0.13.0",
 | 
				
			||||||
 "solana-sdk 0.13.0",
 | 
					 "solana-sdk 0.13.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2478,12 +2479,10 @@ dependencies = [
 | 
				
			|||||||
name = "solana-token-program"
 | 
					name = "solana-token-program"
 | 
				
			||||||
version = "0.13.0"
 | 
					version = "0.13.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bincode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 | 
				
			||||||
 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 | 
				
			||||||
 "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 | 
				
			||||||
 "solana-logger 0.13.0",
 | 
					 "solana-logger 0.13.0",
 | 
				
			||||||
 "solana-sdk 0.13.0",
 | 
					 "solana-sdk 0.13.0",
 | 
				
			||||||
 | 
					 "solana-token-api 0.13.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
@@ -2502,6 +2501,9 @@ dependencies = [
 | 
				
			|||||||
 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 | 
					 "solana-logger 0.13.0",
 | 
				
			||||||
 | 
					 "solana-metrics 0.13.0",
 | 
				
			||||||
 | 
					 "solana-runtime 0.13.0",
 | 
				
			||||||
 "solana-sdk 0.13.0",
 | 
					 "solana-sdk 0.13.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2509,11 +2511,8 @@ dependencies = [
 | 
				
			|||||||
name = "solana-vote-program"
 | 
					name = "solana-vote-program"
 | 
				
			||||||
version = "0.13.0"
 | 
					version = "0.13.0"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "bincode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 | 
				
			||||||
 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
					 "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
				
			||||||
 "solana-logger 0.13.0",
 | 
					 "solana-logger 0.13.0",
 | 
				
			||||||
 "solana-metrics 0.13.0",
 | 
					 | 
				
			||||||
 "solana-runtime 0.13.0",
 | 
					 | 
				
			||||||
 "solana-sdk 0.13.0",
 | 
					 "solana-sdk 0.13.0",
 | 
				
			||||||
 "solana-vote-api 0.13.0",
 | 
					 "solana-vote-api 0.13.0",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -141,7 +141,7 @@ pub fn process_instruction(
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
mod test {
 | 
					mod tests {
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
    use crate::budget_instruction::BudgetInstruction;
 | 
					    use crate::budget_instruction::BudgetInstruction;
 | 
				
			||||||
    use crate::budget_script::BudgetScript;
 | 
					    use crate::budget_script::BudgetScript;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,10 +10,15 @@ edition = "2018"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
bincode = "1.1.2"
 | 
					bincode = "1.1.2"
 | 
				
			||||||
 | 
					log = "0.4.2"
 | 
				
			||||||
serde = "1.0.89"
 | 
					serde = "1.0.89"
 | 
				
			||||||
serde_derive = "1.0.89"
 | 
					serde_derive = "1.0.89"
 | 
				
			||||||
 | 
					solana-logger = { path = "../../logger", version = "0.13.0" }
 | 
				
			||||||
solana-sdk = { path = "../../sdk", version = "0.13.0" }
 | 
					solana-sdk = { path = "../../sdk", version = "0.13.0" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dev-dependencies]
 | 
				
			||||||
 | 
					solana-runtime = { path = "../../runtime", version = "0.13.0" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[lib]
 | 
					[lib]
 | 
				
			||||||
name = "solana_storage_api"
 | 
					name = "solana_storage_api"
 | 
				
			||||||
crate-type = ["lib"]
 | 
					crate-type = ["lib"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					pub mod storage_processor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use serde_derive::{Deserialize, Serialize};
 | 
					use serde_derive::{Deserialize, Serialize};
 | 
				
			||||||
use solana_sdk::hash::Hash;
 | 
					use solana_sdk::hash::Hash;
 | 
				
			||||||
use solana_sdk::pubkey::Pubkey;
 | 
					use solana_sdk::pubkey::Pubkey;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										460
									
								
								programs/storage_api/src/storage_processor.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										460
									
								
								programs/storage_api/src/storage_processor.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,460 @@
 | 
				
			|||||||
 | 
					//! storage program
 | 
				
			||||||
 | 
					//!  Receive mining proofs from miners, validate the answers
 | 
				
			||||||
 | 
					//!  and give reward for good proofs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::*;
 | 
				
			||||||
 | 
					use log::*;
 | 
				
			||||||
 | 
					use solana_sdk::account::KeyedAccount;
 | 
				
			||||||
 | 
					use solana_sdk::pubkey::Pubkey;
 | 
				
			||||||
 | 
					use solana_sdk::transaction::InstructionError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub const TOTAL_VALIDATOR_REWARDS: u64 = 1000;
 | 
				
			||||||
 | 
					pub const TOTAL_REPLICATOR_REWARDS: u64 = 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn count_valid_proofs(proofs: &[ProofStatus]) -> u64 {
 | 
				
			||||||
 | 
					    let mut num = 0;
 | 
				
			||||||
 | 
					    for proof in proofs {
 | 
				
			||||||
 | 
					        if let ProofStatus::Valid = proof {
 | 
				
			||||||
 | 
					            num += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    num
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn process_instruction(
 | 
				
			||||||
 | 
					    _program_id: &Pubkey,
 | 
				
			||||||
 | 
					    keyed_accounts: &mut [KeyedAccount],
 | 
				
			||||||
 | 
					    data: &[u8],
 | 
				
			||||||
 | 
					    _tick_height: u64,
 | 
				
			||||||
 | 
					) -> Result<(), InstructionError> {
 | 
				
			||||||
 | 
					    solana_logger::setup();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if keyed_accounts.len() != 1 {
 | 
				
			||||||
 | 
					        // keyed_accounts[1] should be the main storage key
 | 
				
			||||||
 | 
					        // to access its data
 | 
				
			||||||
 | 
					        Err(InstructionError::InvalidArgument)?;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // accounts_keys[0] must be signed
 | 
				
			||||||
 | 
					    if keyed_accounts[0].signer_key().is_none() {
 | 
				
			||||||
 | 
					        info!("account[0] is unsigned");
 | 
				
			||||||
 | 
					        Err(InstructionError::GenericError)?;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if let Ok(syscall) = bincode::deserialize(data) {
 | 
				
			||||||
 | 
					        let mut storage_account_state = if let Ok(storage_account_state) =
 | 
				
			||||||
 | 
					            bincode::deserialize(&keyed_accounts[0].account.data)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            storage_account_state
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            StorageProgramState::default()
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        debug!(
 | 
				
			||||||
 | 
					            "deserialized state height: {}",
 | 
				
			||||||
 | 
					            storage_account_state.entry_height
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        match syscall {
 | 
				
			||||||
 | 
					            StorageProgram::SubmitMiningProof {
 | 
				
			||||||
 | 
					                sha_state,
 | 
				
			||||||
 | 
					                entry_height,
 | 
				
			||||||
 | 
					                signature,
 | 
				
			||||||
 | 
					            } => {
 | 
				
			||||||
 | 
					                let segment_index = get_segment_from_entry(entry_height);
 | 
				
			||||||
 | 
					                let current_segment_index =
 | 
				
			||||||
 | 
					                    get_segment_from_entry(storage_account_state.entry_height);
 | 
				
			||||||
 | 
					                if segment_index >= current_segment_index {
 | 
				
			||||||
 | 
					                    return Err(InstructionError::InvalidArgument);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                debug!(
 | 
				
			||||||
 | 
					                    "Mining proof submitted with state {:?} entry_height: {}",
 | 
				
			||||||
 | 
					                    sha_state, entry_height
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let proof_info = ProofInfo {
 | 
				
			||||||
 | 
					                    id: *keyed_accounts[0].signer_key().unwrap(),
 | 
				
			||||||
 | 
					                    sha_state,
 | 
				
			||||||
 | 
					                    signature,
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                storage_account_state.proofs[segment_index].push(proof_info);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            StorageProgram::AdvertiseStorageRecentBlockhash { hash, entry_height } => {
 | 
				
			||||||
 | 
					                let original_segments = storage_account_state.entry_height / ENTRIES_PER_SEGMENT;
 | 
				
			||||||
 | 
					                let segments = entry_height / ENTRIES_PER_SEGMENT;
 | 
				
			||||||
 | 
					                debug!(
 | 
				
			||||||
 | 
					                    "advertise new last id segments: {} orig: {}",
 | 
				
			||||||
 | 
					                    segments, original_segments
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                if segments <= original_segments {
 | 
				
			||||||
 | 
					                    return Err(InstructionError::InvalidArgument);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                storage_account_state.entry_height = entry_height;
 | 
				
			||||||
 | 
					                storage_account_state.hash = hash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // move the proofs to previous_proofs
 | 
				
			||||||
 | 
					                storage_account_state.previous_proofs = storage_account_state.proofs.clone();
 | 
				
			||||||
 | 
					                storage_account_state.proofs.clear();
 | 
				
			||||||
 | 
					                storage_account_state
 | 
				
			||||||
 | 
					                    .proofs
 | 
				
			||||||
 | 
					                    .resize(segments as usize, Vec::new());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // move lockout_validations to reward_validations
 | 
				
			||||||
 | 
					                storage_account_state.reward_validations =
 | 
				
			||||||
 | 
					                    storage_account_state.lockout_validations.clone();
 | 
				
			||||||
 | 
					                storage_account_state.lockout_validations.clear();
 | 
				
			||||||
 | 
					                storage_account_state
 | 
				
			||||||
 | 
					                    .lockout_validations
 | 
				
			||||||
 | 
					                    .resize(segments as usize, Vec::new());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            StorageProgram::ProofValidation {
 | 
				
			||||||
 | 
					                entry_height,
 | 
				
			||||||
 | 
					                proof_mask,
 | 
				
			||||||
 | 
					            } => {
 | 
				
			||||||
 | 
					                if entry_height >= storage_account_state.entry_height {
 | 
				
			||||||
 | 
					                    return Err(InstructionError::InvalidArgument);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let segment_index = get_segment_from_entry(entry_height);
 | 
				
			||||||
 | 
					                if storage_account_state.previous_proofs[segment_index].len() != proof_mask.len() {
 | 
				
			||||||
 | 
					                    return Err(InstructionError::InvalidArgument);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // TODO: Check that each proof mask matches the signature
 | 
				
			||||||
 | 
					                /*for (i, entry) in proof_mask.iter().enumerate() {
 | 
				
			||||||
 | 
					                    if storage_account_state.previous_proofs[segment_index][i] != signature.as_ref[0] {
 | 
				
			||||||
 | 
					                        return Err(InstructionError::InvalidArgument);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let info = ValidationInfo {
 | 
				
			||||||
 | 
					                    id: *keyed_accounts[0].signer_key().unwrap(),
 | 
				
			||||||
 | 
					                    proof_mask,
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                storage_account_state.lockout_validations[segment_index].push(info);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            StorageProgram::ClaimStorageReward { entry_height } => {
 | 
				
			||||||
 | 
					                let claims_index = get_segment_from_entry(entry_height);
 | 
				
			||||||
 | 
					                let account_key = keyed_accounts[0].signer_key().unwrap();
 | 
				
			||||||
 | 
					                let mut num_validations = 0;
 | 
				
			||||||
 | 
					                let mut total_validations = 0;
 | 
				
			||||||
 | 
					                for validation in &storage_account_state.reward_validations[claims_index] {
 | 
				
			||||||
 | 
					                    if *account_key == validation.id {
 | 
				
			||||||
 | 
					                        num_validations += count_valid_proofs(&validation.proof_mask);
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        total_validations += count_valid_proofs(&validation.proof_mask);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                total_validations += num_validations;
 | 
				
			||||||
 | 
					                if total_validations > 0 {
 | 
				
			||||||
 | 
					                    keyed_accounts[0].account.lamports +=
 | 
				
			||||||
 | 
					                        (TOTAL_VALIDATOR_REWARDS * num_validations) / total_validations;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if bincode::serialize_into(
 | 
				
			||||||
 | 
					            &mut keyed_accounts[0].account.data[..],
 | 
				
			||||||
 | 
					            &storage_account_state,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .is_err()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return Err(InstructionError::AccountDataTooSmall);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        info!("Invalid instruction data: {:?}", data);
 | 
				
			||||||
 | 
					        Err(InstructionError::InvalidInstructionData)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					    use crate::{ProofStatus, StorageTransaction, ENTRIES_PER_SEGMENT};
 | 
				
			||||||
 | 
					    use bincode::deserialize;
 | 
				
			||||||
 | 
					    use solana_runtime::bank::Bank;
 | 
				
			||||||
 | 
					    use solana_sdk::account::{create_keyed_accounts, Account};
 | 
				
			||||||
 | 
					    use solana_sdk::genesis_block::GenesisBlock;
 | 
				
			||||||
 | 
					    use solana_sdk::hash::{hash, Hash};
 | 
				
			||||||
 | 
					    use solana_sdk::pubkey::Pubkey;
 | 
				
			||||||
 | 
					    use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
 | 
				
			||||||
 | 
					    use solana_sdk::system_transaction::SystemTransaction;
 | 
				
			||||||
 | 
					    use solana_sdk::transaction::{CompiledInstruction, Transaction};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn test_transaction(
 | 
				
			||||||
 | 
					        tx: &Transaction,
 | 
				
			||||||
 | 
					        program_accounts: &mut [Account],
 | 
				
			||||||
 | 
					    ) -> Result<(), InstructionError> {
 | 
				
			||||||
 | 
					        assert_eq!(tx.instructions.len(), 1);
 | 
				
			||||||
 | 
					        let CompiledInstruction {
 | 
				
			||||||
 | 
					            ref accounts,
 | 
				
			||||||
 | 
					            ref data,
 | 
				
			||||||
 | 
					            ..
 | 
				
			||||||
 | 
					        } = tx.instructions[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        info!("accounts: {:?}", accounts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut keyed_accounts: Vec<_> = accounts
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .map(|&index| {
 | 
				
			||||||
 | 
					                let index = index as usize;
 | 
				
			||||||
 | 
					                let key = &tx.account_keys[index];
 | 
				
			||||||
 | 
					                (key, index < tx.signatures.len())
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .zip(program_accounts.iter_mut())
 | 
				
			||||||
 | 
					            .map(|((key, is_signer), account)| KeyedAccount::new(key, is_signer, account))
 | 
				
			||||||
 | 
					            .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let ret = process_instruction(&id(), &mut keyed_accounts, &data, 42);
 | 
				
			||||||
 | 
					        info!("ret: {:?}", ret);
 | 
				
			||||||
 | 
					        ret
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_storage_tx() {
 | 
				
			||||||
 | 
					        let keypair = Keypair::new();
 | 
				
			||||||
 | 
					        let mut accounts = [(keypair.pubkey(), Account::default())];
 | 
				
			||||||
 | 
					        let mut keyed_accounts = create_keyed_accounts(&mut accounts);
 | 
				
			||||||
 | 
					        assert!(process_instruction(&id(), &mut keyed_accounts, &[], 42).is_err());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_serialize_overflow() {
 | 
				
			||||||
 | 
					        let keypair = Keypair::new();
 | 
				
			||||||
 | 
					        let mut keyed_accounts = Vec::new();
 | 
				
			||||||
 | 
					        let mut user_account = Account::default();
 | 
				
			||||||
 | 
					        let pubkey = keypair.pubkey();
 | 
				
			||||||
 | 
					        keyed_accounts.push(KeyedAccount::new(&pubkey, true, &mut user_account));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx = StorageTransaction::new_advertise_recent_blockhash(
 | 
				
			||||||
 | 
					            &keypair,
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            ENTRIES_PER_SEGMENT,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            process_instruction(&id(), &mut keyed_accounts, &tx.instructions[0].data, 42),
 | 
				
			||||||
 | 
					            Err(InstructionError::AccountDataTooSmall)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_invalid_accounts_len() {
 | 
				
			||||||
 | 
					        let keypair = Keypair::new();
 | 
				
			||||||
 | 
					        let mut accounts = [Account::default()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx = StorageTransaction::new_mining_proof(
 | 
				
			||||||
 | 
					            &keypair,
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            0,
 | 
				
			||||||
 | 
					            Signature::default(),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert!(test_transaction(&tx, &mut accounts).is_err());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut accounts = [Account::default(), Account::default(), Account::default()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert!(test_transaction(&tx, &mut accounts).is_err());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_submit_mining_invalid_entry_height() {
 | 
				
			||||||
 | 
					        solana_logger::setup();
 | 
				
			||||||
 | 
					        let keypair = Keypair::new();
 | 
				
			||||||
 | 
					        let mut accounts = [Account::default(), Account::default()];
 | 
				
			||||||
 | 
					        accounts[1].data.resize(16 * 1024, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx = StorageTransaction::new_mining_proof(
 | 
				
			||||||
 | 
					            &keypair,
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            0,
 | 
				
			||||||
 | 
					            Signature::default(),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Haven't seen a transaction to roll over the epoch, so this should fail
 | 
				
			||||||
 | 
					        assert!(test_transaction(&tx, &mut accounts).is_err());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_submit_mining_ok() {
 | 
				
			||||||
 | 
					        solana_logger::setup();
 | 
				
			||||||
 | 
					        let keypair = Keypair::new();
 | 
				
			||||||
 | 
					        let mut accounts = [Account::default(), Account::default()];
 | 
				
			||||||
 | 
					        accounts[0].data.resize(16 * 1024, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx = StorageTransaction::new_advertise_recent_blockhash(
 | 
				
			||||||
 | 
					            &keypair,
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            ENTRIES_PER_SEGMENT,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        test_transaction(&tx, &mut accounts).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx = StorageTransaction::new_mining_proof(
 | 
				
			||||||
 | 
					            &keypair,
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            0,
 | 
				
			||||||
 | 
					            Signature::default(),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        test_transaction(&tx, &mut accounts).unwrap();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_validate_mining() {
 | 
				
			||||||
 | 
					        solana_logger::setup();
 | 
				
			||||||
 | 
					        let keypair = Keypair::new();
 | 
				
			||||||
 | 
					        let mut accounts = [Account::default(), Account::default()];
 | 
				
			||||||
 | 
					        accounts[0].data.resize(16 * 1024, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let entry_height = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx = StorageTransaction::new_advertise_recent_blockhash(
 | 
				
			||||||
 | 
					            &keypair,
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            ENTRIES_PER_SEGMENT,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        test_transaction(&tx, &mut accounts).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx = StorageTransaction::new_mining_proof(
 | 
				
			||||||
 | 
					            &keypair,
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            entry_height,
 | 
				
			||||||
 | 
					            Signature::default(),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        test_transaction(&tx, &mut accounts).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx = StorageTransaction::new_advertise_recent_blockhash(
 | 
				
			||||||
 | 
					            &keypair,
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            ENTRIES_PER_SEGMENT * 2,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        test_transaction(&tx, &mut accounts).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx = StorageTransaction::new_proof_validation(
 | 
				
			||||||
 | 
					            &keypair,
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            entry_height,
 | 
				
			||||||
 | 
					            vec![ProofStatus::Valid],
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        test_transaction(&tx, &mut accounts).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx = StorageTransaction::new_advertise_recent_blockhash(
 | 
				
			||||||
 | 
					            &keypair,
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            ENTRIES_PER_SEGMENT * 3,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        test_transaction(&tx, &mut accounts).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx = StorageTransaction::new_reward_claim(&keypair, Hash::default(), entry_height);
 | 
				
			||||||
 | 
					        test_transaction(&tx, &mut accounts).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert!(accounts[0].lamports == TOTAL_VALIDATOR_REWARDS);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn get_storage_entry_height(bank: &Bank, account: &Pubkey) -> u64 {
 | 
				
			||||||
 | 
					        match bank.get_account(&account) {
 | 
				
			||||||
 | 
					            Some(storage_system_account) => {
 | 
				
			||||||
 | 
					                let state = deserialize(&storage_system_account.data);
 | 
				
			||||||
 | 
					                if let Ok(state) = state {
 | 
				
			||||||
 | 
					                    let state: StorageProgramState = state;
 | 
				
			||||||
 | 
					                    return state.entry_height;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            None => {
 | 
				
			||||||
 | 
					                info!("error in reading entry_height");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn get_storage_blockhash(bank: &Bank, account: &Pubkey) -> Hash {
 | 
				
			||||||
 | 
					        if let Some(storage_system_account) = bank.get_account(&account) {
 | 
				
			||||||
 | 
					            let state = deserialize(&storage_system_account.data);
 | 
				
			||||||
 | 
					            if let Ok(state) = state {
 | 
				
			||||||
 | 
					                let state: StorageProgramState = state;
 | 
				
			||||||
 | 
					                return state.hash;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Hash::default()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_bank_storage() {
 | 
				
			||||||
 | 
					        let (mut genesis_block, alice) = GenesisBlock::new(1000);
 | 
				
			||||||
 | 
					        genesis_block
 | 
				
			||||||
 | 
					            .native_programs
 | 
				
			||||||
 | 
					            .push(("solana_storage_program".to_string(), id()));
 | 
				
			||||||
 | 
					        let bank = Bank::new(&genesis_block);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let bob = Keypair::new();
 | 
				
			||||||
 | 
					        let jack = Keypair::new();
 | 
				
			||||||
 | 
					        let jill = Keypair::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let x = 42;
 | 
				
			||||||
 | 
					        let blockhash = genesis_block.hash();
 | 
				
			||||||
 | 
					        let x2 = x * 2;
 | 
				
			||||||
 | 
					        let storage_blockhash = hash(&[x2]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bank.register_tick(&blockhash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bank.transfer(10, &alice, &jill.pubkey(), blockhash)
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bank.transfer(10, &alice, &bob.pubkey(), blockhash).unwrap();
 | 
				
			||||||
 | 
					        bank.transfer(10, &alice, &jack.pubkey(), blockhash)
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx = SystemTransaction::new_program_account(
 | 
				
			||||||
 | 
					            &alice,
 | 
				
			||||||
 | 
					            &bob.pubkey(),
 | 
				
			||||||
 | 
					            blockhash,
 | 
				
			||||||
 | 
					            1,
 | 
				
			||||||
 | 
					            4 * 1024,
 | 
				
			||||||
 | 
					            &id(),
 | 
				
			||||||
 | 
					            0,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bank.process_transaction(&tx).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let tx = StorageTransaction::new_advertise_recent_blockhash(
 | 
				
			||||||
 | 
					            &bob,
 | 
				
			||||||
 | 
					            storage_blockhash,
 | 
				
			||||||
 | 
					            blockhash,
 | 
				
			||||||
 | 
					            ENTRIES_PER_SEGMENT,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bank.process_transaction(&tx).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let entry_height = 0;
 | 
				
			||||||
 | 
					        let tx = StorageTransaction::new_mining_proof(
 | 
				
			||||||
 | 
					            &bob,
 | 
				
			||||||
 | 
					            Hash::default(),
 | 
				
			||||||
 | 
					            blockhash,
 | 
				
			||||||
 | 
					            entry_height,
 | 
				
			||||||
 | 
					            Signature::default(),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        let _result = bank.process_transaction(&tx).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            get_storage_entry_height(&bank, &bob.pubkey()),
 | 
				
			||||||
 | 
					            ENTRIES_PER_SEGMENT
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            get_storage_blockhash(&bank, &bob.pubkey()),
 | 
				
			||||||
 | 
					            storage_blockhash
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -9,17 +9,11 @@ homepage = "https://solana.com/"
 | 
				
			|||||||
edition = "2018"
 | 
					edition = "2018"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
bincode = "1.1.2"
 | 
					 | 
				
			||||||
log = "0.4.2"
 | 
					log = "0.4.2"
 | 
				
			||||||
serde = "1.0.89"
 | 
					 | 
				
			||||||
serde_derive = "1.0.89"
 | 
					 | 
				
			||||||
solana-logger = { path = "../../logger", version = "0.13.0" }
 | 
					solana-logger = { path = "../../logger", version = "0.13.0" }
 | 
				
			||||||
solana-sdk = { path = "../../sdk", version = "0.13.0" }
 | 
					solana-sdk = { path = "../../sdk", version = "0.13.0" }
 | 
				
			||||||
solana-storage-api = { path = "../storage_api", version = "0.13.0" }
 | 
					solana-storage-api = { path = "../storage_api", version = "0.13.0" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dev-dependencies]
 | 
					 | 
				
			||||||
solana-runtime = { path = "../../runtime", version = "0.13.0" }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[lib]
 | 
					[lib]
 | 
				
			||||||
name = "solana_storage_program"
 | 
					name = "solana_storage_program"
 | 
				
			||||||
crate-type = ["cdylib"]
 | 
					crate-type = ["cdylib"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,365 +1,3 @@
 | 
				
			|||||||
//! storage program
 | 
					use solana_storage_api::storage_processor::process_instruction;
 | 
				
			||||||
//!  Receive mining proofs from miners, validate the answers
 | 
					 | 
				
			||||||
//!  and give reward for good proofs.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use log::*;
 | 
					solana_sdk::process_instruction_entrypoint!(process_instruction);
 | 
				
			||||||
extern crate solana_sdk;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use solana_sdk::account::KeyedAccount;
 | 
					 | 
				
			||||||
use solana_sdk::pubkey::Pubkey;
 | 
					 | 
				
			||||||
use solana_sdk::solana_entrypoint;
 | 
					 | 
				
			||||||
use solana_sdk::transaction::InstructionError;
 | 
					 | 
				
			||||||
use solana_storage_api::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub const TOTAL_VALIDATOR_REWARDS: u64 = 1000;
 | 
					 | 
				
			||||||
pub const TOTAL_REPLICATOR_REWARDS: u64 = 1000;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn count_valid_proofs(proofs: &[ProofStatus]) -> u64 {
 | 
					 | 
				
			||||||
    let mut num = 0;
 | 
					 | 
				
			||||||
    for proof in proofs {
 | 
					 | 
				
			||||||
        if let ProofStatus::Valid = proof {
 | 
					 | 
				
			||||||
            num += 1;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    num
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
solana_entrypoint!(entrypoint);
 | 
					 | 
				
			||||||
fn entrypoint(
 | 
					 | 
				
			||||||
    _program_id: &Pubkey,
 | 
					 | 
				
			||||||
    keyed_accounts: &mut [KeyedAccount],
 | 
					 | 
				
			||||||
    data: &[u8],
 | 
					 | 
				
			||||||
    _tick_height: u64,
 | 
					 | 
				
			||||||
) -> Result<(), InstructionError> {
 | 
					 | 
				
			||||||
    solana_logger::setup();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if keyed_accounts.len() != 1 {
 | 
					 | 
				
			||||||
        // keyed_accounts[1] should be the main storage key
 | 
					 | 
				
			||||||
        // to access its data
 | 
					 | 
				
			||||||
        Err(InstructionError::InvalidArgument)?;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // accounts_keys[0] must be signed
 | 
					 | 
				
			||||||
    if keyed_accounts[0].signer_key().is_none() {
 | 
					 | 
				
			||||||
        info!("account[0] is unsigned");
 | 
					 | 
				
			||||||
        Err(InstructionError::GenericError)?;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if let Ok(syscall) = bincode::deserialize(data) {
 | 
					 | 
				
			||||||
        let mut storage_account_state = if let Ok(storage_account_state) =
 | 
					 | 
				
			||||||
            bincode::deserialize(&keyed_accounts[0].account.data)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            storage_account_state
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            StorageProgramState::default()
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        debug!(
 | 
					 | 
				
			||||||
            "deserialized state height: {}",
 | 
					 | 
				
			||||||
            storage_account_state.entry_height
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        match syscall {
 | 
					 | 
				
			||||||
            StorageProgram::SubmitMiningProof {
 | 
					 | 
				
			||||||
                sha_state,
 | 
					 | 
				
			||||||
                entry_height,
 | 
					 | 
				
			||||||
                signature,
 | 
					 | 
				
			||||||
            } => {
 | 
					 | 
				
			||||||
                let segment_index = get_segment_from_entry(entry_height);
 | 
					 | 
				
			||||||
                let current_segment_index =
 | 
					 | 
				
			||||||
                    get_segment_from_entry(storage_account_state.entry_height);
 | 
					 | 
				
			||||||
                if segment_index >= current_segment_index {
 | 
					 | 
				
			||||||
                    return Err(InstructionError::InvalidArgument);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                debug!(
 | 
					 | 
				
			||||||
                    "Mining proof submitted with state {:?} entry_height: {}",
 | 
					 | 
				
			||||||
                    sha_state, entry_height
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                let proof_info = ProofInfo {
 | 
					 | 
				
			||||||
                    id: *keyed_accounts[0].signer_key().unwrap(),
 | 
					 | 
				
			||||||
                    sha_state,
 | 
					 | 
				
			||||||
                    signature,
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
                storage_account_state.proofs[segment_index].push(proof_info);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            StorageProgram::AdvertiseStorageRecentBlockhash { hash, entry_height } => {
 | 
					 | 
				
			||||||
                let original_segments = storage_account_state.entry_height / ENTRIES_PER_SEGMENT;
 | 
					 | 
				
			||||||
                let segments = entry_height / ENTRIES_PER_SEGMENT;
 | 
					 | 
				
			||||||
                debug!(
 | 
					 | 
				
			||||||
                    "advertise new last id segments: {} orig: {}",
 | 
					 | 
				
			||||||
                    segments, original_segments
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
                if segments <= original_segments {
 | 
					 | 
				
			||||||
                    return Err(InstructionError::InvalidArgument);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                storage_account_state.entry_height = entry_height;
 | 
					 | 
				
			||||||
                storage_account_state.hash = hash;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // move the proofs to previous_proofs
 | 
					 | 
				
			||||||
                storage_account_state.previous_proofs = storage_account_state.proofs.clone();
 | 
					 | 
				
			||||||
                storage_account_state.proofs.clear();
 | 
					 | 
				
			||||||
                storage_account_state
 | 
					 | 
				
			||||||
                    .proofs
 | 
					 | 
				
			||||||
                    .resize(segments as usize, Vec::new());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // move lockout_validations to reward_validations
 | 
					 | 
				
			||||||
                storage_account_state.reward_validations =
 | 
					 | 
				
			||||||
                    storage_account_state.lockout_validations.clone();
 | 
					 | 
				
			||||||
                storage_account_state.lockout_validations.clear();
 | 
					 | 
				
			||||||
                storage_account_state
 | 
					 | 
				
			||||||
                    .lockout_validations
 | 
					 | 
				
			||||||
                    .resize(segments as usize, Vec::new());
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            StorageProgram::ProofValidation {
 | 
					 | 
				
			||||||
                entry_height,
 | 
					 | 
				
			||||||
                proof_mask,
 | 
					 | 
				
			||||||
            } => {
 | 
					 | 
				
			||||||
                if entry_height >= storage_account_state.entry_height {
 | 
					 | 
				
			||||||
                    return Err(InstructionError::InvalidArgument);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                let segment_index = get_segment_from_entry(entry_height);
 | 
					 | 
				
			||||||
                if storage_account_state.previous_proofs[segment_index].len() != proof_mask.len() {
 | 
					 | 
				
			||||||
                    return Err(InstructionError::InvalidArgument);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // TODO: Check that each proof mask matches the signature
 | 
					 | 
				
			||||||
                /*for (i, entry) in proof_mask.iter().enumerate() {
 | 
					 | 
				
			||||||
                    if storage_account_state.previous_proofs[segment_index][i] != signature.as_ref[0] {
 | 
					 | 
				
			||||||
                        return Err(InstructionError::InvalidArgument);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                let info = ValidationInfo {
 | 
					 | 
				
			||||||
                    id: *keyed_accounts[0].signer_key().unwrap(),
 | 
					 | 
				
			||||||
                    proof_mask,
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
                storage_account_state.lockout_validations[segment_index].push(info);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            StorageProgram::ClaimStorageReward { entry_height } => {
 | 
					 | 
				
			||||||
                let claims_index = get_segment_from_entry(entry_height);
 | 
					 | 
				
			||||||
                let account_key = keyed_accounts[0].signer_key().unwrap();
 | 
					 | 
				
			||||||
                let mut num_validations = 0;
 | 
					 | 
				
			||||||
                let mut total_validations = 0;
 | 
					 | 
				
			||||||
                for validation in &storage_account_state.reward_validations[claims_index] {
 | 
					 | 
				
			||||||
                    if *account_key == validation.id {
 | 
					 | 
				
			||||||
                        num_validations += count_valid_proofs(&validation.proof_mask);
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        total_validations += count_valid_proofs(&validation.proof_mask);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                total_validations += num_validations;
 | 
					 | 
				
			||||||
                if total_validations > 0 {
 | 
					 | 
				
			||||||
                    keyed_accounts[0].account.lamports +=
 | 
					 | 
				
			||||||
                        (TOTAL_VALIDATOR_REWARDS * num_validations) / total_validations;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if bincode::serialize_into(
 | 
					 | 
				
			||||||
            &mut keyed_accounts[0].account.data[..],
 | 
					 | 
				
			||||||
            &storage_account_state,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        .is_err()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            return Err(InstructionError::AccountDataTooSmall);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        info!("Invalid instruction data: {:?}", data);
 | 
					 | 
				
			||||||
        Err(InstructionError::InvalidInstructionData)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[cfg(test)]
 | 
					 | 
				
			||||||
mod test {
 | 
					 | 
				
			||||||
    use super::*;
 | 
					 | 
				
			||||||
    use solana_sdk::account::{create_keyed_accounts, Account};
 | 
					 | 
				
			||||||
    use solana_sdk::hash::Hash;
 | 
					 | 
				
			||||||
    use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
 | 
					 | 
				
			||||||
    use solana_sdk::transaction::{CompiledInstruction, Transaction};
 | 
					 | 
				
			||||||
    use solana_storage_api::{ProofStatus, StorageTransaction};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn test_transaction(
 | 
					 | 
				
			||||||
        tx: &Transaction,
 | 
					 | 
				
			||||||
        program_accounts: &mut [Account],
 | 
					 | 
				
			||||||
    ) -> Result<(), InstructionError> {
 | 
					 | 
				
			||||||
        assert_eq!(tx.instructions.len(), 1);
 | 
					 | 
				
			||||||
        let CompiledInstruction {
 | 
					 | 
				
			||||||
            ref accounts,
 | 
					 | 
				
			||||||
            ref data,
 | 
					 | 
				
			||||||
            ..
 | 
					 | 
				
			||||||
        } = tx.instructions[0];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        info!("accounts: {:?}", accounts);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut keyed_accounts: Vec<_> = accounts
 | 
					 | 
				
			||||||
            .iter()
 | 
					 | 
				
			||||||
            .map(|&index| {
 | 
					 | 
				
			||||||
                let index = index as usize;
 | 
					 | 
				
			||||||
                let key = &tx.account_keys[index];
 | 
					 | 
				
			||||||
                (key, index < tx.signatures.len())
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            .zip(program_accounts.iter_mut())
 | 
					 | 
				
			||||||
            .map(|((key, is_signer), account)| KeyedAccount::new(key, is_signer, account))
 | 
					 | 
				
			||||||
            .collect();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let ret = entrypoint(&id(), &mut keyed_accounts, &data, 42);
 | 
					 | 
				
			||||||
        info!("ret: {:?}", ret);
 | 
					 | 
				
			||||||
        ret
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn test_storage_tx() {
 | 
					 | 
				
			||||||
        let keypair = Keypair::new();
 | 
					 | 
				
			||||||
        let mut accounts = [(keypair.pubkey(), Account::default())];
 | 
					 | 
				
			||||||
        let mut keyed_accounts = create_keyed_accounts(&mut accounts);
 | 
					 | 
				
			||||||
        assert!(entrypoint(&id(), &mut keyed_accounts, &[], 42).is_err());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn test_serialize_overflow() {
 | 
					 | 
				
			||||||
        let keypair = Keypair::new();
 | 
					 | 
				
			||||||
        let mut keyed_accounts = Vec::new();
 | 
					 | 
				
			||||||
        let mut user_account = Account::default();
 | 
					 | 
				
			||||||
        let pubkey = keypair.pubkey();
 | 
					 | 
				
			||||||
        keyed_accounts.push(KeyedAccount::new(&pubkey, true, &mut user_account));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let tx = StorageTransaction::new_advertise_recent_blockhash(
 | 
					 | 
				
			||||||
            &keypair,
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            ENTRIES_PER_SEGMENT,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert_eq!(
 | 
					 | 
				
			||||||
            entrypoint(&id(), &mut keyed_accounts, &tx.instructions[0].data, 42),
 | 
					 | 
				
			||||||
            Err(InstructionError::AccountDataTooSmall)
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn test_invalid_accounts_len() {
 | 
					 | 
				
			||||||
        let keypair = Keypair::new();
 | 
					 | 
				
			||||||
        let mut accounts = [Account::default()];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let tx = StorageTransaction::new_mining_proof(
 | 
					 | 
				
			||||||
            &keypair,
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            0,
 | 
					 | 
				
			||||||
            Signature::default(),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        assert!(test_transaction(&tx, &mut accounts).is_err());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut accounts = [Account::default(), Account::default(), Account::default()];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert!(test_transaction(&tx, &mut accounts).is_err());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn test_submit_mining_invalid_entry_height() {
 | 
					 | 
				
			||||||
        solana_logger::setup();
 | 
					 | 
				
			||||||
        let keypair = Keypair::new();
 | 
					 | 
				
			||||||
        let mut accounts = [Account::default(), Account::default()];
 | 
					 | 
				
			||||||
        accounts[1].data.resize(16 * 1024, 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let tx = StorageTransaction::new_mining_proof(
 | 
					 | 
				
			||||||
            &keypair,
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            0,
 | 
					 | 
				
			||||||
            Signature::default(),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Haven't seen a transaction to roll over the epoch, so this should fail
 | 
					 | 
				
			||||||
        assert!(test_transaction(&tx, &mut accounts).is_err());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn test_submit_mining_ok() {
 | 
					 | 
				
			||||||
        solana_logger::setup();
 | 
					 | 
				
			||||||
        let keypair = Keypair::new();
 | 
					 | 
				
			||||||
        let mut accounts = [Account::default(), Account::default()];
 | 
					 | 
				
			||||||
        accounts[0].data.resize(16 * 1024, 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let tx = StorageTransaction::new_advertise_recent_blockhash(
 | 
					 | 
				
			||||||
            &keypair,
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            ENTRIES_PER_SEGMENT,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        test_transaction(&tx, &mut accounts).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let tx = StorageTransaction::new_mining_proof(
 | 
					 | 
				
			||||||
            &keypair,
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            0,
 | 
					 | 
				
			||||||
            Signature::default(),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        test_transaction(&tx, &mut accounts).unwrap();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[test]
 | 
					 | 
				
			||||||
    fn test_validate_mining() {
 | 
					 | 
				
			||||||
        solana_logger::setup();
 | 
					 | 
				
			||||||
        let keypair = Keypair::new();
 | 
					 | 
				
			||||||
        let mut accounts = [Account::default(), Account::default()];
 | 
					 | 
				
			||||||
        accounts[0].data.resize(16 * 1024, 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let entry_height = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let tx = StorageTransaction::new_advertise_recent_blockhash(
 | 
					 | 
				
			||||||
            &keypair,
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            ENTRIES_PER_SEGMENT,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        test_transaction(&tx, &mut accounts).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let tx = StorageTransaction::new_mining_proof(
 | 
					 | 
				
			||||||
            &keypair,
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            entry_height,
 | 
					 | 
				
			||||||
            Signature::default(),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        test_transaction(&tx, &mut accounts).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let tx = StorageTransaction::new_advertise_recent_blockhash(
 | 
					 | 
				
			||||||
            &keypair,
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            ENTRIES_PER_SEGMENT * 2,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        test_transaction(&tx, &mut accounts).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let tx = StorageTransaction::new_proof_validation(
 | 
					 | 
				
			||||||
            &keypair,
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            entry_height,
 | 
					 | 
				
			||||||
            vec![ProofStatus::Valid],
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        test_transaction(&tx, &mut accounts).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let tx = StorageTransaction::new_advertise_recent_blockhash(
 | 
					 | 
				
			||||||
            &keypair,
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            Hash::default(),
 | 
					 | 
				
			||||||
            ENTRIES_PER_SEGMENT * 3,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        test_transaction(&tx, &mut accounts).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let tx = StorageTransaction::new_reward_claim(&keypair, Hash::default(), entry_height);
 | 
					 | 
				
			||||||
        test_transaction(&tx, &mut accounts).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        assert!(accounts[0].lamports == TOTAL_VALIDATOR_REWARDS);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,104 +0,0 @@
 | 
				
			|||||||
use bincode::deserialize;
 | 
					 | 
				
			||||||
use log::*;
 | 
					 | 
				
			||||||
use solana_runtime::bank::Bank;
 | 
					 | 
				
			||||||
use solana_sdk::genesis_block::GenesisBlock;
 | 
					 | 
				
			||||||
use solana_sdk::hash::{hash, Hash};
 | 
					 | 
				
			||||||
use solana_sdk::pubkey::Pubkey;
 | 
					 | 
				
			||||||
use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
 | 
					 | 
				
			||||||
use solana_sdk::system_transaction::SystemTransaction;
 | 
					 | 
				
			||||||
use solana_storage_api::{StorageTransaction, ENTRIES_PER_SEGMENT};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn get_storage_entry_height(bank: &Bank, account: &Pubkey) -> u64 {
 | 
					 | 
				
			||||||
    match bank.get_account(&account) {
 | 
					 | 
				
			||||||
        Some(storage_system_account) => {
 | 
					 | 
				
			||||||
            let state = deserialize(&storage_system_account.data);
 | 
					 | 
				
			||||||
            if let Ok(state) = state {
 | 
					 | 
				
			||||||
                let state: solana_storage_api::StorageProgramState = state;
 | 
					 | 
				
			||||||
                return state.entry_height;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        None => {
 | 
					 | 
				
			||||||
            info!("error in reading entry_height");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn get_storage_blockhash(bank: &Bank, account: &Pubkey) -> Hash {
 | 
					 | 
				
			||||||
    if let Some(storage_system_account) = bank.get_account(&account) {
 | 
					 | 
				
			||||||
        let state = deserialize(&storage_system_account.data);
 | 
					 | 
				
			||||||
        if let Ok(state) = state {
 | 
					 | 
				
			||||||
            let state: solana_storage_api::StorageProgramState = state;
 | 
					 | 
				
			||||||
            return state.hash;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    Hash::default()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[test]
 | 
					 | 
				
			||||||
fn test_bank_storage() {
 | 
					 | 
				
			||||||
    let (mut genesis_block, alice) = GenesisBlock::new(1000);
 | 
					 | 
				
			||||||
    genesis_block.native_programs.push((
 | 
					 | 
				
			||||||
        "solana_storage_program".to_string(),
 | 
					 | 
				
			||||||
        solana_storage_api::id(),
 | 
					 | 
				
			||||||
    ));
 | 
					 | 
				
			||||||
    let bank = Bank::new(&genesis_block);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let bob = Keypair::new();
 | 
					 | 
				
			||||||
    let jack = Keypair::new();
 | 
					 | 
				
			||||||
    let jill = Keypair::new();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let x = 42;
 | 
					 | 
				
			||||||
    let blockhash = genesis_block.hash();
 | 
					 | 
				
			||||||
    let x2 = x * 2;
 | 
					 | 
				
			||||||
    let storage_blockhash = hash(&[x2]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bank.register_tick(&blockhash);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bank.transfer(10, &alice, &jill.pubkey(), blockhash)
 | 
					 | 
				
			||||||
        .unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bank.transfer(10, &alice, &bob.pubkey(), blockhash).unwrap();
 | 
					 | 
				
			||||||
    bank.transfer(10, &alice, &jack.pubkey(), blockhash)
 | 
					 | 
				
			||||||
        .unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let tx = SystemTransaction::new_program_account(
 | 
					 | 
				
			||||||
        &alice,
 | 
					 | 
				
			||||||
        &bob.pubkey(),
 | 
					 | 
				
			||||||
        blockhash,
 | 
					 | 
				
			||||||
        1,
 | 
					 | 
				
			||||||
        4 * 1024,
 | 
					 | 
				
			||||||
        &solana_storage_api::id(),
 | 
					 | 
				
			||||||
        0,
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bank.process_transaction(&tx).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let tx = StorageTransaction::new_advertise_recent_blockhash(
 | 
					 | 
				
			||||||
        &bob,
 | 
					 | 
				
			||||||
        storage_blockhash,
 | 
					 | 
				
			||||||
        blockhash,
 | 
					 | 
				
			||||||
        ENTRIES_PER_SEGMENT,
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bank.process_transaction(&tx).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let entry_height = 0;
 | 
					 | 
				
			||||||
    let tx = StorageTransaction::new_mining_proof(
 | 
					 | 
				
			||||||
        &bob,
 | 
					 | 
				
			||||||
        Hash::default(),
 | 
					 | 
				
			||||||
        blockhash,
 | 
					 | 
				
			||||||
        entry_height,
 | 
					 | 
				
			||||||
        Signature::default(),
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    let _result = bank.process_transaction(&tx).unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    assert_eq!(
 | 
					 | 
				
			||||||
        get_storage_entry_height(&bank, &bob.pubkey()),
 | 
					 | 
				
			||||||
        ENTRIES_PER_SEGMENT
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    assert_eq!(
 | 
					 | 
				
			||||||
        get_storage_blockhash(&bank, &bob.pubkey()),
 | 
					 | 
				
			||||||
        storage_blockhash
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -10,8 +10,10 @@ edition = "2018"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
bincode = "1.1.2"
 | 
					bincode = "1.1.2"
 | 
				
			||||||
 | 
					log = "0.4.2"
 | 
				
			||||||
serde = "1.0.89"
 | 
					serde = "1.0.89"
 | 
				
			||||||
serde_derive = "1.0.89"
 | 
					serde_derive = "1.0.89"
 | 
				
			||||||
 | 
					solana-logger = { path = "../../logger", version = "0.13.0" }
 | 
				
			||||||
solana-sdk = { path = "../../sdk", version = "0.13.0" }
 | 
					solana-sdk = { path = "../../sdk", version = "0.13.0" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[lib]
 | 
					[lib]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,6 @@
 | 
				
			|||||||
 | 
					pub mod token_processor;
 | 
				
			||||||
 | 
					mod token_state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use solana_sdk::pubkey::Pubkey;
 | 
					use solana_sdk::pubkey::Pubkey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TOKEN_PROGRAM_ID: [u8; 32] = [
 | 
					const TOKEN_PROGRAM_ID: [u8; 32] = [
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								programs/token_api/src/token_processor.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								programs/token_api/src/token_processor.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					use crate::token_state::TokenState;
 | 
				
			||||||
 | 
					use bincode::serialize;
 | 
				
			||||||
 | 
					use log::*;
 | 
				
			||||||
 | 
					use solana_sdk::account::KeyedAccount;
 | 
				
			||||||
 | 
					use solana_sdk::pubkey::Pubkey;
 | 
				
			||||||
 | 
					use solana_sdk::transaction::InstructionError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn process_instruction(
 | 
				
			||||||
 | 
					    program_id: &Pubkey,
 | 
				
			||||||
 | 
					    info: &mut [KeyedAccount],
 | 
				
			||||||
 | 
					    input: &[u8],
 | 
				
			||||||
 | 
					    _tick_height: u64,
 | 
				
			||||||
 | 
					) -> Result<(), InstructionError> {
 | 
				
			||||||
 | 
					    solana_logger::setup();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TokenState::process(program_id, info, input).map_err(|e| {
 | 
				
			||||||
 | 
					        error!("error: {:?}", e);
 | 
				
			||||||
 | 
					        InstructionError::CustomError(serialize(&e).unwrap())
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -92,54 +92,54 @@ enum Command {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
 | 
					#[derive(Debug, Serialize, Deserialize, PartialEq)]
 | 
				
			||||||
pub enum TokenProgram {
 | 
					pub enum TokenState {
 | 
				
			||||||
    Unallocated,
 | 
					    Unallocated,
 | 
				
			||||||
    Token(TokenInfo),
 | 
					    Token(TokenInfo),
 | 
				
			||||||
    Account(TokenAccountInfo),
 | 
					    Account(TokenAccountInfo),
 | 
				
			||||||
    Invalid,
 | 
					    Invalid,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
impl Default for TokenProgram {
 | 
					impl Default for TokenState {
 | 
				
			||||||
    fn default() -> TokenProgram {
 | 
					    fn default() -> TokenState {
 | 
				
			||||||
        TokenProgram::Unallocated
 | 
					        TokenState::Unallocated
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl TokenProgram {
 | 
					impl TokenState {
 | 
				
			||||||
    #[allow(clippy::needless_pass_by_value)]
 | 
					    #[allow(clippy::needless_pass_by_value)]
 | 
				
			||||||
    fn map_to_invalid_args(err: std::boxed::Box<bincode::ErrorKind>) -> Error {
 | 
					    fn map_to_invalid_args(err: std::boxed::Box<bincode::ErrorKind>) -> Error {
 | 
				
			||||||
        warn!("invalid argument: {:?}", err);
 | 
					        warn!("invalid argument: {:?}", err);
 | 
				
			||||||
        Error::InvalidArgument
 | 
					        Error::InvalidArgument
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn deserialize(input: &[u8]) -> Result<TokenProgram> {
 | 
					    pub fn deserialize(input: &[u8]) -> Result<TokenState> {
 | 
				
			||||||
        if input.is_empty() {
 | 
					        if input.is_empty() {
 | 
				
			||||||
            Err(Error::InvalidArgument)?;
 | 
					            Err(Error::InvalidArgument)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        match input[0] {
 | 
					        match input[0] {
 | 
				
			||||||
            0 => Ok(TokenProgram::Unallocated),
 | 
					            0 => Ok(TokenState::Unallocated),
 | 
				
			||||||
            1 => Ok(TokenProgram::Token(
 | 
					            1 => Ok(TokenState::Token(
 | 
				
			||||||
                bincode::deserialize(&input[1..]).map_err(Self::map_to_invalid_args)?,
 | 
					                bincode::deserialize(&input[1..]).map_err(Self::map_to_invalid_args)?,
 | 
				
			||||||
            )),
 | 
					            )),
 | 
				
			||||||
            2 => Ok(TokenProgram::Account(
 | 
					            2 => Ok(TokenState::Account(
 | 
				
			||||||
                bincode::deserialize(&input[1..]).map_err(Self::map_to_invalid_args)?,
 | 
					                bincode::deserialize(&input[1..]).map_err(Self::map_to_invalid_args)?,
 | 
				
			||||||
            )),
 | 
					            )),
 | 
				
			||||||
            _ => Err(Error::InvalidArgument),
 | 
					            _ => Err(Error::InvalidArgument),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn serialize(self: &TokenProgram, output: &mut [u8]) -> Result<()> {
 | 
					    fn serialize(self: &TokenState, output: &mut [u8]) -> Result<()> {
 | 
				
			||||||
        if output.is_empty() {
 | 
					        if output.is_empty() {
 | 
				
			||||||
            warn!("serialize fail: ouput.len is 0");
 | 
					            warn!("serialize fail: ouput.len is 0");
 | 
				
			||||||
            Err(Error::InvalidArgument)?;
 | 
					            Err(Error::InvalidArgument)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            TokenProgram::Unallocated | TokenProgram::Invalid => Err(Error::InvalidArgument),
 | 
					            TokenState::Unallocated | TokenState::Invalid => Err(Error::InvalidArgument),
 | 
				
			||||||
            TokenProgram::Token(token_info) => {
 | 
					            TokenState::Token(token_info) => {
 | 
				
			||||||
                output[0] = 1;
 | 
					                output[0] = 1;
 | 
				
			||||||
                let writer = std::io::BufWriter::new(&mut output[1..]);
 | 
					                let writer = std::io::BufWriter::new(&mut output[1..]);
 | 
				
			||||||
                bincode::serialize_into(writer, &token_info).map_err(Self::map_to_invalid_args)
 | 
					                bincode::serialize_into(writer, &token_info).map_err(Self::map_to_invalid_args)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            TokenProgram::Account(account_info) => {
 | 
					            TokenState::Account(account_info) => {
 | 
				
			||||||
                output[0] = 2;
 | 
					                output[0] = 2;
 | 
				
			||||||
                let writer = std::io::BufWriter::new(&mut output[1..]);
 | 
					                let writer = std::io::BufWriter::new(&mut output[1..]);
 | 
				
			||||||
                bincode::serialize_into(writer, &account_info).map_err(Self::map_to_invalid_args)
 | 
					                bincode::serialize_into(writer, &account_info).map_err(Self::map_to_invalid_args)
 | 
				
			||||||
@@ -149,7 +149,7 @@ impl TokenProgram {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    #[allow(dead_code)]
 | 
					    #[allow(dead_code)]
 | 
				
			||||||
    pub fn amount(&self) -> Result<u64> {
 | 
					    pub fn amount(&self) -> Result<u64> {
 | 
				
			||||||
        if let TokenProgram::Account(account_info) = self {
 | 
					        if let TokenState::Account(account_info) = self {
 | 
				
			||||||
            Ok(account_info.amount)
 | 
					            Ok(account_info.amount)
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            Err(Error::InvalidArgument)
 | 
					            Err(Error::InvalidArgument)
 | 
				
			||||||
@@ -159,28 +159,28 @@ impl TokenProgram {
 | 
				
			|||||||
    #[allow(dead_code)]
 | 
					    #[allow(dead_code)]
 | 
				
			||||||
    pub fn only_owner(&self, key: &Pubkey) -> Result<()> {
 | 
					    pub fn only_owner(&self, key: &Pubkey) -> Result<()> {
 | 
				
			||||||
        if *key != Pubkey::default() {
 | 
					        if *key != Pubkey::default() {
 | 
				
			||||||
            if let TokenProgram::Account(account_info) = self {
 | 
					            if let TokenState::Account(account_info) = self {
 | 
				
			||||||
                if account_info.owner == *key {
 | 
					                if account_info.owner == *key {
 | 
				
			||||||
                    return Ok(());
 | 
					                    return Ok(());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        warn!("TokenProgram: non-owner rejected");
 | 
					        warn!("TokenState: non-owner rejected");
 | 
				
			||||||
        Err(Error::NotOwner)
 | 
					        Err(Error::NotOwner)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn process_command_newtoken(
 | 
					    pub fn process_command_newtoken(
 | 
				
			||||||
        info: &mut [KeyedAccount],
 | 
					        info: &mut [KeyedAccount],
 | 
				
			||||||
        token_info: TokenInfo,
 | 
					        token_info: TokenInfo,
 | 
				
			||||||
        input_program_accounts: &[TokenProgram],
 | 
					        input_program_accounts: &[TokenState],
 | 
				
			||||||
        output_program_accounts: &mut Vec<(usize, TokenProgram)>,
 | 
					        output_program_accounts: &mut Vec<(usize, TokenState)>,
 | 
				
			||||||
    ) -> Result<()> {
 | 
					    ) -> Result<()> {
 | 
				
			||||||
        if input_program_accounts.len() != 2 {
 | 
					        if input_program_accounts.len() != 2 {
 | 
				
			||||||
            error!("Expected 2 accounts");
 | 
					            error!("Expected 2 accounts");
 | 
				
			||||||
            Err(Error::InvalidArgument)?;
 | 
					            Err(Error::InvalidArgument)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let TokenProgram::Account(dest_account) = &input_program_accounts[1] {
 | 
					        if let TokenState::Account(dest_account) = &input_program_accounts[1] {
 | 
				
			||||||
            if info[0].signer_key().unwrap() != &dest_account.token {
 | 
					            if info[0].signer_key().unwrap() != &dest_account.token {
 | 
				
			||||||
                error!("account 1 token mismatch");
 | 
					                error!("account 1 token mismatch");
 | 
				
			||||||
                Err(Error::InvalidArgument)?;
 | 
					                Err(Error::InvalidArgument)?;
 | 
				
			||||||
@@ -193,24 +193,24 @@ impl TokenProgram {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            let mut output_dest_account = dest_account.clone();
 | 
					            let mut output_dest_account = dest_account.clone();
 | 
				
			||||||
            output_dest_account.amount = token_info.supply;
 | 
					            output_dest_account.amount = token_info.supply;
 | 
				
			||||||
            output_program_accounts.push((1, TokenProgram::Account(output_dest_account)));
 | 
					            output_program_accounts.push((1, TokenState::Account(output_dest_account)));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            error!("account 1 invalid");
 | 
					            error!("account 1 invalid");
 | 
				
			||||||
            Err(Error::InvalidArgument)?;
 | 
					            Err(Error::InvalidArgument)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if input_program_accounts[0] != TokenProgram::Unallocated {
 | 
					        if input_program_accounts[0] != TokenState::Unallocated {
 | 
				
			||||||
            error!("account 0 not available");
 | 
					            error!("account 0 not available");
 | 
				
			||||||
            Err(Error::InvalidArgument)?;
 | 
					            Err(Error::InvalidArgument)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        output_program_accounts.push((0, TokenProgram::Token(token_info)));
 | 
					        output_program_accounts.push((0, TokenState::Token(token_info)));
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn process_command_newaccount(
 | 
					    pub fn process_command_newaccount(
 | 
				
			||||||
        info: &mut [KeyedAccount],
 | 
					        info: &mut [KeyedAccount],
 | 
				
			||||||
        input_program_accounts: &[TokenProgram],
 | 
					        input_program_accounts: &[TokenState],
 | 
				
			||||||
        output_program_accounts: &mut Vec<(usize, TokenProgram)>,
 | 
					        output_program_accounts: &mut Vec<(usize, TokenState)>,
 | 
				
			||||||
    ) -> Result<()> {
 | 
					    ) -> Result<()> {
 | 
				
			||||||
        // key 0 - Destination new token account
 | 
					        // key 0 - Destination new token account
 | 
				
			||||||
        // key 1 - Owner of the account
 | 
					        // key 1 - Owner of the account
 | 
				
			||||||
@@ -220,7 +220,7 @@ impl TokenProgram {
 | 
				
			|||||||
            error!("Expected 3 accounts");
 | 
					            error!("Expected 3 accounts");
 | 
				
			||||||
            Err(Error::InvalidArgument)?;
 | 
					            Err(Error::InvalidArgument)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if input_program_accounts[0] != TokenProgram::Unallocated {
 | 
					        if input_program_accounts[0] != TokenState::Unallocated {
 | 
				
			||||||
            error!("account 0 is already allocated");
 | 
					            error!("account 0 is already allocated");
 | 
				
			||||||
            Err(Error::InvalidArgument)?;
 | 
					            Err(Error::InvalidArgument)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -236,22 +236,22 @@ impl TokenProgram {
 | 
				
			|||||||
                original_amount: 0,
 | 
					                original_amount: 0,
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        output_program_accounts.push((0, TokenProgram::Account(token_account_info)));
 | 
					        output_program_accounts.push((0, TokenState::Account(token_account_info)));
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn process_command_transfer(
 | 
					    pub fn process_command_transfer(
 | 
				
			||||||
        info: &mut [KeyedAccount],
 | 
					        info: &mut [KeyedAccount],
 | 
				
			||||||
        amount: u64,
 | 
					        amount: u64,
 | 
				
			||||||
        input_program_accounts: &[TokenProgram],
 | 
					        input_program_accounts: &[TokenState],
 | 
				
			||||||
        output_program_accounts: &mut Vec<(usize, TokenProgram)>,
 | 
					        output_program_accounts: &mut Vec<(usize, TokenState)>,
 | 
				
			||||||
    ) -> Result<()> {
 | 
					    ) -> Result<()> {
 | 
				
			||||||
        if input_program_accounts.len() < 3 {
 | 
					        if input_program_accounts.len() < 3 {
 | 
				
			||||||
            error!("Expected 3 accounts");
 | 
					            error!("Expected 3 accounts");
 | 
				
			||||||
            Err(Error::InvalidArgument)?;
 | 
					            Err(Error::InvalidArgument)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let (TokenProgram::Account(source_account), TokenProgram::Account(dest_account)) =
 | 
					        if let (TokenState::Account(source_account), TokenState::Account(dest_account)) =
 | 
				
			||||||
            (&input_program_accounts[1], &input_program_accounts[2])
 | 
					            (&input_program_accounts[1], &input_program_accounts[2])
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if source_account.token != dest_account.token {
 | 
					            if source_account.token != dest_account.token {
 | 
				
			||||||
@@ -275,7 +275,7 @@ impl TokenProgram {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            let mut output_source_account = source_account.clone();
 | 
					            let mut output_source_account = source_account.clone();
 | 
				
			||||||
            output_source_account.amount -= amount;
 | 
					            output_source_account.amount -= amount;
 | 
				
			||||||
            output_program_accounts.push((1, TokenProgram::Account(output_source_account)));
 | 
					            output_program_accounts.push((1, TokenState::Account(output_source_account)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if let Some(ref delegate_info) = source_account.delegate {
 | 
					            if let Some(ref delegate_info) = source_account.delegate {
 | 
				
			||||||
                if input_program_accounts.len() != 4 {
 | 
					                if input_program_accounts.len() != 4 {
 | 
				
			||||||
@@ -284,7 +284,7 @@ impl TokenProgram {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let delegate_account = source_account;
 | 
					                let delegate_account = source_account;
 | 
				
			||||||
                if let TokenProgram::Account(source_account) = &input_program_accounts[3] {
 | 
					                if let TokenState::Account(source_account) = &input_program_accounts[3] {
 | 
				
			||||||
                    if source_account.token != delegate_account.token {
 | 
					                    if source_account.token != delegate_account.token {
 | 
				
			||||||
                        error!("account 1/3 token mismatch");
 | 
					                        error!("account 1/3 token mismatch");
 | 
				
			||||||
                        Err(Error::InvalidArgument)?;
 | 
					                        Err(Error::InvalidArgument)?;
 | 
				
			||||||
@@ -300,7 +300,7 @@ impl TokenProgram {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    let mut output_source_account = source_account.clone();
 | 
					                    let mut output_source_account = source_account.clone();
 | 
				
			||||||
                    output_source_account.amount -= amount;
 | 
					                    output_source_account.amount -= amount;
 | 
				
			||||||
                    output_program_accounts.push((3, TokenProgram::Account(output_source_account)));
 | 
					                    output_program_accounts.push((3, TokenState::Account(output_source_account)));
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    error!("account 3 is an invalid account");
 | 
					                    error!("account 3 is an invalid account");
 | 
				
			||||||
                    Err(Error::InvalidArgument)?;
 | 
					                    Err(Error::InvalidArgument)?;
 | 
				
			||||||
@@ -309,7 +309,7 @@ impl TokenProgram {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            let mut output_dest_account = dest_account.clone();
 | 
					            let mut output_dest_account = dest_account.clone();
 | 
				
			||||||
            output_dest_account.amount += amount;
 | 
					            output_dest_account.amount += amount;
 | 
				
			||||||
            output_program_accounts.push((2, TokenProgram::Account(output_dest_account)));
 | 
					            output_program_accounts.push((2, TokenState::Account(output_dest_account)));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            error!("account 1 and/or 2 are invalid accounts");
 | 
					            error!("account 1 and/or 2 are invalid accounts");
 | 
				
			||||||
            Err(Error::InvalidArgument)?;
 | 
					            Err(Error::InvalidArgument)?;
 | 
				
			||||||
@@ -320,15 +320,15 @@ impl TokenProgram {
 | 
				
			|||||||
    pub fn process_command_approve(
 | 
					    pub fn process_command_approve(
 | 
				
			||||||
        info: &mut [KeyedAccount],
 | 
					        info: &mut [KeyedAccount],
 | 
				
			||||||
        amount: u64,
 | 
					        amount: u64,
 | 
				
			||||||
        input_program_accounts: &[TokenProgram],
 | 
					        input_program_accounts: &[TokenState],
 | 
				
			||||||
        output_program_accounts: &mut Vec<(usize, TokenProgram)>,
 | 
					        output_program_accounts: &mut Vec<(usize, TokenState)>,
 | 
				
			||||||
    ) -> Result<()> {
 | 
					    ) -> Result<()> {
 | 
				
			||||||
        if input_program_accounts.len() != 3 {
 | 
					        if input_program_accounts.len() != 3 {
 | 
				
			||||||
            error!("Expected 3 accounts");
 | 
					            error!("Expected 3 accounts");
 | 
				
			||||||
            Err(Error::InvalidArgument)?;
 | 
					            Err(Error::InvalidArgument)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let (TokenProgram::Account(source_account), TokenProgram::Account(delegate_account)) =
 | 
					        if let (TokenState::Account(source_account), TokenState::Account(delegate_account)) =
 | 
				
			||||||
            (&input_program_accounts[1], &input_program_accounts[2])
 | 
					            (&input_program_accounts[1], &input_program_accounts[2])
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if source_account.token != delegate_account.token {
 | 
					            if source_account.token != delegate_account.token {
 | 
				
			||||||
@@ -363,8 +363,7 @@ impl TokenProgram {
 | 
				
			|||||||
                        source: delegate_info.source,
 | 
					                        source: delegate_info.source,
 | 
				
			||||||
                        original_amount: amount,
 | 
					                        original_amount: amount,
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                    output_program_accounts
 | 
					                    output_program_accounts.push((2, TokenState::Account(output_delegate_account)));
 | 
				
			||||||
                        .push((2, TokenProgram::Account(output_delegate_account)));
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@@ -376,15 +375,15 @@ impl TokenProgram {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    pub fn process_command_setowner(
 | 
					    pub fn process_command_setowner(
 | 
				
			||||||
        info: &mut [KeyedAccount],
 | 
					        info: &mut [KeyedAccount],
 | 
				
			||||||
        input_program_accounts: &[TokenProgram],
 | 
					        input_program_accounts: &[TokenState],
 | 
				
			||||||
        output_program_accounts: &mut Vec<(usize, TokenProgram)>,
 | 
					        output_program_accounts: &mut Vec<(usize, TokenState)>,
 | 
				
			||||||
    ) -> Result<()> {
 | 
					    ) -> Result<()> {
 | 
				
			||||||
        if input_program_accounts.len() < 3 {
 | 
					        if input_program_accounts.len() < 3 {
 | 
				
			||||||
            error!("Expected 3 accounts");
 | 
					            error!("Expected 3 accounts");
 | 
				
			||||||
            Err(Error::InvalidArgument)?;
 | 
					            Err(Error::InvalidArgument)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let TokenProgram::Account(source_account) = &input_program_accounts[1] {
 | 
					        if let TokenState::Account(source_account) = &input_program_accounts[1] {
 | 
				
			||||||
            if info[0].signer_key().unwrap() != &source_account.owner {
 | 
					            if info[0].signer_key().unwrap() != &source_account.owner {
 | 
				
			||||||
                info!("owner of account 1 not present");
 | 
					                info!("owner of account 1 not present");
 | 
				
			||||||
                Err(Error::InvalidArgument)?;
 | 
					                Err(Error::InvalidArgument)?;
 | 
				
			||||||
@@ -392,7 +391,7 @@ impl TokenProgram {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            let mut output_source_account = source_account.clone();
 | 
					            let mut output_source_account = source_account.clone();
 | 
				
			||||||
            output_source_account.owner = *info[2].unsigned_key();
 | 
					            output_source_account.owner = *info[2].unsigned_key();
 | 
				
			||||||
            output_program_accounts.push((1, TokenProgram::Account(output_source_account)));
 | 
					            output_program_accounts.push((1, TokenState::Account(output_source_account)));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            info!("account 1 is invalid");
 | 
					            info!("account 1 is invalid");
 | 
				
			||||||
            Err(Error::InvalidArgument)?;
 | 
					            Err(Error::InvalidArgument)?;
 | 
				
			||||||
@@ -408,7 +407,7 @@ impl TokenProgram {
 | 
				
			|||||||
            Err(Error::InvalidArgument)?;
 | 
					            Err(Error::InvalidArgument)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let input_program_accounts: Vec<TokenProgram> = info
 | 
					        let input_program_accounts: Vec<TokenState> = info
 | 
				
			||||||
            .iter()
 | 
					            .iter()
 | 
				
			||||||
            .map(|keyed_account| {
 | 
					            .map(|keyed_account| {
 | 
				
			||||||
                let account = &keyed_account.account;
 | 
					                let account = &keyed_account.account;
 | 
				
			||||||
@@ -417,11 +416,11 @@ impl TokenProgram {
 | 
				
			|||||||
                        Ok(token_program) => token_program,
 | 
					                        Ok(token_program) => token_program,
 | 
				
			||||||
                        Err(err) => {
 | 
					                        Err(err) => {
 | 
				
			||||||
                            error!("deserialize failed: {:?}", err);
 | 
					                            error!("deserialize failed: {:?}", err);
 | 
				
			||||||
                            TokenProgram::Invalid
 | 
					                            TokenState::Invalid
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    TokenProgram::Invalid
 | 
					                    TokenState::Invalid
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .collect();
 | 
					            .collect();
 | 
				
			||||||
@@ -482,47 +481,47 @@ mod test {
 | 
				
			|||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    pub fn serde() {
 | 
					    pub fn serde() {
 | 
				
			||||||
        assert_eq!(TokenProgram::deserialize(&[0]), Ok(TokenProgram::default()));
 | 
					        assert_eq!(TokenState::deserialize(&[0]), Ok(TokenState::default()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut data = vec![0; 256];
 | 
					        let mut data = vec![0; 256];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let account = TokenProgram::Account(TokenAccountInfo {
 | 
					        let account = TokenState::Account(TokenAccountInfo {
 | 
				
			||||||
            token: Pubkey::new(&[1; 32]),
 | 
					            token: Pubkey::new(&[1; 32]),
 | 
				
			||||||
            owner: Pubkey::new(&[2; 32]),
 | 
					            owner: Pubkey::new(&[2; 32]),
 | 
				
			||||||
            amount: 123,
 | 
					            amount: 123,
 | 
				
			||||||
            delegate: None,
 | 
					            delegate: None,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        account.serialize(&mut data).unwrap();
 | 
					        account.serialize(&mut data).unwrap();
 | 
				
			||||||
        assert_eq!(TokenProgram::deserialize(&data), Ok(account));
 | 
					        assert_eq!(TokenState::deserialize(&data), Ok(account));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let account = TokenProgram::Token(TokenInfo {
 | 
					        let account = TokenState::Token(TokenInfo {
 | 
				
			||||||
            supply: 12345,
 | 
					            supply: 12345,
 | 
				
			||||||
            decimals: 2,
 | 
					            decimals: 2,
 | 
				
			||||||
            name: "A test token".to_string(),
 | 
					            name: "A test token".to_string(),
 | 
				
			||||||
            symbol: "TEST".to_string(),
 | 
					            symbol: "TEST".to_string(),
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        account.serialize(&mut data).unwrap();
 | 
					        account.serialize(&mut data).unwrap();
 | 
				
			||||||
        assert_eq!(TokenProgram::deserialize(&data), Ok(account));
 | 
					        assert_eq!(TokenState::deserialize(&data), Ok(account));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    pub fn serde_expect_fail() {
 | 
					    pub fn serde_expect_fail() {
 | 
				
			||||||
        let mut data = vec![0; 256];
 | 
					        let mut data = vec![0; 256];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Certain TokenProgram's may not be serialized
 | 
					        // Certain TokenState's may not be serialized
 | 
				
			||||||
        let account = TokenProgram::default();
 | 
					        let account = TokenState::default();
 | 
				
			||||||
        assert_eq!(account, TokenProgram::Unallocated);
 | 
					        assert_eq!(account, TokenState::Unallocated);
 | 
				
			||||||
        assert!(account.serialize(&mut data).is_err());
 | 
					        assert!(account.serialize(&mut data).is_err());
 | 
				
			||||||
        assert!(account.serialize(&mut data).is_err());
 | 
					        assert!(account.serialize(&mut data).is_err());
 | 
				
			||||||
        let account = TokenProgram::Invalid;
 | 
					        let account = TokenState::Invalid;
 | 
				
			||||||
        assert!(account.serialize(&mut data).is_err());
 | 
					        assert!(account.serialize(&mut data).is_err());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Bad deserialize data
 | 
					        // Bad deserialize data
 | 
				
			||||||
        assert!(TokenProgram::deserialize(&[]).is_err());
 | 
					        assert!(TokenState::deserialize(&[]).is_err());
 | 
				
			||||||
        assert!(TokenProgram::deserialize(&[1]).is_err());
 | 
					        assert!(TokenState::deserialize(&[1]).is_err());
 | 
				
			||||||
        assert!(TokenProgram::deserialize(&[1, 2]).is_err());
 | 
					        assert!(TokenState::deserialize(&[1, 2]).is_err());
 | 
				
			||||||
        assert!(TokenProgram::deserialize(&[2, 2]).is_err());
 | 
					        assert!(TokenState::deserialize(&[2, 2]).is_err());
 | 
				
			||||||
        assert!(TokenProgram::deserialize(&[3]).is_err());
 | 
					        assert!(TokenState::deserialize(&[3]).is_err());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Note: business logic tests are located in the @solana/web3.js test suite
 | 
					    // Note: business logic tests are located in the @solana/web3.js test suite
 | 
				
			||||||
@@ -9,12 +9,10 @@ homepage = "https://solana.com/"
 | 
				
			|||||||
edition = "2018"
 | 
					edition = "2018"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
bincode = "1.1.2"
 | 
					 | 
				
			||||||
log = "0.4.2"
 | 
					log = "0.4.2"
 | 
				
			||||||
serde = "1.0.89"
 | 
					 | 
				
			||||||
serde_derive = "1.0.89"
 | 
					 | 
				
			||||||
solana-logger = { path = "../../logger", version = "0.13.0" }
 | 
					solana-logger = { path = "../../logger", version = "0.13.0" }
 | 
				
			||||||
solana-sdk = { path = "../../sdk", version = "0.13.0" }
 | 
					solana-sdk = { path = "../../sdk", version = "0.13.0" }
 | 
				
			||||||
 | 
					solana-token-api = { path = "../token_api", version = "0.13.0" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[lib]
 | 
					[lib]
 | 
				
			||||||
name = "solana_token_program"
 | 
					name = "solana_token_program"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,23 +1,3 @@
 | 
				
			|||||||
use bincode::serialize;
 | 
					use solana_token_api::token_processor::process_instruction;
 | 
				
			||||||
use log::*;
 | 
					 | 
				
			||||||
use solana_sdk::account::KeyedAccount;
 | 
					 | 
				
			||||||
use solana_sdk::pubkey::Pubkey;
 | 
					 | 
				
			||||||
use solana_sdk::solana_entrypoint;
 | 
					 | 
				
			||||||
use solana_sdk::transaction::InstructionError;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod token_program;
 | 
					solana_sdk::process_instruction_entrypoint!(process_instruction);
 | 
				
			||||||
 | 
					 | 
				
			||||||
solana_entrypoint!(entrypoint);
 | 
					 | 
				
			||||||
fn entrypoint(
 | 
					 | 
				
			||||||
    program_id: &Pubkey,
 | 
					 | 
				
			||||||
    info: &mut [KeyedAccount],
 | 
					 | 
				
			||||||
    input: &[u8],
 | 
					 | 
				
			||||||
    _tick_height: u64,
 | 
					 | 
				
			||||||
) -> Result<(), InstructionError> {
 | 
					 | 
				
			||||||
    solana_logger::setup();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    token_program::TokenProgram::process(program_id, info, input).map_err(|e| {
 | 
					 | 
				
			||||||
        error!("error: {:?}", e);
 | 
					 | 
				
			||||||
        InstructionError::CustomError(serialize(&e).unwrap())
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,8 +13,13 @@ bincode = "1.1.2"
 | 
				
			|||||||
log = "0.4.2"
 | 
					log = "0.4.2"
 | 
				
			||||||
serde = "1.0.89"
 | 
					serde = "1.0.89"
 | 
				
			||||||
serde_derive = "1.0.89"
 | 
					serde_derive = "1.0.89"
 | 
				
			||||||
 | 
					solana-logger = { path = "../../logger", version = "0.13.0" }
 | 
				
			||||||
 | 
					solana-metrics = { path = "../../metrics", version = "0.13.0" }
 | 
				
			||||||
solana-sdk = { path = "../../sdk", version = "0.13.0" }
 | 
					solana-sdk = { path = "../../sdk", version = "0.13.0" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dev-dependencies]
 | 
				
			||||||
 | 
					solana-runtime = { path = "../../runtime", version = "0.13.0" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[lib]
 | 
					[lib]
 | 
				
			||||||
name = "solana_vote_api"
 | 
					name = "solana_vote_api"
 | 
				
			||||||
crate-type = ["lib"]
 | 
					crate-type = ["lib"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
pub mod vote_instruction;
 | 
					pub mod vote_instruction;
 | 
				
			||||||
 | 
					pub mod vote_processor;
 | 
				
			||||||
pub mod vote_state;
 | 
					pub mod vote_state;
 | 
				
			||||||
pub mod vote_transaction;
 | 
					pub mod vote_transaction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										192
									
								
								programs/vote_api/src/vote_processor.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								programs/vote_api/src/vote_processor.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,192 @@
 | 
				
			|||||||
 | 
					//! Vote program
 | 
				
			||||||
 | 
					//! Receive and processes votes from validators
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::vote_instruction::VoteInstruction;
 | 
				
			||||||
 | 
					use crate::vote_state;
 | 
				
			||||||
 | 
					use bincode::deserialize;
 | 
				
			||||||
 | 
					use log::*;
 | 
				
			||||||
 | 
					use solana_sdk::account::KeyedAccount;
 | 
				
			||||||
 | 
					use solana_sdk::pubkey::Pubkey;
 | 
				
			||||||
 | 
					use solana_sdk::transaction::InstructionError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn process_instruction(
 | 
				
			||||||
 | 
					    _program_id: &Pubkey,
 | 
				
			||||||
 | 
					    keyed_accounts: &mut [KeyedAccount],
 | 
				
			||||||
 | 
					    data: &[u8],
 | 
				
			||||||
 | 
					    _tick_height: u64,
 | 
				
			||||||
 | 
					) -> Result<(), InstructionError> {
 | 
				
			||||||
 | 
					    solana_logger::setup();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    trace!("process_instruction: {:?}", data);
 | 
				
			||||||
 | 
					    trace!("keyed_accounts: {:?}", keyed_accounts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
 | 
				
			||||||
 | 
					        VoteInstruction::InitializeAccount => vote_state::initialize_account(keyed_accounts),
 | 
				
			||||||
 | 
					        VoteInstruction::DelegateStake(delegate_id) => {
 | 
				
			||||||
 | 
					            vote_state::delegate_stake(keyed_accounts, &delegate_id)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        VoteInstruction::AuthorizeVoter(voter_id) => {
 | 
				
			||||||
 | 
					            vote_state::authorize_voter(keyed_accounts, &voter_id)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        VoteInstruction::Vote(vote) => {
 | 
				
			||||||
 | 
					            debug!("{:?} by {}", vote, keyed_accounts[0].signer_key().unwrap());
 | 
				
			||||||
 | 
					            solana_metrics::submit(
 | 
				
			||||||
 | 
					                solana_metrics::influxdb::Point::new("vote-native")
 | 
				
			||||||
 | 
					                    .add_field("count", solana_metrics::influxdb::Value::Integer(1))
 | 
				
			||||||
 | 
					                    .to_owned(),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            vote_state::process_vote(keyed_accounts, vote)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        VoteInstruction::ClearCredits => vote_state::clear_credits(keyed_accounts),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					    use crate::id;
 | 
				
			||||||
 | 
					    use crate::vote_instruction::{Vote, VoteInstruction};
 | 
				
			||||||
 | 
					    use crate::vote_state::VoteState;
 | 
				
			||||||
 | 
					    use crate::vote_transaction::VoteTransaction;
 | 
				
			||||||
 | 
					    use solana_runtime::bank::{Bank, Result};
 | 
				
			||||||
 | 
					    use solana_sdk::genesis_block::GenesisBlock;
 | 
				
			||||||
 | 
					    use solana_sdk::hash::hash;
 | 
				
			||||||
 | 
					    use solana_sdk::pubkey::Pubkey;
 | 
				
			||||||
 | 
					    use solana_sdk::signature::{Keypair, KeypairUtil};
 | 
				
			||||||
 | 
					    use solana_sdk::system_instruction::SystemInstruction;
 | 
				
			||||||
 | 
					    use solana_sdk::transaction::{
 | 
				
			||||||
 | 
					        AccountMeta, Instruction, InstructionError, Transaction, TransactionError,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn create_bank(lamports: u64) -> (Bank, Keypair) {
 | 
				
			||||||
 | 
					        let (genesis_block, mint_keypair) = GenesisBlock::new(lamports);
 | 
				
			||||||
 | 
					        let mut bank = Bank::new(&genesis_block);
 | 
				
			||||||
 | 
					        bank.add_instruction_processor(id(), process_instruction);
 | 
				
			||||||
 | 
					        (bank, mint_keypair)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct VoteBank<'a> {
 | 
				
			||||||
 | 
					        bank: &'a Bank,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    impl<'a> VoteBank<'a> {
 | 
				
			||||||
 | 
					        fn new(bank: &'a Bank) -> Self {
 | 
				
			||||||
 | 
					            Self { bank }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn create_vote_account(
 | 
				
			||||||
 | 
					            &self,
 | 
				
			||||||
 | 
					            from_keypair: &Keypair,
 | 
				
			||||||
 | 
					            vote_id: &Pubkey,
 | 
				
			||||||
 | 
					            lamports: u64,
 | 
				
			||||||
 | 
					        ) -> Result<()> {
 | 
				
			||||||
 | 
					            let blockhash = self.bank.last_blockhash();
 | 
				
			||||||
 | 
					            let tx = VoteTransaction::new_account(from_keypair, vote_id, blockhash, lamports, 0);
 | 
				
			||||||
 | 
					            self.bank.process_transaction(&tx)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn create_vote_account_with_delegate(
 | 
				
			||||||
 | 
					            &self,
 | 
				
			||||||
 | 
					            from_keypair: &Keypair,
 | 
				
			||||||
 | 
					            vote_keypair: &Keypair,
 | 
				
			||||||
 | 
					            delegate_id: &Pubkey,
 | 
				
			||||||
 | 
					            lamports: u64,
 | 
				
			||||||
 | 
					        ) -> Result<()> {
 | 
				
			||||||
 | 
					            let blockhash = self.bank.last_blockhash();
 | 
				
			||||||
 | 
					            let tx = VoteTransaction::new_account_with_delegate(
 | 
				
			||||||
 | 
					                from_keypair,
 | 
				
			||||||
 | 
					                vote_keypair,
 | 
				
			||||||
 | 
					                delegate_id,
 | 
				
			||||||
 | 
					                blockhash,
 | 
				
			||||||
 | 
					                lamports,
 | 
				
			||||||
 | 
					                0,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            self.bank.process_transaction(&tx)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn submit_vote(
 | 
				
			||||||
 | 
					            &self,
 | 
				
			||||||
 | 
					            staking_account: &Pubkey,
 | 
				
			||||||
 | 
					            vote_keypair: &Keypair,
 | 
				
			||||||
 | 
					            tick_height: u64,
 | 
				
			||||||
 | 
					        ) -> Result<VoteState> {
 | 
				
			||||||
 | 
					            let blockhash = self.bank.last_blockhash();
 | 
				
			||||||
 | 
					            let tx =
 | 
				
			||||||
 | 
					                VoteTransaction::new_vote(staking_account, vote_keypair, tick_height, blockhash, 0);
 | 
				
			||||||
 | 
					            self.bank.process_transaction(&tx)?;
 | 
				
			||||||
 | 
					            self.bank.register_tick(&hash(blockhash.as_ref()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let vote_account = self.bank.get_account(&vote_keypair.pubkey()).unwrap();
 | 
				
			||||||
 | 
					            Ok(VoteState::deserialize(&vote_account.data).unwrap())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_vote_bank_basic() {
 | 
				
			||||||
 | 
					        let (bank, from_keypair) = create_bank(10_000);
 | 
				
			||||||
 | 
					        let vote_bank = VoteBank::new(&bank);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let vote_keypair = Keypair::new();
 | 
				
			||||||
 | 
					        let vote_id = vote_keypair.pubkey();
 | 
				
			||||||
 | 
					        vote_bank
 | 
				
			||||||
 | 
					            .create_vote_account(&from_keypair, &vote_id, 100)
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let vote_state = vote_bank.submit_vote(&vote_id, &vote_keypair, 0).unwrap();
 | 
				
			||||||
 | 
					        assert_eq!(vote_state.votes.len(), 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_vote_bank_delegate() {
 | 
				
			||||||
 | 
					        let (bank, from_keypair) = create_bank(10_000);
 | 
				
			||||||
 | 
					        let vote_bank = VoteBank::new(&bank);
 | 
				
			||||||
 | 
					        let vote_keypair = Keypair::new();
 | 
				
			||||||
 | 
					        let delegate_keypair = Keypair::new();
 | 
				
			||||||
 | 
					        let delegate_id = delegate_keypair.pubkey();
 | 
				
			||||||
 | 
					        vote_bank
 | 
				
			||||||
 | 
					            .create_vote_account_with_delegate(&from_keypair, &vote_keypair, &delegate_id, 100)
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn test_vote_via_bank_with_no_signature() {
 | 
				
			||||||
 | 
					        let (bank, mallory_keypair) = create_bank(10_000);
 | 
				
			||||||
 | 
					        let vote_bank = VoteBank::new(&bank);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let vote_keypair = Keypair::new();
 | 
				
			||||||
 | 
					        let vote_id = vote_keypair.pubkey();
 | 
				
			||||||
 | 
					        vote_bank
 | 
				
			||||||
 | 
					            .create_vote_account(&mallory_keypair, &vote_id, 100)
 | 
				
			||||||
 | 
					            .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mallory_id = mallory_keypair.pubkey();
 | 
				
			||||||
 | 
					        let blockhash = bank.last_blockhash();
 | 
				
			||||||
 | 
					        let vote_ix = Instruction::new(
 | 
				
			||||||
 | 
					            id(),
 | 
				
			||||||
 | 
					            &VoteInstruction::Vote(Vote::new(0)),
 | 
				
			||||||
 | 
					            vec![AccountMeta::new(vote_id, false)], // <--- attack!! No signer required.
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Sneak in an instruction so that the transaction is signed but
 | 
				
			||||||
 | 
					        // the 0th account in the second instruction is not! The program
 | 
				
			||||||
 | 
					        // needs to check that it's signed.
 | 
				
			||||||
 | 
					        let move_ix = SystemInstruction::new_move(&mallory_id, &vote_id, 1);
 | 
				
			||||||
 | 
					        let mut tx = Transaction::new(vec![move_ix, vote_ix]);
 | 
				
			||||||
 | 
					        tx.sign(&[&mallory_keypair], blockhash);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let result = bank.process_transaction(&tx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // And ensure there's no vote.
 | 
				
			||||||
 | 
					        let vote_account = bank.get_account(&vote_id).unwrap();
 | 
				
			||||||
 | 
					        let vote_state = VoteState::deserialize(&vote_account.data).unwrap();
 | 
				
			||||||
 | 
					        assert_eq!(vote_state.votes.len(), 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            result,
 | 
				
			||||||
 | 
					            Err(TransactionError::InstructionError(
 | 
				
			||||||
 | 
					                1,
 | 
				
			||||||
 | 
					                InstructionError::InvalidArgument
 | 
				
			||||||
 | 
					            ))
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -9,16 +9,11 @@ homepage = "https://solana.com/"
 | 
				
			|||||||
edition = "2018"
 | 
					edition = "2018"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
bincode = "1.1.2"
 | 
					 | 
				
			||||||
log = "0.4.2"
 | 
					log = "0.4.2"
 | 
				
			||||||
solana-logger = { path = "../../logger", version = "0.13.0" }
 | 
					solana-logger = { path = "../../logger", version = "0.13.0" }
 | 
				
			||||||
solana-metrics = { path = "../../metrics", version = "0.13.0" }
 | 
					 | 
				
			||||||
solana-sdk = { path = "../../sdk", version = "0.13.0" }
 | 
					solana-sdk = { path = "../../sdk", version = "0.13.0" }
 | 
				
			||||||
solana-vote-api = { path = "../vote_api", version = "0.13.0" }
 | 
					solana-vote-api = { path = "../vote_api", version = "0.13.0" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dev-dependencies]
 | 
					 | 
				
			||||||
solana-runtime = { path = "../../runtime", version = "0.13.0" }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[lib]
 | 
					[lib]
 | 
				
			||||||
name = "solana_vote_program"
 | 
					name = "solana_vote_program"
 | 
				
			||||||
crate-type = ["cdylib"]
 | 
					crate-type = ["cdylib"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,44 +1,3 @@
 | 
				
			|||||||
//! Vote program
 | 
					use solana_vote_api::vote_processor::process_instruction;
 | 
				
			||||||
//! Receive and processes votes from validators
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use bincode::deserialize;
 | 
					solana_sdk::process_instruction_entrypoint!(process_instruction);
 | 
				
			||||||
use log::*;
 | 
					 | 
				
			||||||
use solana_sdk::account::KeyedAccount;
 | 
					 | 
				
			||||||
use solana_sdk::pubkey::Pubkey;
 | 
					 | 
				
			||||||
use solana_sdk::solana_entrypoint;
 | 
					 | 
				
			||||||
use solana_sdk::transaction::InstructionError;
 | 
					 | 
				
			||||||
use solana_vote_api::vote_instruction::VoteInstruction;
 | 
					 | 
				
			||||||
use solana_vote_api::vote_state;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
solana_entrypoint!(entrypoint);
 | 
					 | 
				
			||||||
fn entrypoint(
 | 
					 | 
				
			||||||
    _program_id: &Pubkey,
 | 
					 | 
				
			||||||
    keyed_accounts: &mut [KeyedAccount],
 | 
					 | 
				
			||||||
    data: &[u8],
 | 
					 | 
				
			||||||
    _tick_height: u64,
 | 
					 | 
				
			||||||
) -> Result<(), InstructionError> {
 | 
					 | 
				
			||||||
    solana_logger::setup();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    trace!("process_instruction: {:?}", data);
 | 
					 | 
				
			||||||
    trace!("keyed_accounts: {:?}", keyed_accounts);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
 | 
					 | 
				
			||||||
        VoteInstruction::InitializeAccount => vote_state::initialize_account(keyed_accounts),
 | 
					 | 
				
			||||||
        VoteInstruction::DelegateStake(delegate_id) => {
 | 
					 | 
				
			||||||
            vote_state::delegate_stake(keyed_accounts, &delegate_id)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        VoteInstruction::AuthorizeVoter(voter_id) => {
 | 
					 | 
				
			||||||
            vote_state::authorize_voter(keyed_accounts, &voter_id)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        VoteInstruction::Vote(vote) => {
 | 
					 | 
				
			||||||
            debug!("{:?} by {}", vote, keyed_accounts[0].signer_key().unwrap());
 | 
					 | 
				
			||||||
            solana_metrics::submit(
 | 
					 | 
				
			||||||
                solana_metrics::influxdb::Point::new("vote-native")
 | 
					 | 
				
			||||||
                    .add_field("count", solana_metrics::influxdb::Value::Integer(1))
 | 
					 | 
				
			||||||
                    .to_owned(),
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            vote_state::process_vote(keyed_accounts, vote)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        VoteInstruction::ClearCredits => vote_state::clear_credits(keyed_accounts),
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,141 +0,0 @@
 | 
				
			|||||||
use solana_runtime::bank::{Bank, Result};
 | 
					 | 
				
			||||||
use solana_sdk::genesis_block::GenesisBlock;
 | 
					 | 
				
			||||||
use solana_sdk::hash::hash;
 | 
					 | 
				
			||||||
use solana_sdk::pubkey::Pubkey;
 | 
					 | 
				
			||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
 | 
					 | 
				
			||||||
use solana_sdk::system_instruction::SystemInstruction;
 | 
					 | 
				
			||||||
use solana_sdk::transaction::{
 | 
					 | 
				
			||||||
    AccountMeta, Instruction, InstructionError, Transaction, TransactionError,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use solana_vote_api::vote_instruction::{Vote, VoteInstruction};
 | 
					 | 
				
			||||||
use solana_vote_api::vote_state::VoteState;
 | 
					 | 
				
			||||||
use solana_vote_api::vote_transaction::VoteTransaction;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct VoteBank<'a> {
 | 
					 | 
				
			||||||
    bank: &'a Bank,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl<'a> VoteBank<'a> {
 | 
					 | 
				
			||||||
    fn new(bank: &'a Bank) -> Self {
 | 
					 | 
				
			||||||
        bank.add_native_program("solana_vote_program", &solana_vote_api::id());
 | 
					 | 
				
			||||||
        Self { bank }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn create_vote_account(
 | 
					 | 
				
			||||||
        &self,
 | 
					 | 
				
			||||||
        from_keypair: &Keypair,
 | 
					 | 
				
			||||||
        vote_id: &Pubkey,
 | 
					 | 
				
			||||||
        lamports: u64,
 | 
					 | 
				
			||||||
    ) -> Result<()> {
 | 
					 | 
				
			||||||
        let blockhash = self.bank.last_blockhash();
 | 
					 | 
				
			||||||
        let tx = VoteTransaction::new_account(from_keypair, vote_id, blockhash, lamports, 0);
 | 
					 | 
				
			||||||
        self.bank.process_transaction(&tx)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn create_vote_account_with_delegate(
 | 
					 | 
				
			||||||
        &self,
 | 
					 | 
				
			||||||
        from_keypair: &Keypair,
 | 
					 | 
				
			||||||
        vote_keypair: &Keypair,
 | 
					 | 
				
			||||||
        delegate_id: &Pubkey,
 | 
					 | 
				
			||||||
        lamports: u64,
 | 
					 | 
				
			||||||
    ) -> Result<()> {
 | 
					 | 
				
			||||||
        let blockhash = self.bank.last_blockhash();
 | 
					 | 
				
			||||||
        let tx = VoteTransaction::new_account_with_delegate(
 | 
					 | 
				
			||||||
            from_keypair,
 | 
					 | 
				
			||||||
            vote_keypair,
 | 
					 | 
				
			||||||
            delegate_id,
 | 
					 | 
				
			||||||
            blockhash,
 | 
					 | 
				
			||||||
            lamports,
 | 
					 | 
				
			||||||
            0,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        self.bank.process_transaction(&tx)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn submit_vote(
 | 
					 | 
				
			||||||
        &self,
 | 
					 | 
				
			||||||
        staking_account: &Pubkey,
 | 
					 | 
				
			||||||
        vote_keypair: &Keypair,
 | 
					 | 
				
			||||||
        tick_height: u64,
 | 
					 | 
				
			||||||
    ) -> Result<VoteState> {
 | 
					 | 
				
			||||||
        let blockhash = self.bank.last_blockhash();
 | 
					 | 
				
			||||||
        let tx =
 | 
					 | 
				
			||||||
            VoteTransaction::new_vote(staking_account, vote_keypair, tick_height, blockhash, 0);
 | 
					 | 
				
			||||||
        self.bank.process_transaction(&tx)?;
 | 
					 | 
				
			||||||
        self.bank.register_tick(&hash(blockhash.as_ref()));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let vote_account = self.bank.get_account(&vote_keypair.pubkey()).unwrap();
 | 
					 | 
				
			||||||
        Ok(VoteState::deserialize(&vote_account.data).unwrap())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[test]
 | 
					 | 
				
			||||||
fn test_vote_bank_basic() {
 | 
					 | 
				
			||||||
    let (genesis_block, from_keypair) = GenesisBlock::new(10_000);
 | 
					 | 
				
			||||||
    let bank = Bank::new(&genesis_block);
 | 
					 | 
				
			||||||
    let vote_bank = VoteBank::new(&bank);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let vote_keypair = Keypair::new();
 | 
					 | 
				
			||||||
    let vote_id = vote_keypair.pubkey();
 | 
					 | 
				
			||||||
    vote_bank
 | 
					 | 
				
			||||||
        .create_vote_account(&from_keypair, &vote_id, 100)
 | 
					 | 
				
			||||||
        .unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let vote_state = vote_bank.submit_vote(&vote_id, &vote_keypair, 0).unwrap();
 | 
					 | 
				
			||||||
    assert_eq!(vote_state.votes.len(), 1);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[test]
 | 
					 | 
				
			||||||
fn test_vote_bank_delegate() {
 | 
					 | 
				
			||||||
    let (genesis_block, from_keypair) = GenesisBlock::new(10_000);
 | 
					 | 
				
			||||||
    let bank = Bank::new(&genesis_block);
 | 
					 | 
				
			||||||
    let vote_bank = VoteBank::new(&bank);
 | 
					 | 
				
			||||||
    let vote_keypair = Keypair::new();
 | 
					 | 
				
			||||||
    let delegate_keypair = Keypair::new();
 | 
					 | 
				
			||||||
    let delegate_id = delegate_keypair.pubkey();
 | 
					 | 
				
			||||||
    vote_bank
 | 
					 | 
				
			||||||
        .create_vote_account_with_delegate(&from_keypair, &vote_keypair, &delegate_id, 100)
 | 
					 | 
				
			||||||
        .unwrap();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[test]
 | 
					 | 
				
			||||||
fn test_vote_via_bank_with_no_signature() {
 | 
					 | 
				
			||||||
    let (genesis_block, mallory_keypair) = GenesisBlock::new(10_000);
 | 
					 | 
				
			||||||
    let bank = Bank::new(&genesis_block);
 | 
					 | 
				
			||||||
    let vote_bank = VoteBank::new(&bank);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let vote_keypair = Keypair::new();
 | 
					 | 
				
			||||||
    let vote_id = vote_keypair.pubkey();
 | 
					 | 
				
			||||||
    vote_bank
 | 
					 | 
				
			||||||
        .create_vote_account(&mallory_keypair, &vote_id, 100)
 | 
					 | 
				
			||||||
        .unwrap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let mallory_id = mallory_keypair.pubkey();
 | 
					 | 
				
			||||||
    let blockhash = bank.last_blockhash();
 | 
					 | 
				
			||||||
    let vote_ix = Instruction::new(
 | 
					 | 
				
			||||||
        solana_vote_api::id(),
 | 
					 | 
				
			||||||
        &VoteInstruction::Vote(Vote::new(0)),
 | 
					 | 
				
			||||||
        vec![AccountMeta::new(vote_id, false)], // <--- attack!! No signer required.
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Sneak in an instruction so that the transaction is signed but
 | 
					 | 
				
			||||||
    // the 0th account in the second instruction is not! The program
 | 
					 | 
				
			||||||
    // needs to check that it's signed.
 | 
					 | 
				
			||||||
    let move_ix = SystemInstruction::new_move(&mallory_id, &vote_id, 1);
 | 
					 | 
				
			||||||
    let mut tx = Transaction::new(vec![move_ix, vote_ix]);
 | 
					 | 
				
			||||||
    tx.sign(&[&mallory_keypair], blockhash);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let result = bank.process_transaction(&tx);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // And ensure there's no vote.
 | 
					 | 
				
			||||||
    let vote_account = bank.get_account(&vote_id).unwrap();
 | 
					 | 
				
			||||||
    let vote_state = VoteState::deserialize(&vote_account.data).unwrap();
 | 
					 | 
				
			||||||
    assert_eq!(vote_state.votes.len(), 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    assert_eq!(
 | 
					 | 
				
			||||||
        result,
 | 
					 | 
				
			||||||
        Err(TransactionError::InstructionError(
 | 
					 | 
				
			||||||
            1,
 | 
					 | 
				
			||||||
            InstructionError::InvalidArgument
 | 
					 | 
				
			||||||
        ))
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user