armaniferrante 4 年之前
父節點
當前提交
7abb781e1b
共有 2 個文件被更改,包括 131 次插入25 次删除
  1. 128 24
      lang/src/fuzzing.rs
  2. 3 1
      lang/src/lib.rs

+ 128 - 24
lang/src/fuzzing.rs

@@ -1,44 +1,47 @@
 //! The fuzz modules provides utilities to facilitate fuzzing anchor programs.
 
 use bumpalo::Bump;
+use num_derive::ToPrimitive;
 use safe_transmute::to_bytes::transmute_to_bytes;
 use solana_program::account_info::next_account_info;
 use solana_program::account_info::AccountInfo;
 use solana_program::bpf_loader;
 use solana_program::clock::Epoch;
 use solana_program::entrypoint::ProgramResult;
-use solana_program::instruction::Instruction;
+use solana_program::instruction::{AccountMeta, Instruction};
 use solana_program::program_error::ProgramError;
 use solana_program::program_pack::Pack;
 use solana_program::pubkey::Pubkey;
 use solana_program::rent::Rent;
-use solana_program::system_instruction::SystemError;
-use solana_program::system_instruction::SystemInstruction;
+use solana_program::system_instruction::{SystemError, SystemInstruction};
 use solana_program::system_program;
 use solana_program::sysvar::{self, Sysvar};
-use spl_token::state::Account as TokenAccount;
-use spl_token::state::Mint;
+use spl_token::state::{Account as TokenAccount, Mint};
+use std::cell::RefCell;
 use std::collections::HashMap;
+use std::error::Error;
 use std::fmt::Debug;
 use std::mem::size_of;
-use std::sync::{Arc, Mutex, MutexGuard};
+use std::rc::Rc;
+use std::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard};
 use thiserror::Error;
 
 lazy_static::lazy_static! {
-    static ref ENV: Arc<Mutex<Environment>> = Arc::new(Mutex::new(Environment::new()));
+    static ref ENV: Arc<Environment> = Arc::new(Environment::new());
 }
 
 // Global host environment.
-pub fn env() -> MutexGuard<'static, Environment> {
-    ENV.lock().unwrap()
+pub fn env<'info>() -> Arc<Environment> {
+    ENV.clone()
 }
 
 // The host execution environment.
+#[derive(Debug)]
 pub struct Environment {
     // All registered programs that can be invoked.
     programs: HashMap<Pubkey, Box<dyn Program>>,
     // The currently executing program.
-    current_program: Option<Pubkey>,
+    current_program: RefCell<Option<Pubkey>>,
     // Account storage.
     accounts: AccountStore,
 }
