Skip to content

Commit

Permalink
(wip) seedgen Cargo.toml edit
Browse files Browse the repository at this point in the history
  • Loading branch information
kevin-valerio committed Oct 23, 2024
1 parent 31f571f commit d1177d7
Show file tree
Hide file tree
Showing 12 changed files with 281 additions and 50 deletions.
5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,8 @@ std = [

[dev-dependencies]
assert_cmd = { version = "2.0.16" }
toml_edit = { version = "0.22.20" }
predicates = { version = "3.1.2" }


[dependencies]
# Standard crates and helpers
tempfile = { version = "3.12.0" }
Expand Down Expand Up @@ -74,6 +72,7 @@ regex = { version = "1.10.4" }
ratatui = { version = "0.28.0", features = ["all-widgets"] }
crossterm = { version = "0.28.1" }
chrono = { version = "0.4.38" }
toml_edit = { version = "0.22.22" }

# Substrate-specific crates
parity-scale-codec = { version = "3.6.12" }
Expand All @@ -97,7 +96,7 @@ contract-metadata = { version = "4.1.1" }
colored = { version = "2.1.0" }
thiserror = { version = "1.0.63" }
time = { version = "0.3.36" }

pretty_assertions = "1.4.1"

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
16 changes: 7 additions & 9 deletions sample/dns/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

#[ink::contract]
mod dns {
use ink::storage::Mapping;
use ink::storage::StorageVec;
use ink::storage::{
Mapping,
StorageVec,
};

const FORBIDDEN_DOMAIN: [u8; 32] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1,
]; //we forbid it :/
]; // we forbid it :/

/// Emitted whenever a new name is being registered.
#[ink(event)]
Expand Down Expand Up @@ -57,7 +59,6 @@ mod dns {
state: i32,

should_panic_after_three_calls: bool,

}

