armaniferrante 4 anni fa
parent
commit
f37119aef5

+ 14 - 7
Cargo.lock

@@ -167,6 +167,19 @@ dependencies = [
  "syn 1.0.57",
 ]
 
+[[package]]
+name = "anchor-fuzzing"
+version = "0.3.0"
+dependencies = [
+ "bincode",
+ "bumpalo",
+ "lazy_static",
+ "rand 0.7.3",
+ "safe-transmute",
+ "solana-program",
+ "spl-token 3.1.0",
+]
+
 [[package]]
 name = "anchor-lang"
 version = "0.3.0"
@@ -179,16 +192,10 @@ dependencies = [
  "anchor-attribute-program",
  "anchor-attribute-state",
  "anchor-derive-accounts",
+ "anchor-fuzzing",
  "base64 0.13.0",
- "bincode",
  "borsh",
- "bumpalo",
- "lazy_static",
- "num-derive",
- "rand 0.7.3",
- "safe-transmute",
  "solana-program",
- "spl-token 3.1.0",
  "thiserror",
 ]
 

+ 1 - 0
Cargo.toml

@@ -5,6 +5,7 @@ members = [
     "lang",
     "lang/attribute/*",
     "lang/derive/*",
+    "lang/fuzzing",
     "lang/syn",
     "spl",
 ]

+ 3 - 9
lang/Cargo.toml

@@ -9,7 +9,7 @@ description = "Solana Sealevel eDSL"
 
 [features]
 derive = []
-fuzzing = ["bincode", "lazy_static", "spl-token", "bumpalo", "rand", "safe-transmute", "num-derive"]
+fuzzing = ["anchor-fuzzing"]
 default = []
 
 [dependencies]
@@ -26,11 +26,5 @@ solana-program = "=1.5.15"
 thiserror = "1.0.20"
 base64 = "0.13.0"
 
-# Fuzz deps.
-bincode = { version = "1.3.1", optional = true }
-bumpalo = { version = "3.4.0", features = ["collections", "boxed"], optional = true }
-lazy_static = { version = "1.4.0", optional = true }
-rand = { version = "0.7.3", optional = true }
-safe-transmute = { version = "0.11.0", optional = true }
-spl-token = { version = "3.0.1", features = ["no-entrypoint"], optional = true }
-num-derive = { version = "0.3.3", optional = true }
+# Fuzzing.
+anchor-fuzzing = { path = "./fuzzing", version = "0.3.0", optional = true }

+ 14 - 0
lang/fuzzing/Cargo.toml

@@ -0,0 +1,14 @@
+[package]
+name = "anchor-fuzzing"
+version = "0.3.0"
+authors = ["armaniferrante <armaniferrante@gmail.com>"]
+edition = "2018"
+
+[dependencies]
+bincode = "1.3.1"
+bumpalo = { version = "3.4.0", features = ["collections", "boxed"] }
+lazy_static = "1.4.0"
+rand = "0.7.3"
+safe-transmute = "0.11.0"
+spl-token = { version = "3.0.1", features = ["no-entrypoint"] }
+solana-program = "=1.5.15"

+ 242 - 0
lang/fuzzing/src/lib.rs