@@ -47,7 +50,7 @@ impl Environment {
     pub fn new() -> Environment {
         let mut env = Environment {
             programs: HashMap::new(),
-            current_program: None,
+            current_program: RefCell::new(None),
             accounts: AccountStore::new(),
         };
         env.register(Box::new(SystemProgram));
@@ -55,8 +58,8 @@ impl Environment {
         env
     }
 
-    pub fn accounts_mut(&mut self) -> &mut AccountStore {
-        &mut self.accounts
+    pub fn accounts(&self) -> &AccountStore {
+        &self.accounts
     }
 
     // Registers the program on the environment so that it can be invoked via
@@ -66,15 +69,15 @@ impl Environment {
     }
 
     // Performs a cross program invocation.
-    pub fn invoke<'info>(
-        &mut self,
+    pub fn invoke(
+        &self,
         ix: &Instruction,
-        accounts: &[AccountInfo<'info>],
+        accounts: &[AccountInfo],
         seeds: &[&[&[u8]]],
     ) -> ProgramResult {
         // If seeds were given, then calculate the expected PDA.
         let pda = {
-            match self.current_program {
+            match *self.current_program.borrow() {
                 None => None,
                 Some(current_program) => match seeds.len() > 0 {
                     false => None,
@@ -86,7 +89,7 @@ impl Environment {
         };
 
         // Set the current program.
-        self.current_program = Some(ix.program_id);
+        self.current_program.replace(Some(ix.program_id));
 
         // Invoke the current program.
         let program = self.programs.get(&ix.program_id).unwrap();
@@ -112,6 +115,13 @@ impl Environment {
     }
 }
 
+// Not acutally Sync. Implemented so that we can use the Environment as a
+// lazy static without using locks (which is inconvenient and can cause
+// deadlock). The Environment, as presently constructed, should never be
+// used across threads.
+unsafe impl std::marker::Sync for Environment {}
+
+#[derive(Debug)]
 pub struct AccountStore {
     // Storage bytes.
     storage: Bump,
@@ -123,6 +133,11 @@ impl AccountStore {
             storage: Bump::new(),
         }
     }
+
+    pub fn storage(&self) -> &Bump {
+        &self.storage
+    }
+
     pub fn new_sol_account(&self, lamports: u64) -> AccountInfo {
         AccountInfo::new(
             random_pubkey(&self.storage),
@@ -249,9 +264,9 @@ impl Program for SplToken {
 struct SystemProgram;
 
 impl SystemProgram {
-    fn create_account(
+    fn create_account<'info>(
         &self,
-        accounts: &[AccountInfo],
+        accounts: &[AccountInfo<'info>],
         lamports: u64,
         space: u64,
         owner: Pubkey,
@@ -259,14 +274,24 @@ impl SystemProgram {
         let acc_infos = &mut accounts.into_iter();
 
         let from = next_account_info(acc_infos)?;
-        let created = next_account_info(acc_infos)?;
+        let mut created = next_account_info(acc_infos)?;
 
         if **created.lamports.borrow() > 0 {
-            return Err(ProgramError::Custom(
-                SystemError::AccountAlreadyInUse.to_u32(),
-            ));
+            panic!("{}", SystemError::AccountAlreadyInUse);
         }
 
+        let environment = env();
+        let data = environment
+            .accounts
+            .storage()
+            .alloc_slice_fill_copy(space as usize, 0u8);
+        // Safe because the lifetime is extended to match the other accounts
+        // also allocated in the bump allocator.
+        let data = unsafe { extend_lifetime(data) };
+
+        let created = unsafe { into_mut(created) };
+        created.data.replace(data);
+
         **from.lamports.borrow_mut() -= lamports;
         **created.lamports.borrow_mut() += lamports;
 
@@ -413,3 +438,82 @@ impl Program for SystemProgram {
         system_program::ID
     }
 }
+
+unsafe fn extend_lifetime<'a, 'info>(data: &'a mut [u8]) -> &'info mut [u8] {
+    std::mem::transmute::<&'a mut [u8], &'info mut [u8]>(data)
+}
+
+unsafe fn into_mut<'a, 'info>(acc: &'a AccountInfo<'info>) -> &'a mut AccountInfo<'info> {
+    std::mem::transmute::<&AccountInfo, &mut AccountInfo>(acc)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn system_program_creates_account() {
+        let mut environment = Environment::new();
+
+        let owner = Pubkey::new_unique();
+        let from = environment.accounts.new_sol_account(1000);
+        let created = environment.accounts.new_sol_account(0);
+
+        let ix = {
+            let data = bincode::serialize(&SystemInstruction::CreateAccount {
+                lamports: 999,
+                space: 1234,
+                owner,
+            })
+            .unwrap();
+
+            Instruction {
+                program_id: system_program::ID,
+                data,
+                accounts: vec![
+                    AccountMeta::new(*from.key, true),
+                    AccountMeta::new(*created.key, true),
+                ],
+            }
+        };
+
+        let accounts = &[from.clone(), created.clone()];
+
+        assert_eq!(*created.data.borrow_mut(), &[]);
+        assert_eq!(**from.lamports.borrow(), 1000);
+        environment.invoke(&ix, accounts, &[]).unwrap();
+        assert_eq!(*created.data.borrow_mut(), &[0u8; 1234]);
+        assert_eq!(**created.lamports.borrow(), 999);
+        assert_eq!(**from.lamports.borrow(), 1);
+    }
+
+    #[test]
+    fn system_program_transfer() {
+        let mut environment = Environment::new();
+
+        let owner = Pubkey::new_unique();
+        let from = environment.accounts.new_sol_account(1000);
+        let to = environment.accounts.new_sol_account(0);
+
+        let ix = {
+            let data = bincode::serialize(&SystemInstruction::Transfer { lamports: 999 }).unwrap();
+
+            Instruction {
+                program_id: system_program::ID,
+                data,
+                accounts: vec![
+                    AccountMeta::new(*from.key, true),
+                    AccountMeta::new(*to.key, true),
+                ],
+            }
+        };
+
+        let accounts = &[from.clone(), to.clone()];
+
+        assert_eq!(**from.lamports.borrow(), 1000);
+        assert_eq!(**to.lamports.borrow(), 0);
+        environment.invoke(&ix, accounts, &[]).unwrap();
+        assert_eq!(**from.lamports.borrow(), 1);
+        assert_eq!(**to.lamports.borrow(), 999);
+    }
+}

+ 3 - 1
lang/src/lib.rs

@@ -21,6 +21,8 @@
 //!
 //! Presented here are the Rust primitives for building on Solana.
 
+#![cfg_attr(any(fuzzing, feature = "fuzzing"), allow(mutable_transmutes))]
+
 extern crate self as anchor_lang;
 
 use solana_program::account_info::AccountInfo;
@@ -36,7 +38,7 @@ mod context;
 mod cpi_account;
 mod ctor;
 mod error;
-#[cfg(fuzzing)]
+#[cfg(any(fuzzing, feature = "fuzzing"))]
 pub mod fuzzing;
 pub mod idl;
 mod program_account;