diff --git a/examples/reentrancy_guard/flip_on_me/.gitignore b/examples/reentrancy_guard/.gitignore similarity index 100% rename from examples/reentrancy_guard/flip_on_me/.gitignore rename to examples/reentrancy_guard/.gitignore diff --git a/examples/reentrancy_guard/Cargo.toml b/examples/reentrancy_guard/Cargo.toml new file mode 100644 index 000000000..fc645c820 --- /dev/null +++ b/examples/reentrancy_guard/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "flipper" +version = "1.4.0" +authors = ["Supercolony "] +edition = "2021" + +[dependencies] +ink_primitives = { branch = "master", git = "https://github.com/paritytech/ink", default-features = false } +ink_metadata = { branch = "master", git = "https://github.com/paritytech/ink", default-features = false, features = ["derive"], optional = true } +ink_env = { branch = "master", git = "https://github.com/paritytech/ink", default-features = false } +ink_storage = { branch = "master", git = "https://github.com/paritytech/ink", default-features = false } +ink_lang = { branch = "master", git = "https://github.com/paritytech/ink", default-features = false } +ink_prelude = { branch = "master", git = "https://github.com/paritytech/ink", default-features = false } + +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } + +# These dependencies +brush = { path = "../..", default-features = false, features = ["reentrancy_guard"] } + +[lib] +name = "flipper" +path = "lib.rs" + +crate-type = [ + "rlib", +] + +[features] +default = ["std"] +std = [ + "ink_primitives/std", + "ink_metadata", + "ink_metadata/std", + "ink_env/std", + "ink_storage/std", + "ink_lang/std", + "scale/std", + "scale-info", + "scale-info/std", + + # These dependencies + "brush/std", +] + +ink-as-dependency = [] + +[profile.dev] +codegen-units = 16 +overflow-checks = false + +[profile.release] +overflow-checks = false \ No newline at end of file diff --git a/examples/reentrancy_guard/flipper/.gitignore b/examples/reentrancy_guard/contracts/flip_on_me/.gitignore similarity index 100% rename from examples/reentrancy_guard/flipper/.gitignore rename to examples/reentrancy_guard/contracts/flip_on_me/.gitignore diff --git a/examples/reentrancy_guard/flip_on_me/Cargo.toml b/examples/reentrancy_guard/contracts/flip_on_me/Cargo.toml similarity index 81% rename from examples/reentrancy_guard/flip_on_me/Cargo.toml rename to examples/reentrancy_guard/contracts/flip_on_me/Cargo.toml index 999bb103b..45aec5063 100644 --- a/examples/reentrancy_guard/flip_on_me/Cargo.toml +++ b/examples/reentrancy_guard/contracts/flip_on_me/Cargo.toml @@ -15,10 +15,8 @@ ink_prelude = { branch = "master", git = "https://github.com/paritytech/ink", de scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } -# This dependencies -# We will do a cross contract call via `MyFlipper` wrapper imported here -brush = { path = "../../..", default-features = false, features = ["reentrancy_guard"] } -my_flipper_guard = { path = "../flipper", default-features = false, features = ["ink-as-dependency"] } +flipper = { path = "../..", default-features = false } +brush = { path = "../../../..", default-features = false } [lib] name = "flip_on_me" @@ -41,8 +39,8 @@ std = [ "scale-info", "scale-info/std", - # This dependencies - "my_flipper_guard/std", + "flipper/std", + "brush/std", ] ink-as-dependency = [] diff --git a/examples/reentrancy_guard/contracts/flip_on_me/lib.rs b/examples/reentrancy_guard/contracts/flip_on_me/lib.rs new file mode 100644 index 000000000..5f4d8588a --- /dev/null +++ b/examples/reentrancy_guard/contracts/flip_on_me/lib.rs @@ -0,0 +1,20 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +#[brush::contract] +pub mod flip_on_me { + use flipper::traits::flip_on_me::*; + use ink_storage::traits::SpreadAllocate; + + #[ink(storage)] + #[derive(Default, SpreadAllocate)] + pub struct FlipOnMeContract {} + + impl FlipOnMeContract { + #[ink(constructor)] + pub fn new() -> Self { + ink_lang::codegen::initialize_contract(|_instance: &mut Self| {}) + } + } + + impl FlipOnMe for FlipOnMeContract {} +} diff --git a/examples/reentrancy_guard/contracts/flipper/.gitignore b/examples/reentrancy_guard/contracts/flipper/.gitignore new file mode 100644 index 000000000..8de8f877e --- /dev/null +++ b/examples/reentrancy_guard/contracts/flipper/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/examples/reentrancy_guard/flipper/Cargo.toml b/examples/reentrancy_guard/contracts/flipper/Cargo.toml similarity index 87% rename from examples/reentrancy_guard/flipper/Cargo.toml rename to examples/reentrancy_guard/contracts/flipper/Cargo.toml index 2f84b4cf9..ad8f09423 100644 --- a/examples/reentrancy_guard/flipper/Cargo.toml +++ b/examples/reentrancy_guard/contracts/flipper/Cargo.toml @@ -16,7 +16,8 @@ scale = { package = "parity-scale-codec", version = "3", default-features = fals scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } # These dependencies -brush = { path = "../../..", default-features = false, features = ["reentrancy_guard"] } +flipper = { path = "../..", default-features = false } +brush = { path = "../../../..", default-features = false } [lib] name = "my_flipper_guard" @@ -24,8 +25,6 @@ path = "lib.rs" crate-type = [ "cdylib", - # This contract will be imported by FlipOnMe contract, so we need build this crate also like a `rlib` - "rlib", ] [features] @@ -42,6 +41,7 @@ std = [ "scale-info/std", # These dependencies + "flipper/std", "brush/std", ] diff --git a/examples/reentrancy_guard/contracts/flipper/lib.rs b/examples/reentrancy_guard/contracts/flipper/lib.rs new file mode 100644 index 000000000..4523c9408 --- /dev/null +++ b/examples/reentrancy_guard/contracts/flipper/lib.rs @@ -0,0 +1,34 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +#[brush::contract] +pub mod my_flipper_guard { + use flipper::traits::flipper::*; + use ink_storage::traits::SpreadAllocate; + + #[ink(storage)] + #[derive(Default, SpreadAllocate, ReentrancyGuardStorage)] + pub struct MyFlipper { + #[ReentrancyGuardStorageField] + guard: ReentrancyGuardData, + value: bool, + } + + impl FlipperStorage for MyFlipper { + fn value(&self) -> &bool { + &self.value + } + + fn value_mut(&mut self) -> &mut bool { + &mut self.value + } + } + + impl MyFlipper { + #[ink(constructor)] + pub fn new() -> Self { + ink_lang::codegen::initialize_contract(|_instance: &mut Self| {}) + } + } + + impl Flipper for MyFlipper {} +} diff --git a/examples/reentrancy_guard/flip_on_me/lib.rs b/examples/reentrancy_guard/flip_on_me/lib.rs deleted file mode 100644 index 9c8525d85..000000000 --- a/examples/reentrancy_guard/flip_on_me/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -#[ink_lang::contract] -pub mod flip_on_me { - use brush::contracts::reentrancy_guard::*; - use ink_storage::traits::SpreadAllocate; - use my_flipper_guard::my_flipper_guard::FlipperRef; - - #[ink(storage)] - #[derive(Default, SpreadAllocate)] - pub struct FlipOnMe {} - - impl FlipOnMe { - #[ink(constructor)] - pub fn new() -> Self { - ink_lang::codegen::initialize_contract(|_instance: &mut Self| {}) - } - - #[ink(message)] - pub fn flip_on_me(&mut self) -> Result<(), ReentrancyGuardError> { - let caller = self.env().caller(); - self.flip_on_target(caller) - } - - #[ink(message)] - pub fn flip_on_target(&mut self, callee: AccountId) -> Result<(), ReentrancyGuardError> { - // This method does a cross-contract call to caller contract and calls the `flip` method. - FlipperRef::flip(&callee) - } - } -} diff --git a/examples/reentrancy_guard/flipper/lib.rs b/examples/reentrancy_guard/flipper/lib.rs deleted file mode 100644 index 48b5e9f0f..000000000 --- a/examples/reentrancy_guard/flipper/lib.rs +++ /dev/null @@ -1,104 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -/// This is a stub implementation of contract with method `flip_on_me`. -/// We need this implementation to create wrapper for contract's account id. -/// With this wrapper, we can easily call methods of some contract. -/// Example: -/// ``` -/// let mut flipper: CallerOfFlip = FromAccountId::from_account_id(callee); -/// flipper.flip_on_me(); -/// ``` -#[ink_lang::contract(compile_as_dependency = true)] -pub mod flip_on_me { - use brush::contracts::reentrancy_guard::*; - #[ink(storage)] - pub struct CallerOfFlip {} - - impl CallerOfFlip { - #[ink(constructor)] - pub fn new() -> Self { - unimplemented!() - } - } - - impl CallerOfFlip { - #[ink(message)] - pub fn flip_on_me(&mut self) -> Result<(), ReentrancyGuardError> { - unimplemented!() - } - } -} - -#[brush::contract] -pub mod my_flipper_guard { - use brush::{ - contracts::reentrancy_guard::*, - modifiers, - }; - use ink_storage::traits::SpreadAllocate; - - use crate::flip_on_me::CallerOfFlipRef; - use ink_env::call::FromAccountId; - - pub trait FlipperStorage { - fn value(&self) -> &bool; - fn value_mut(&mut self) -> &mut bool; - } - - // TODO: Refactor example to have a separate trait defined in ../ to avoid `ink-as-dependency` - #[brush::wrapper] - pub type FlipperRef = dyn Flipper; - - #[brush::trait_definition] - pub trait Flipper: FlipperStorage + ReentrancyGuardStorage { - #[ink(message)] - fn get_value(&self) -> bool { - self.value().clone() - } - - #[ink(message)] - #[brush::modifiers(non_reentrant)] - fn flip(&mut self) -> Result<(), ReentrancyGuardError> { - *self.value_mut() = !self.value().clone(); - Ok(()) - } - - #[ink(message)] - #[modifiers(non_reentrant)] - fn call_flip_on_me(&mut self, callee: AccountId) -> Result<(), ReentrancyGuardError> { - // This method will do a cross-contract call to callee account. It calls method `flip_on_me`. - // Callee contract during execution of `flip_on_me` will call `flip` of this contract. - // `call_flip_on_me` and `flip` are marked with `non_reentrant` modifier. It means, - // that call of `flip` after `call_flip_on_me` must fail. - let mut flipper: CallerOfFlipRef = FromAccountId::from_account_id(callee); - flipper.flip_on_me() - } - } - - #[ink(storage)] - #[derive(Default, SpreadAllocate, ReentrancyGuardStorage)] - pub struct MyFlipper { - #[ReentrancyGuardStorageField] - guard: ReentrancyGuardData, - value: bool, - } - - impl FlipperStorage for MyFlipper { - fn value(&self) -> &bool { - &self.value - } - - fn value_mut(&mut self) -> &mut bool { - &mut self.value - } - } - - impl MyFlipper { - #[ink(constructor)] - pub fn new() -> Self { - ink_lang::codegen::initialize_contract(|_instance: &mut Self| {}) - } - } - - impl Flipper for MyFlipper {} -} diff --git a/examples/reentrancy_guard/lib.rs b/examples/reentrancy_guard/lib.rs new file mode 100644 index 000000000..57a707ac6 --- /dev/null +++ b/examples/reentrancy_guard/lib.rs @@ -0,0 +1,3 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod traits; diff --git a/examples/reentrancy_guard/traits/flip_on_me.rs b/examples/reentrancy_guard/traits/flip_on_me.rs new file mode 100644 index 000000000..80ee93087 --- /dev/null +++ b/examples/reentrancy_guard/traits/flip_on_me.rs @@ -0,0 +1,23 @@ +pub use brush::contracts::reentrancy_guard::*; +use brush::traits::{ + AccountId, + InkStorage, +}; + +#[brush::wrapper] +pub type FlipOnMeRef = dyn FlipOnMe; + +#[brush::trait_definition] +pub trait FlipOnMe: InkStorage { + #[ink(message)] + fn flip_on_me(&mut self) -> Result<(), ReentrancyGuardError> { + let caller = Self::env().caller(); + self.flip_on_target(caller) + } + + #[ink(message)] + fn flip_on_target(&mut self, callee: AccountId) -> Result<(), ReentrancyGuardError> { + // This method does a cross-contract call to caller contract and calls the `flip` method. + crate::traits::flipper::FlipperRef::flip(&callee) + } +} diff --git a/examples/reentrancy_guard/traits/flipper.rs b/examples/reentrancy_guard/traits/flipper.rs new file mode 100644 index 000000000..6771ece39 --- /dev/null +++ b/examples/reentrancy_guard/traits/flipper.rs @@ -0,0 +1,38 @@ +pub use brush::contracts::reentrancy_guard::*; +use brush::{ + modifiers, + traits::AccountId, +}; + +pub trait FlipperStorage { + fn value(&self) -> &bool; + fn value_mut(&mut self) -> &mut bool; +} + +#[brush::wrapper] +pub type FlipperRef = dyn Flipper; + +#[brush::trait_definition] +pub trait Flipper: FlipperStorage + ReentrancyGuardStorage { + #[ink(message)] + fn get_value(&self) -> bool { + self.value().clone() + } + + #[ink(message)] + #[brush::modifiers(non_reentrant)] + fn flip(&mut self) -> Result<(), ReentrancyGuardError> { + *self.value_mut() = !self.value().clone(); + Ok(()) + } + + #[ink(message)] + #[modifiers(non_reentrant)] + fn call_flip_on_me(&mut self, callee: AccountId) -> Result<(), ReentrancyGuardError> { + // This method will do a cross-contract call to callee account. It calls method `flip_on_me`. + // Callee contract during execution of `flip_on_me` will call `flip` of this contract. + // `call_flip_on_me` and `flip` are marked with `non_reentrant` modifier. It means, + // that call of `flip` after `call_flip_on_me` must fail. + crate::traits::flip_on_me::FlipOnMeRef::flip_on_me(&callee) + } +} diff --git a/examples/reentrancy_guard/traits/mod.rs b/examples/reentrancy_guard/traits/mod.rs new file mode 100644 index 000000000..12f6d553b --- /dev/null +++ b/examples/reentrancy_guard/traits/mod.rs @@ -0,0 +1,2 @@ +pub mod flip_on_me; +pub mod flipper; diff --git a/redspot.config.ts b/redspot.config.ts index 14a4ba3b5..54c17c565 100644 --- a/redspot.config.ts +++ b/redspot.config.ts @@ -8,7 +8,7 @@ export default { contract: { ink: { toolchain: 'nightly', - sources: ['example_project_structure/contracts/**', 'examples/**', 'mock/**'] + sources: ['example_project_structure/contracts/**', 'examples/**/', 'mock/**', `!examples/reentrancy_guard/Cargo.toml`] } }, networks: { diff --git a/utils/brush_lang/proc_macros/metadata.rs b/utils/brush_lang/proc_macros/metadata.rs index c0d789fca..f9bd7a468 100644 --- a/utils/brush_lang/proc_macros/metadata.rs +++ b/utils/brush_lang/proc_macros/metadata.rs @@ -1,4 +1,4 @@ -use cargo_metadata::MetadataCommand; +use cargo_metadata::camino::Utf8PathBuf; use fs2::FileExt; use proc_macro2::TokenStream as TokenStream2; use serde::{ @@ -18,7 +18,6 @@ use std::{ Seek, SeekFrom, }, - path::PathBuf, str::FromStr, }; use syn::{ @@ -122,36 +121,24 @@ pub(crate) enum LockType { } /// Function returns exclusively locked file for metadata. -/// It stores file in the nearest target folder -/// from the directory where the build command has been invoked(output of `pwd` command). -/// If the directory doesn't contain `Cargo.toml` file, -/// it will try to find `Cargo.toml` in the upper directories. +/// It stores file in the target folder where `ink_lang` is stored. pub(crate) fn get_locked_file(t: LockType) -> File { - let mut manifest_path = PathBuf::from(env::var("PWD").expect("Can't get PWD")).join("Cargo.toml"); - - // if the current directory does not contain a Cargo.toml file, go up until you find it. - while !manifest_path.exists() { - if let Some(str) = manifest_path.as_os_str().to_str() { - // If `/Cargo.toml` is not exist, it means that we will do infinity while, so break it - assert_ne!(str, "/Cargo.toml", "Can't find Cargo.toml in directories tree"); - } - // Remove Cargo.toml - manifest_path.pop(); - // Remove parent folder - manifest_path.pop(); - manifest_path = manifest_path.join("Cargo.toml"); - } - - let mut cmd = MetadataCommand::new(); - let metadata = cmd - .manifest_path(manifest_path.clone()) - .exec() - .expect("Error invoking `cargo metadata`"); - - let dir = metadata.target_directory.join(TEMP_FILE); - - let file = match OpenOptions::new().read(true).write(true).create(true).open(&dir) { - Err(why) => panic!("Couldn't open temporary storage: {}", why), + const PREFIX: &str = "ink_lang="; + const SUFFIX: &str = "target/"; + let target: String = env::args() + .find(|arg| arg.contains(PREFIX)) + .expect("Unable to find PREFIX"); + let target: String = target + .chars() + .skip(PREFIX.len()) + .take(target.find(SUFFIX).expect("Unable to find debug/deps") - PREFIX.len() + SUFFIX.len()) + .collect(); + + let target_dir = Utf8PathBuf::from_str(target.as_str()).expect("Can't generate Path from target"); + let dir = target_dir.join(TEMP_FILE); + + let file = match OpenOptions::new().create(true).read(true).write(true).open(&dir) { + Err(why) => panic!("Couldn't open temporary storage {} : {}", dir, why), Ok(file) => file, }; match t {