@@ -0,0 +1,242 @@
+//! The fuzz module provides utilities to facilitate fuzzing anchor programs.
+
+#![allow(mutable_transmutes)]
+
+use crate::spl_token_program::SplTokenProgram;
+use crate::system_program::SystemProgram;
+use bumpalo::Bump;
+use safe_transmute::to_bytes::transmute_to_bytes;
+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::program_pack::Pack;
+use solana_program::pubkey::Pubkey;
+use solana_program::rent::Rent;
+use solana_program::sysvar::{self, Sysvar};
+use spl_token::state::{Account as TokenAccount, Mint};
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::fmt::Debug;
+use std::mem::size_of;
+
+mod spl_token_program;
+mod system_program;
+
+lazy_static::lazy_static! {
+    static ref ENV: Host = Host::new();
+}
+
+// Global host environment.
+pub fn env() -> &'static Host {
+    &ENV
+}
+
+// The host execution environment.
+#[derive(Debug)]
+pub struct Host {
+    // All registered programs that can be invoked.
+    programs: HashMap<Pubkey, Box<dyn Program>>,
+    // The currently executing program.
+    current_program: RefCell<Option<Pubkey>>,
+    // Account storage.
+    accounts: AccountStore,
+}
+
+impl Host {
+    pub fn new() -> Host {
+        let mut env = Host {
+            programs: HashMap::new(),
+            current_program: RefCell::new(None),
+            accounts: AccountStore::new(),
+        };
+        env.register(Box::new(SystemProgram));
+        env.register(Box::new(SplTokenProgram));
+        env
+    }
+
+    pub fn accounts(&self) -> &AccountStore {
+        &self.accounts
+    }
+
+    // Registers the program on the environment so that it can be invoked via
+    // CPI.
+    pub fn register(&mut self, program: Box<dyn Program>) {
+        self.programs.insert(program.id(), program);
+    }
+
+    // Performs a cross program invocation.
+    pub fn invoke(
+        &self,
+        ix: &Instruction,
+        accounts: &[AccountInfo],
+        seeds: &[&[&[u8]]],
+    ) -> ProgramResult {
+        // If seeds were given, then calculate the expected PDA.
+        let pda = {
+            match *self.current_program.borrow() {
+                None => None,
+                Some(current_program) => match seeds.len() > 0 {
+                    false => None,
+                    true => {
+                        Some(Pubkey::create_program_address(seeds[0], &current_program).unwrap())
+                    }
+                },
+            }
+        };
+
+        // Set the current program.
+        self.current_program.replace(Some(ix.program_id));
+
+        // Invoke the current program.
+        let program = self.programs.get(&ix.program_id).unwrap();
+        let account_infos: Vec<AccountInfo> = ix
+            .accounts
+            .iter()
+            .map(|meta| {
+                let mut acc_info = accounts
+                    .iter()
+                    .find(|info| *info.key == meta.pubkey)
+                    .unwrap()
+                    .clone();
+                // If a PDA was given, mark it as signer.
+                if let Some(pda) = pda {
+                    if acc_info.key == &pda {
+                        acc_info.is_signer = true;
+                    }
+                }
+                acc_info
+            })
+            .collect();
+        program.entry(&ix.program_id, &account_infos, &ix.data)
+    }
+}
+
+// Not acutally Sync. Implemented so that we can use the Host as a
+// lazy static without using locks (which is inconvenient and can cause
+// deadlock). The Host, as presently constructed, should never be
+// used across threads.
+unsafe impl std::marker::Sync for Host {}
+
+#[derive(Debug)]
+pub struct AccountStore {
+    // Storage bytes.
+    storage: Bump,
+}
+
+impl AccountStore {
+    pub fn new() -> Self {
+        Self {
+            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),
+            true,
+            false,
+            self.storage.alloc(lamports),
+            &mut [],
+            // Allocate on the bump allocator, so that the owner can be safely
+            // mutated by the SystemProgram's `create_account` instruction.
+            self.storage.alloc(system_program::ID),
+            false,
+            Epoch::default(),
+        )
+    }
+
+    pub fn new_token_mint(&self) -> AccountInfo {
+        let rent = Rent::default();
+        let data = self.storage.alloc_slice_fill_copy(Mint::LEN, 0u8);
+        let mut mint = Mint::default();
+        mint.is_initialized = true;
+        Mint::pack(mint, data).unwrap();
+        AccountInfo::new(
+            random_pubkey(&self.storage),
+            false,
+            true,
+            self.storage.alloc(rent.minimum_balance(data.len())),
+            data,
+            &spl_token::ID,
+            false,
+            Epoch::default(),
+        )
+    }
+
+    pub fn new_token_account<'a, 'b>(
+        &self,
+        mint_pubkey: &'a Pubkey,
+        owner_pubkey: &'b Pubkey,
+        balance: u64,
+    ) -> AccountInfo {
+        let rent = Rent::default();
+        let data = self.storage.alloc_slice_fill_copy(TokenAccount::LEN, 0u8);
+        let mut account = TokenAccount::default();
+        account.state = spl_token::state::AccountState::Initialized;
+        account.mint = *mint_pubkey;
+        account.owner = *owner_pubkey;
+        account.amount = balance;
+        TokenAccount::pack(account, data).unwrap();
+        AccountInfo::new(
+            random_pubkey(&self.storage),
+            false,
+            true,
+            self.storage.alloc(rent.minimum_balance(data.len())),
+            data,
+            &spl_token::ID,
+            false,
+            Epoch::default(),
+        )
+    }
+
+    pub fn new_program(&self) -> AccountInfo {
+        AccountInfo::new(
+            random_pubkey(&self.storage),
+            false,
+            false,
+            self.storage.alloc(0),
+            &mut [],
+            &bpf_loader::ID,
+            true,
+            Epoch::default(),
+        )
+    }
+
+    fn new_rent_sysvar_account(&self) -> AccountInfo {
+        let lamports = 100000;
+        let data = self.storage.alloc_slice_fill_copy(size_of::<Rent>(), 0u8);
+        let mut account_info = AccountInfo::new(
+            &sysvar::rent::ID,
+            false,
+            false,
+            self.storage.alloc(lamports),
+            data,
+            &sysvar::ID,
+            false,
+            Epoch::default(),
+        );
+        let rent = Rent::default();
+        rent.to_account_info(&mut account_info).unwrap();
+        account_info
+    }
+}
+
+fn random_pubkey(bump: &Bump) -> &Pubkey {
+    bump.alloc(Pubkey::new(transmute_to_bytes(&rand::random::<[u64; 4]>())))
+}
+
+// Program that can be executed in the environment.
+pub trait Program: Send + Sync + Debug {
+    // The program's ID.
+    fn id(&self) -> Pubkey;
+
+    // Entrypoint to start executing the program.
+    fn entry(&self, program_id: &Pubkey, accounts: &[AccountInfo], ix_data: &[u8])
+        -> ProgramResult;
+}