impl Default for DomainNameService {
Expand Down Expand Up @@ -123,7 +124,6 @@ mod dns {
Ok(())
}


#[ink(message)]
pub fn set_address(&mut self, name: Hash, new_address: AccountId) -> Result<()> {
let caller = self.env().caller();
Expand Down Expand Up @@ -157,8 +157,8 @@ mod dns {
return Err(Error::CallerIsNotOwner);
}

if number == 69i32 {
//NOP, 69 is forbidden! right?
if number == 42i32 {
// NOP, 42 is forbidden! right?
return Err(Error::ForbiddenDomain);
}

Expand Down Expand Up @@ -240,7 +240,6 @@ mod dns {
ink::env::test::set_caller::<Environment>(caller);
}


#[ink::test]
fn test_invariants_nested_calls() {
let accounts = default_accounts();
Expand Down Expand Up @@ -281,7 +280,6 @@ mod dns {
assert_eq!(contract.should_panic_after_three_calls, true);
}


#[ink::test]
fn transfer_works() {
let accounts = default_accounts();
Expand Down
2 changes: 1 addition & 1 deletion sample/dummy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ std = [
phink = []

ink-as-dependency = []
e2e-tests = []
e2e-tests = []
43 changes: 40 additions & 3 deletions sample/dummy/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ mod dummy {
if data.chars().nth(1).unwrap() == 'u' {
if data.chars().nth(2).unwrap() == 'z' {
if data.chars().nth(3).unwrap() == 'z' {
self.forbidden_number = 69;
self.forbidden_number = 42;
}
}
}
Expand All @@ -58,7 +58,44 @@ mod dummy {
fn new_works() {
let mut a = MyBuggedContract::new();
// a.toz(32, Hash::from([0x99; 32]));
a.crash_with_invariant("xxx".to_string()).unwrap();
a.crash_with_invariant("abc".to_string()).unwrap();
a.crash_with_invariant("def".to_string()).unwrap();
a.crash_with_invariant("fuz".to_string()).unwrap();
}

#[ink::test]
fn for_seedgen() {
let mut a = MyBuggedContract::new();
a.crash_with_invariant("abc".to_string()).unwrap();
a.toz(32, crate::dummy::Hash::from([0x99; 32]));
a.crash_with_invariant("fuz".to_string()).unwrap();
}
}

#[cfg(all(test, feature = "e2e-tests"))]
mod e2e_tests {
use super::*;
use ink_e2e::ContractsBackend;

type E2EResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;

#[ink_e2e::test]
async fn it_works<Client: E2EBackend>(mut client: Client) -> E2EResult<()> {
let mut constructor = MyBuggedContractRef::new();
let contract = client
.instantiate("dummy", &ink_e2e::alice(), &mut constructor)
.submit()
.await
.expect("instantiate failed");
let mut call_builder = contract.call_builder::<MyBuggedContract>();

let flip = call_builder.toz(432432, crate::dummy::Hash::from([0x12; 32]));
let _flip_res = client
.call(&ink_e2e::bob(), &flip)
.submit()
.await
.expect("flip failed");
Ok(())
}
}

Expand All @@ -69,7 +106,7 @@ mod dummy {
#[cfg(feature = "phink")]
#[ink(message)]
pub fn phink_assert_dangerous_number(&self) {
let forbidden_number = 69;
let forbidden_number = 42;
assert_ne!(self.forbidden_number, forbidden_number);
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ pub fn format_error(e: anyhow::Error) -> String {

let mut source = e.source();
while let Some(cause) = source {
message = format!("{message}\n{} {cause}\n", "--> ".cyan().bold());
let arrow = "--> ".cyan().bold();
message = format!("{message}\n{arrow} {cause}\n");
source = cause.source();
}

Expand Down
2 changes: 1 addition & 1 deletion src/instrumenter/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub mod instrumentation;
pub mod path;
pub mod seeder;
pub mod seedgen;
pub mod traits;
113 changes: 88 additions & 25 deletions src/instrumenter/seeder.rs → src/instrumenter/seedgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::{
EmptyResult,
ResultOf,
};

use anyhow::{
bail,
Context,
Expand Down Expand Up @@ -30,7 +31,11 @@ use syn::{
Pat,
Stmt,
};
use tempfile::tempdir;
use toml_edit::{
DocumentMut,
Formatted,
Value,
};

#[derive(Debug, Clone)]
pub struct SeedExtractInjector {
Expand All @@ -48,53 +53,85 @@ impl SeedExtractInjector {
compiled_path,
})
}

/// Fork the contract, insert the snippet to extract the seeds, patch Cargo.toml, run the tests,
/// extracts the seeds.
pub fn prepare(&self) -> EmptyResult {
self.fork()
.context("Forking the project to a new directory failed")?;

self.insert_snippet()
.context("Inserting the snippet into the file for seed extraction wasn't possible")?;

// self.build()
// .context("Couldn't build the contract required for seed extraction")?;
self.patch_toml()
.context("Inserting the snippet into the file for seed extraction wasn't possible")?;

self.build()
.context("Couldn't build the contract required for seed extraction")?;
Ok(())
}

pub fn run_tests(&self) -> EmptyResult {
todo!()
}

/// Insert the snippet that will extract each call and send it via `debug_println!`
fn insert_snippet(&self) -> EmptyResult {
self.for_each_file(|file_path| {
let source_code =
fs::read_to_string(&file_path).context(format!("Couldn't read {file_path:?}"))?;

self.instrument_file(file_path, &source_code, self.clone())
self.instrument_file(file_path, &source_code, self)
.context("Failed to instrument the file")
})?;
Ok(())
}
}

impl ContractVisitor for SeedExtractInjector {
fn input_directory(&self) -> PathBuf {
self.contract_path.to_path_buf()
}
/// Patch the `Cargo.toml` to use our own version of ink!
fn patch_toml(&self) -> EmptyResult {
let cargo_path = &self.output_directory().join("Cargo.toml");
let cargo_content = fs::read_to_string(cargo_path)?;
let mut doc = cargo_content.parse::<DocumentMut>()?;
const REPO: &str = "https://github.com/kevin-valerio/ink";

// Function to update ink dependencies in a table
fn update_ink_deps(table: &mut toml_edit::Table) {
for (key, value) in table.iter_mut() {
if let toml_edit::Item::Value(Value::InlineTable(dep_table)) = value {
// Only modify if it's a table and contains a version
if dep_table.contains_key("version") {
// Check package name if specified, otherwise use the key
let dep_name = dep_table
.get("package")
.and_then(|v| v.as_str())
.unwrap_or(&*key);

if dep_name.starts_with("ink_") || dep_name == "ink" {
dep_table.insert(
"git",
Value::String(Formatted::new(REPO.parse().unwrap())),
);
}
}
}
}
}

fn output_directory(&self) -> PathBuf {
match &self.compiled_path {
// We create a new directory in tmp/ if nothing is passed
None => tempdir().unwrap().into_path(),
Some(contract) => contract.into(),
if let Some(deps) = doc.get_mut("dependencies").and_then(|d| d.as_table_mut()) {
update_ink_deps(deps);
}
}

fn verbose(&self) -> bool {
true
if let Some(dev_deps) = doc
.get_mut("dev-dependencies")
.and_then(|d| d.as_table_mut())
{
update_ink_deps(dev_deps);
}

fs::write(cargo_path, doc.to_string())?;
Ok(())
}
}

impl SeedExtractInjector {
/// Check if the function has the `#[ink(message)]` attribute
fn has_ink_message_attribute(i: &mut ImplItemFn) -> bool {
for attr in &i.attrs {
Expand All @@ -113,7 +150,33 @@ impl SeedExtractInjector {
}
}

impl VisitMut for SeedExtractInjector {
impl ContractVisitor for SeedExtractInjector {
fn input_directory(&self) -> PathBuf {
self.contract_path.to_path_buf()
}

fn output_directory(&self) -> PathBuf {
let path = match &self.compiled_path {
None => {
// Create a new directory that is not tied to `TempDir`
let dir = std::env::temp_dir().join("phink_seedgen");
fs::create_dir_all(&dir).expect("Failed to create directory");
dir
}
Some(contract) => contract.to_path_buf(),
};
if self.verbose() {
println!("Using {path:?} for contract output");
}
path
}

fn verbose(&self) -> bool {
true
}
}

impl VisitMut for &SeedExtractInjector {
fn visit_item_mut(&mut self, item: &mut Item) {
match item {
Item::Fn(f) => self.visit_item_fn_mut(f),
Expand All @@ -127,7 +190,7 @@ impl VisitMut for SeedExtractInjector {
fn visit_item_impl_mut(&mut self, i: &mut ItemImpl) {
for item in &mut i.items {
if let ImplItem::Fn(method) = item {
if Self::has_ink_message_attribute(method) {
if SeedExtractInjector::has_ink_message_attribute(method) {
let fn_name = &method.sig.ident;
// If the visited function isn't an invariant
if !fn_name.to_string().starts_with("phink_") {
Expand Down Expand Up @@ -195,7 +258,7 @@ impl VisitMut for SeedExtractInjector {

#[cfg(test)]
mod tests {
use crate::instrumenter::seeder::SeedExtractInjector;
use crate::instrumenter::seedgen::SeedExtractInjector;
use quote::quote;
use std::path::PathBuf;
use syn::{
Expand Down Expand Up @@ -243,7 +306,7 @@ mod tests {
if data.chars().nth(1).unwrap() == 'u' {
if data.chars().nth(2).unwrap() == 'z' {
if data.chars().nth(3).unwrap() == 'z' {
self.forbidden_number = 69;
self.forbidden_number = 42;
}
}
}
Expand Down Expand Up @@ -282,15 +345,15 @@ mod tests {
#[cfg(feature = "phink")]
#[ink(message)]
pub fn phink_assert_dangerous_number(&self) {
let forbidden_number = 69;
let forbidden_number = 42;
assert_ne!(self.forbidden_number, forbidden_number);
}
}
}"#;

let mut syntax_tree: File = parse_str(input_code).expect("Failed to parse code");
let mut seed_injector =
SeedExtractInjector::new(&PathBuf::from("sample/dummy"), None).unwrap();
&SeedExtractInjector::new(&PathBuf::from("sample/dummy"), None).unwrap();
seed_injector.visit_file_mut(&mut syntax_tree);

let generated_code = quote!(#syntax_tree).to_string();
Expand Down
Loading

0 comments on commit d1177d7

Please sign in to comment.