+ 22 - 0
lang/fuzzing/src/spl_token_program.rs

@@ -0,0 +1,22 @@
+use crate::Program;
+use solana_program::account_info::AccountInfo;
+use solana_program::entrypoint::ProgramResult;
+use solana_program::pubkey::Pubkey;
+use std::fmt::Debug;
+
+#[derive(Debug)]
+pub struct SplTokenProgram;
+
+impl Program for SplTokenProgram {
+    fn entry(
+        &self,
+        program_id: &Pubkey,
+        accounts: &[AccountInfo],
+        ix_data: &[u8],
+    ) -> ProgramResult {
+        spl_token::processor::Processor::process(program_id, accounts, ix_data)
+    }
+    fn id(&self) -> Pubkey {
+        spl_token::ID
+    }
+}

+ 298 - 0
lang/fuzzing/src/system_program.rs

@@ -0,0 +1,298 @@
+use crate::{env, Host, Program};
+use solana_program::account_info::next_account_info;
+use solana_program::account_info::AccountInfo;
+use solana_program::entrypoint::ProgramResult;
+use solana_program::program_error::ProgramError;
+use solana_program::pubkey::Pubkey;
+use solana_program::system_instruction::{SystemError, SystemInstruction};
+use solana_program::system_program;
+use std::fmt::Debug;
+
+pub use system_program::ID;
+
+// System program emulation to plug into the fuzzing host. Not all instructions
+// are implemented, but PRs are welcome.
+#[derive(Debug)]
+pub struct SystemProgram;
+
+impl Program for SystemProgram {
+    fn entry(
+        &self,
+        _program_id: &Pubkey,
+        accounts: &[AccountInfo],
+        ix_data: &[u8],
+    ) -> ProgramResult {
+        let ix: SystemInstruction =
+            bincode::deserialize(ix_data).map_err(|_| ProgramError::InvalidInstructionData)?;
+
+        match ix {
+            SystemInstruction::CreateAccount {
+                lamports,
+                space,
+                owner,
+            } => self.create_account(accounts, lamports, space, owner),
+            SystemInstruction::Transfer { lamports } => self.transfer(accounts, lamports),
+            SystemInstruction::CreateAccountWithSeed {
+                base,
+                seed,
+                lamports,
+                space,
+                owner,
+            } => self.create_account_with_seed(accounts, base, seed, lamports, space, owner),
+            SystemInstruction::Assign { owner } => self.assign(accounts, owner),
+            SystemInstruction::AdvanceNonceAccount => self.advance_nonce_account(accounts),
+            SystemInstruction::WithdrawNonceAccount(lamports) => {
+                self.withdraw_nonce_account(accounts, lamports)
+            }
+            SystemInstruction::InitializeNonceAccount(entity) => {
+                self.initialize_nonce_account(accounts, entity)
+            }
+            SystemInstruction::AuthorizeNonceAccount(entity) => {
+                self.authorize_nonce_account(accounts, entity)
+            }
+            SystemInstruction::Allocate { space } => self.allocate(accounts, space),
+            SystemInstruction::AllocateWithSeed {
+                base,
+                seed,
+                space,
+                owner,
+            } => self.allocate_with_seed(accounts, base, seed, space, owner),
+            SystemInstruction::AssignWithSeed { base, seed, owner } => {
+                self.assign_with_seed(accounts, base, seed, owner)
+            }
+            SystemInstruction::TransferWithSeed {
+                lamports,
+                from_seed,
+                from_owner,
+            } => self.transfer_with_seed(accounts, lamports, from_seed, from_owner),
+        }
+    }
+
+    fn id(&self) -> Pubkey {
+        system_program::ID
+    }
+}
+
+impl SystemProgram {
+    fn create_account<'info>(
+        &self,
+        accounts: &[AccountInfo<'info>],
+        lamports: u64,
+        space: u64,
+        owner: Pubkey,
+    ) -> ProgramResult {
+        let acc_infos = &mut accounts.into_iter();
+
+        let from = next_account_info(acc_infos)?;
+        let created = next_account_info(acc_infos)?;
+
+        if !from.is_signer {
+            panic!("From not signer");
+        }
+        if !created.is_signer {
+            panic!("Created not signer");
+        }
+        if !from.is_writable {
+            panic!("From not writable");
+        }
+        if !created.is_writable {
+            panic!("Created not writable");
+        }
+
+        if **created.lamports.borrow() > 0 {
+            panic!("{}", SystemError::AccountAlreadyInUse);
+        }
+
+        // Safe because access to this method is single threaded.
+        created.data.replace(
+            env()
+                .accounts
+                .storage()
+                .alloc_slice_fill_copy(space as usize, 0u8),
+        );
+
+        let owner_og_ptr_const = created.owner as *const Pubkey;
+        let owner_og_ptr_mut = owner_og_ptr_const as *mut Pubkey;
+
+        unsafe {
+            std::ptr::write_unaligned(owner_og_ptr_mut, owner);
+        }
+
+        **from.lamports.borrow_mut() -= lamports;
+        **created.lamports.borrow_mut() += lamports;
+
+        Ok(())
+    }
+
+    fn transfer(&self, accounts: &[AccountInfo], lamports: u64) -> ProgramResult {
+        let acc_infos = &mut accounts.into_iter();
+
+        let from = next_account_info(acc_infos)?;
+        let to = next_account_info(acc_infos)?;
+
+        if !from.is_signer {
+            panic!("From not signer");
+        }
+        if !from.is_writable {
+            panic!("From not writable");
+        }
+        if !to.is_writable {
+            panic!("To not writable");
+        }
+
+        **from.lamports.borrow_mut() -= lamports;
+        **to.lamports.borrow_mut() += lamports;
+
+        Ok(())
+    }
+
+    fn create_account_with_seed(
+        &self,
+        accounts: &[AccountInfo],
+        base: Pubkey,
+        seed: String,
+        lamports: u64,
+        space: u64,
+        owner: Pubkey,
+    ) -> ProgramResult {
+        unimplemented!()
+    }
+
+    fn assign(&self, _accounts: &[AccountInfo], owner: Pubkey) -> ProgramResult {
+        unimplemented!()
+    }
+
+    fn advance_nonce_account(&self, _accounts: &[AccountInfo]) -> ProgramResult {
+        unimplemented!()
+    }
+
+    fn withdraw_nonce_account(&self, _accounts: &[AccountInfo], _lamports: u64) -> ProgramResult {
+        unimplemented!()
+    }
+
+    fn initialize_nonce_account(
+        &self,
+        _accounts: &[AccountInfo],
+        _entity: Pubkey,
+    ) -> ProgramResult {
+        unimplemented!()
+    }
+
+    fn authorize_nonce_account(&self, _accounts: &[AccountInfo], _entity: Pubkey) -> ProgramResult {
+        unimplemented!()
+    }
+
+    fn allocate(&self, _accounts: &[AccountInfo], _space: u64) -> ProgramResult {
+        unimplemented!()
+    }
+
+    fn allocate_with_seed(
+        &self,
+        _accounts: &[AccountInfo],
+        _base: Pubkey,
+        _seed: String,
+        _space: u64,
+        _owner: Pubkey,
+    ) -> ProgramResult {
+        unimplemented!()
+    }
+
+    fn assign_with_seed(
+        &self,
+        _accounts: &[AccountInfo],
+        _base: Pubkey,
+        _seed: String,
+        _owner: Pubkey,
+    ) -> ProgramResult {
+        unimplemented!()
+    }
+
+    fn transfer_with_seed(
+        &self,
+        _accounts: &[AccountInfo],
+        _lamports: u64,
+        _from_seed: String,
+        _from_owner: Pubkey,
+    ) -> ProgramResult {
+        unimplemented!()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use solana_program::instruction::{AccountMeta, Instruction};
+
+    #[test]
+    fn system_program_creates_account() {
+        let host = Host::new();
+
+        let owner = Pubkey::new_unique();
+        let mut from = host.accounts.new_sol_account(1000);
+        from.is_writable = true;
+        from.is_signer = true;
+        let mut created = host.accounts.new_sol_account(0);
+        created.is_writable = true;
+        created.is_signer = true;
+        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);
+        assert_eq!(*created.owner, Pubkey::new_from_array([0u8; 32]));
+        host.invoke(&ix, accounts, &[]).unwrap();
+        assert_eq!(*created.data.borrow_mut(), &[0u8; 1234]);
+        assert_eq!(**created.lamports.borrow(), 999);
+        assert_eq!(**from.lamports.borrow(), 1);
+        assert_eq!(*created.owner, owner);
+    }
+
+    #[test]
+    fn system_program_transfer() {
+        let host = Host::new();
+
+        let mut from = host.accounts.new_sol_account(1000);
+        from.is_writable = true;
+        from.is_signer = true;
+        let mut to = host.accounts.new_sol_account(0);
+        to.is_writable = true;
+
+        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);
+        host.invoke(&ix, accounts, &[]).unwrap();
+        assert_eq!(**from.lamports.borrow(), 1);
+        assert_eq!(**to.lamports.borrow(), 999);
+    }
+}

+ 0 - 519
lang/src/fuzzing.rs

@@ -1,519 +0,0 @@
-//! 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::{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, SystemInstruction};
-use solana_program::system_program;
-use solana_program::sysvar::{self, Sysvar};
-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::rc::Rc;
-use std::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard};
-use thiserror::Error;
-
-lazy_static::lazy_static! {
-    static ref ENV: Arc<Environment> = Arc::new(Environment::new());
-}
-
-// Global host environment.
-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: RefCell<Option<Pubkey>>,
-    // Account storage.
-    accounts: AccountStore,
-}
-
-impl Environment {
-    pub fn new() -> Environment {
-        let mut env = Environment {
-            programs: HashMap::new(),
-            current_program: RefCell::new(None),
-            accounts: AccountStore::new(),
-        };
-        env.register(Box::new(SystemProgram));
-        env.register(Box::new(SplToken));
-        env
-    }
-
-    pub fn accounts(&self) -> &AccountStore {
-        &self.accounts
-    }
-
-    // Registers the program on the environment so that it can be invoked via
-    // CPI.
-    pub fn register(&mut self, program: Box<dyn Program>) {
-        self.programs.insert(program.id(), program);
-    }
-
-    // Performs a cross program invocation.
-    pub fn invoke(
-        &self,
-        ix: &Instruction,
-        accounts: &[AccountInfo],
-        seeds: &[&[&[u8]]],
-    ) -> ProgramResult {
-        // If seeds were given, then calculate the expected PDA.
-        let pda = {
-            match *self.current_program.borrow() {
-                None => None,
-                Some(current_program) => match seeds.len() > 0 {
-                    false => None,
-                    true => {
-                        Some(Pubkey::create_program_address(seeds[0], &current_program).unwrap())
-                    }
-                },
-            }
-        };
-
-        // Set the current program.
-        self.current_program.replace(Some(ix.program_id));
-
-        // Invoke the current program.
-        let program = self.programs.get(&ix.program_id).unwrap();
-        let account_infos: Vec<AccountInfo> = ix
-            .accounts
-            .iter()
-            .map(|meta| {
-                let mut acc_info = accounts
-                    .iter()
-                    .find(|info| *info.key == meta.pubkey)
-                    .unwrap()
-                    .clone();
-                // If a PDA was given, market it as signer.
-                if let Some(pda) = pda {
-                    if acc_info.key == &pda {
-                        acc_info.is_signer = true;
-                    }
-                }
-                acc_info
-            })
-            .collect();
-        program.entry(&ix.program_id, &account_infos, &ix.data)
-    }
-}
-
-// 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,
-}
-
-impl AccountStore {
-    pub fn new() -> Self {
-        Self {
-            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),
-            true,
-            false,
-            self.storage.alloc(lamports),
-            &mut [],
-            &system_program::ID,
-            false,
-            Epoch::default(),
-        )
-    }
-
-    pub fn new_token_mint(&self) -> AccountInfo {
-        let rent = Rent::default();
-        let data = self.storage.alloc_slice_fill_copy(Mint::LEN, 0u8);
-        let mut mint = Mint::default();
-        mint.is_initialized = true;
-        Mint::pack(mint, data).unwrap();
-        AccountInfo::new(
-            random_pubkey(&self.storage),
-            false,
-            true,
-            self.storage.alloc(rent.minimum_balance(data.len())),
-            data,
-            &spl_token::ID,
-            false,
-            Epoch::default(),
-        )
-    }
-
-    pub fn new_token_account<'a, 'b>(
-        &self,
-        mint_pubkey: &'a Pubkey,
-        owner_pubkey: &'b Pubkey,
-        balance: u64,
-    ) -> AccountInfo {
-        let rent = Rent::default();
-        let data = self.storage.alloc_slice_fill_copy(TokenAccount::LEN, 0u8);
-        let mut account = TokenAccount::default();
-        account.state = spl_token::state::AccountState::Initialized;
-        account.mint = *mint_pubkey;
-        account.owner = *owner_pubkey;
-        account.amount = balance;
-        TokenAccount::pack(account, data).unwrap();
-        AccountInfo::new(
-            random_pubkey(&self.storage),
-            false,
-            true,
-            self.storage.alloc(rent.minimum_balance(data.len())),
-            data,
-            &spl_token::ID,
-            false,
-            Epoch::default(),
-        )
-    }
-
-    pub fn new_program(&self) -> AccountInfo {
-        AccountInfo::new(
-            random_pubkey(&self.storage),
-            false,
-            false,
-            self.storage.alloc(0),
-            &mut [],
-            &bpf_loader::ID,
-            true,
-            Epoch::default(),
-        )
-    }
-
-    fn new_rent_sysvar_account(&self) -> AccountInfo {
-        let lamports = 100000;
-        let data = self.storage.alloc_slice_fill_copy(size_of::<Rent>(), 0u8);
-        let mut account_info = AccountInfo::new(
-            &sysvar::rent::ID,
-            false,
-            false,
-            self.storage.alloc(lamports),
-            data,
-            &sysvar::ID,
-            false,
-            Epoch::default(),
-        );
-        let rent = Rent::default();
-        rent.to_account_info(&mut account_info).unwrap();
-        account_info
-    }
-}
-
-fn random_pubkey(bump: &Bump) -> &Pubkey {
-    bump.alloc(Pubkey::new(transmute_to_bytes(&rand::random::<[u64; 4]>())))
-}
-
-// Program that can be executed in the environment.
-pub trait Program: Send + Sync + Debug {
-    // The program's ID.
-    fn id(&self) -> Pubkey;
-
-    // Entrypoint to start executing the program.
-    fn entry(&self, program_id: &Pubkey, accounts: &[AccountInfo], ix_data: &[u8])
-        -> ProgramResult;
-}
-
-#[derive(Debug)]
-struct SplToken;
-
-impl Program for SplToken {
-    fn entry(
-        &self,
-        program_id: &Pubkey,
-        accounts: &[AccountInfo],
-        ix_data: &[u8],
-    ) -> ProgramResult {
-        spl_token::processor::Processor::process(program_id, accounts, ix_data)
-    }
-    fn id(&self) -> Pubkey {
-        spl_token::ID
-    }
-}
-
-// Bare minimum implementation of the system program. Not all instructions are
-// implemented. PRs are welcome.
-#[derive(Debug)]
-struct SystemProgram;
-
-impl SystemProgram {
-    fn create_account<'info>(
-        &self,
-        accounts: &[AccountInfo<'info>],
-        lamports: u64,
-        space: u64,
-        owner: Pubkey,
-    ) -> ProgramResult {
-        let acc_infos = &mut accounts.into_iter();
-
-        let from = next_account_info(acc_infos)?;
-        let mut created = next_account_info(acc_infos)?;
-
-        if **created.lamports.borrow() > 0 {
-            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;
-
-        Ok(())
-    }
-
-    fn transfer(&self, accounts: &[AccountInfo], lamports: u64) -> ProgramResult {
-        let acc_infos = &mut accounts.into_iter();
-
-        let from = next_account_info(acc_infos)?;
-        let to = next_account_info(acc_infos)?;
-
-        **from.lamports.borrow_mut() -= lamports;
-        **to.lamports.borrow_mut() += lamports;
-
-        Ok(())
-    }
-
-    fn create_account_with_seed(
-        &self,
-        accounts: &[AccountInfo],
-        base: Pubkey,
-        seed: String,
-        lamports: u64,
-        space: u64,
-        owner: Pubkey,
-    ) -> ProgramResult {
-        unimplemented!()
-    }
-
-    fn assign(&self, _accounts: &[AccountInfo], owner: Pubkey) -> ProgramResult {
-        unimplemented!()
-    }
-
-    fn advance_nonce_account(&self, _accounts: &[AccountInfo]) -> ProgramResult {
-        unimplemented!()
-    }
-
-    fn withdraw_nonce_account(&self, _accounts: &[AccountInfo], _lamports: u64) -> ProgramResult {
-        unimplemented!()
-    }
-
-    fn initialize_nonce_account(
-        &self,
-        _accounts: &[AccountInfo],
-        _entity: Pubkey,
-    ) -> ProgramResult {
-        unimplemented!()
-    }
-
-    fn authorize_nonce_account(&self, _accounts: &[AccountInfo], _entity: Pubkey) -> ProgramResult {
-        unimplemented!()
-    }
-
-    fn allocate(&self, _accounts: &[AccountInfo], _space: u64) -> ProgramResult {
-        unimplemented!()
-    }
-
-    fn allocate_with_seed(
-        &self,
-        _accounts: &[AccountInfo],
-        _base: Pubkey,
-        _seed: String,
-        _space: u64,
-        _owner: Pubkey,
-    ) -> ProgramResult {
-        unimplemented!()
-    }
-
-    fn assign_with_seed(
-        &self,
-        _accounts: &[AccountInfo],
-        _base: Pubkey,
-        _seed: String,
-        _owner: Pubkey,
-    ) -> ProgramResult {
-        unimplemented!()
-    }
-
-    fn transfer_with_seed(
-        &self,
-        _accounts: &[AccountInfo],
-        _lamports: u64,
-        _from_seed: String,
-        _from_owner: Pubkey,
-    ) -> ProgramResult {
-        unimplemented!()
-    }
-}
-
-impl Program for SystemProgram {
-    fn entry(
-        &self,
-        program_id: &Pubkey,
-        accounts: &[AccountInfo],
-        ix_data: &[u8],
-    ) -> ProgramResult {
-        let ix: SystemInstruction =
-            bincode::deserialize(ix_data).map_err(|_| ProgramError::InvalidInstructionData)?;
-
-        match ix {
-            SystemInstruction::CreateAccount {
-                lamports,
-                space,
-                owner,
-            } => self.create_account(accounts, lamports, space, owner),
-            SystemInstruction::Transfer { lamports } => self.transfer(accounts, lamports),
-            SystemInstruction::CreateAccountWithSeed {
-                base,
-                seed,
-                lamports,
-                space,
-                owner,
-            } => self.create_account_with_seed(accounts, base, seed, lamports, space, owner),
-            SystemInstruction::Assign { owner } => self.assign(accounts, owner),
-            SystemInstruction::AdvanceNonceAccount => self.advance_nonce_account(accounts),
-            SystemInstruction::WithdrawNonceAccount(lamports) => {
-                self.withdraw_nonce_account(accounts, lamports)
-            }
-            SystemInstruction::InitializeNonceAccount(entity) => {
-                self.initialize_nonce_account(accounts, entity)
-            }
-            SystemInstruction::AuthorizeNonceAccount(entity) => {
-                self.authorize_nonce_account(accounts, entity)
-            }
-            SystemInstruction::Allocate { space } => self.allocate(accounts, space),
-            SystemInstruction::AllocateWithSeed {
-                base,
-                seed,
-                space,
-                owner,
-            } => self.allocate_with_seed(accounts, base, seed, space, owner),
-            SystemInstruction::AssignWithSeed { base, seed, owner } => {
-                self.assign_with_seed(accounts, base, seed, owner)
-            }
-            SystemInstruction::TransferWithSeed {
-                lamports,
-                from_seed,
-                from_owner,
-            } => self.transfer_with_seed(accounts, lamports, from_seed, from_owner),
-        }
-    }
-    fn id(&self) -> Pubkey {
-        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);
-    }
-}

+ 1 - 5
lang/src/lib.rs

@@ -21,8 +21,6 @@
 //!
 //! 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;
@@ -38,8 +36,6 @@ mod context;
 mod cpi_account;
 mod ctor;
 mod error;
-#[cfg(any(fuzzing, feature = "fuzzing"))]
-pub mod fuzzing;
 pub mod idl;
 mod program_account;
 mod state;
@@ -221,7 +217,7 @@ pub mod cpi {
         accounts: &[AccountInfo<'info>],
         seeds: &[&[&[u8]]],
     ) -> ProgramResult {
-        fuzzing::env().invoke(ix, accounts, seeds)
+        anchor_fuzzing::env().invoke(ix, accounts, seeds)
     }
 }