Skip to content

Commit d1177d7

Browse files
committed
(wip) seedgen Cargo.toml edit
1 parent 31f571f commit d1177d7

File tree

12 files changed

+281
-50
lines changed

12 files changed

+281
-50
lines changed

Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,8 @@ std = [
4343

4444
[dev-dependencies]
4545
assert_cmd = { version = "2.0.16" }
46-
toml_edit = { version = "0.22.20" }
4746
predicates = { version = "3.1.2" }
4847

49-
5048
[dependencies]
5149
# Standard crates and helpers
5250
tempfile = { version = "3.12.0" }
@@ -74,6 +72,7 @@ regex = { version = "1.10.4" }
7472
ratatui = { version = "0.28.0", features = ["all-widgets"] }
7573
crossterm = { version = "0.28.1" }
7674
chrono = { version = "0.4.38" }
75+
toml_edit = { version = "0.22.22" }
7776

7877
# Substrate-specific crates
7978
parity-scale-codec = { version = "3.6.12" }
@@ -97,7 +96,7 @@ contract-metadata = { version = "4.1.1" }
9796
colored = { version = "2.1.0" }
9897
thiserror = { version = "1.0.63" }
9998
time = { version = "0.3.36" }
100-
99+
pretty_assertions = "1.4.1"
101100

102101
[lints.rust]
103102
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }

sample/dns/lib.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
#[ink::contract]
44
mod dns {
5-
use ink::storage::Mapping;
6-
use ink::storage::StorageVec;
5+
use ink::storage::{
6+
Mapping,
7+
StorageVec,
8+
};
79

810
const FORBIDDEN_DOMAIN: [u8; 32] = [
911
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,
1012
0, 1,
11-
]; //we forbid it :/
13+
]; // we forbid it :/
1214

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

5961
should_panic_after_three_calls: bool,
60-
6162
}
6263

6364
impl Default for DomainNameService {
@@ -123,7 +124,6 @@ mod dns {
123124
Ok(())
124125
}
125126

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

160-
if number == 69i32 {
161-
//NOP, 69 is forbidden! right?
160+
if number == 42i32 {
161+
// NOP, 42 is forbidden! right?
162162
return Err(Error::ForbiddenDomain);
163163
}
164164

@@ -240,7 +240,6 @@ mod dns {
240240
ink::env::test::set_caller::<Environment>(caller);
241241
}
242242

243-
244243
#[ink::test]
245244
fn test_invariants_nested_calls() {
246245
let accounts = default_accounts();
@@ -281,7 +280,6 @@ mod dns {
281280
assert_eq!(contract.should_panic_after_three_calls, true);
282281
}
283282

284-
285283
#[ink::test]
286284
fn transfer_works() {
287285
let accounts = default_accounts();

sample/dummy/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ std = [
2424
phink = []
2525

2626
ink-as-dependency = []
27-
e2e-tests = []
27+
e2e-tests = []

sample/dummy/lib.rs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ mod dummy {
3535
if data.chars().nth(1).unwrap() == 'u' {
3636
if data.chars().nth(2).unwrap() == 'z' {
3737
if data.chars().nth(3).unwrap() == 'z' {
38-
self.forbidden_number = 69;
38+
self.forbidden_number = 42;
3939
}
4040
}
4141
}
@@ -58,7 +58,44 @@ mod dummy {
5858
fn new_works() {
5959
let mut a = MyBuggedContract::new();
6060
// a.toz(32, Hash::from([0x99; 32]));
61-
a.crash_with_invariant("xxx".to_string()).unwrap();
61+
a.crash_with_invariant("abc".to_string()).unwrap();
62+
a.crash_with_invariant("def".to_string()).unwrap();
63+
a.crash_with_invariant("fuz".to_string()).unwrap();
64+
}
65+
66+
#[ink::test]
67+
fn for_seedgen() {
68+
let mut a = MyBuggedContract::new();
69+
a.crash_with_invariant("abc".to_string()).unwrap();
70+
a.toz(32, crate::dummy::Hash::from([0x99; 32]));
71+
a.crash_with_invariant("fuz".to_string()).unwrap();
72+
}
73+
}
74+
75+
#[cfg(all(test, feature = "e2e-tests"))]
76+
mod e2e_tests {
77+
use super::*;
78+
use ink_e2e::ContractsBackend;
79+
80+
type E2EResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
81+
82+
#[ink_e2e::test]
83+
async fn it_works<Client: E2EBackend>(mut client: Client) -> E2EResult<()> {
84+
let mut constructor = MyBuggedContractRef::new();
85+
let contract = client
86+
.instantiate("dummy", &ink_e2e::alice(), &mut constructor)
87+
.submit()
88+
.await
89+
.expect("instantiate failed");
90+
let mut call_builder = contract.call_builder::<MyBuggedContract>();
91+
92+
let flip = call_builder.toz(432432, crate::dummy::Hash::from([0x12; 32]));
93+
let _flip_res = client
94+
.call(&ink_e2e::bob(), &flip)
95+
.submit()
96+
.await
97+
.expect("flip failed");
98+
Ok(())
6299
}
63100
}
64101

@@ -69,7 +106,7 @@ mod dummy {
69106
#[cfg(feature = "phink")]
70107
#[ink(message)]
71108
pub fn phink_assert_dangerous_number(&self) {
72-
let forbidden_number = 69;
109+
let forbidden_number = 42;
73110
assert_ne!(self.forbidden_number, forbidden_number);
74111
}
75112
}

src/cli/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ pub fn format_error(e: anyhow::Error) -> String {
4141

4242
let mut source = e.source();
4343
while let Some(cause) = source {
44-
message = format!("{message}\n{} {cause}\n", "--> ".cyan().bold());
44+
let arrow = "--> ".cyan().bold();
45+
message = format!("{message}\n{arrow} {cause}\n");
4546
source = cause.source();
4647
}
4748

src/instrumenter/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
pub mod instrumentation;
22
pub mod path;
3-
pub mod seeder;
3+
pub mod seedgen;
44
pub mod traits;

src/instrumenter/seeder.rs renamed to src/instrumenter/seedgen.rs

Lines changed: 88 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::{
33
EmptyResult,
44
ResultOf,
55
};
6+
67
use anyhow::{
78
bail,
89
Context,
@@ -30,7 +31,11 @@ use syn::{
3031
Pat,
3132
Stmt,
3233
};
33-
use tempfile::tempdir;
34+
use toml_edit::{
35+
DocumentMut,
36+
Formatted,
37+
Value,
38+
};
3439

3540
#[derive(Debug, Clone)]
3641
pub struct SeedExtractInjector {
@@ -48,53 +53,85 @@ impl SeedExtractInjector {
4853
compiled_path,
4954
})
5055
}
56+
57+
/// Fork the contract, insert the snippet to extract the seeds, patch Cargo.toml, run the tests,
58+
/// extracts the seeds.
5159
pub fn prepare(&self) -> EmptyResult {
5260
self.fork()
5361
.context("Forking the project to a new directory failed")?;
5462

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

58-
// self.build()
59-
// .context("Couldn't build the contract required for seed extraction")?;
66+
self.patch_toml()
67+
.context("Inserting the snippet into the file for seed extraction wasn't possible")?;
68+
69+
self.build()
70+
.context("Couldn't build the contract required for seed extraction")?;
6071
Ok(())
6172
}
6273

6374
pub fn run_tests(&self) -> EmptyResult {
6475
todo!()
6576
}
6677

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

72-
self.instrument_file(file_path, &source_code, self.clone())
84+
self.instrument_file(file_path, &source_code, self)
7385
.context("Failed to instrument the file")
7486
})?;
7587
Ok(())
7688
}
77-
}
7889

79-
impl ContractVisitor for SeedExtractInjector {
80-
fn input_directory(&self) -> PathBuf {
81-
self.contract_path.to_path_buf()
82-
}
90+
/// Patch the `Cargo.toml` to use our own version of ink!
91+
fn patch_toml(&self) -> EmptyResult {
92+
let cargo_path = &self.output_directory().join("Cargo.toml");
93+
let cargo_content = fs::read_to_string(cargo_path)?;
94+
let mut doc = cargo_content.parse::<DocumentMut>()?;
95+
const REPO: &str = "https://github.com/kevin-valerio/ink";
96+
97+
// Function to update ink dependencies in a table
98+
fn update_ink_deps(table: &mut toml_edit::Table) {
99+
for (key, value) in table.iter_mut() {
100+
if let toml_edit::Item::Value(Value::InlineTable(dep_table)) = value {
101+
// Only modify if it's a table and contains a version
102+
if dep_table.contains_key("version") {
103+
// Check package name if specified, otherwise use the key
104+
let dep_name = dep_table
105+
.get("package")
106+
.and_then(|v| v.as_str())
107+
.unwrap_or(&*key);
108+
109+
if dep_name.starts_with("ink_") || dep_name == "ink" {
110+
dep_table.insert(
111+
"git",
112+
Value::String(Formatted::new(REPO.parse().unwrap())),
113+
);
114+
}
115+
}
116+
}
117+
}
118+
}
83119

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

92-
fn verbose(&self) -> bool {
93-
true
124+
if let Some(dev_deps) = doc
125+
.get_mut("dev-dependencies")
126+
.and_then(|d| d.as_table_mut())
127+
{
128+
update_ink_deps(dev_deps);
129+
}
130+
131+
fs::write(cargo_path, doc.to_string())?;
132+
Ok(())
94133
}
95-
}
96134

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

116-
impl VisitMut for SeedExtractInjector {
153+
impl ContractVisitor for SeedExtractInjector {
154+
fn input_directory(&self) -> PathBuf {
155+
self.contract_path.to_path_buf()
156+
}
157+
158+
fn output_directory(&self) -> PathBuf {
159+
let path = match &self.compiled_path {
160+
None => {
161+
// Create a new directory that is not tied to `TempDir`
162+
let dir = std::env::temp_dir().join("phink_seedgen");
163+
fs::create_dir_all(&dir).expect("Failed to create directory");
164+
dir
165+
}
166+
Some(contract) => contract.to_path_buf(),
167+
};
168+
if self.verbose() {
169+
println!("Using {path:?} for contract output");
170+
}
171+
path
172+
}
173+
174+
fn verbose(&self) -> bool {
175+
true
176+
}
177+
}
178+
179+
impl VisitMut for &SeedExtractInjector {
117180
fn visit_item_mut(&mut self, item: &mut Item) {
118181
match item {
119182
Item::Fn(f) => self.visit_item_fn_mut(f),
@@ -127,7 +190,7 @@ impl VisitMut for SeedExtractInjector {
127190
fn visit_item_impl_mut(&mut self, i: &mut ItemImpl) {
128191
for item in &mut i.items {
129192
if let ImplItem::Fn(method) = item {
130-
if Self::has_ink_message_attribute(method) {
193+
if SeedExtractInjector::has_ink_message_attribute(method) {
131194
let fn_name = &method.sig.ident;
132195
// If the visited function isn't an invariant
133196
if !fn_name.to_string().starts_with("phink_") {
@@ -195,7 +258,7 @@ impl VisitMut for SeedExtractInjector {
195258

196259
#[cfg(test)]
197260
mod tests {
198-
use crate::instrumenter::seeder::SeedExtractInjector;
261+
use crate::instrumenter::seedgen::SeedExtractInjector;
199262
use quote::quote;
200263
use std::path::PathBuf;
201264
use syn::{
@@ -243,7 +306,7 @@ mod tests {
243306
if data.chars().nth(1).unwrap() == 'u' {
244307
if data.chars().nth(2).unwrap() == 'z' {
245308
if data.chars().nth(3).unwrap() == 'z' {
246-
self.forbidden_number = 69;
309+
self.forbidden_number = 42;
247310
}
248311
}
249312
}
@@ -282,15 +345,15 @@ mod tests {
282345
#[cfg(feature = "phink")]
283346
#[ink(message)]
284347
pub fn phink_assert_dangerous_number(&self) {
285-
let forbidden_number = 69;
348+
let forbidden_number = 42;
286349
assert_ne!(self.forbidden_number, forbidden_number);
287350
}
288351
}
289352
}"#;
290353

291354
let mut syntax_tree: File = parse_str(input_code).expect("Failed to parse code");
292355
let mut seed_injector =
293-
SeedExtractInjector::new(&PathBuf::from("sample/dummy"), None).unwrap();
356+
&SeedExtractInjector::new(&PathBuf::from("sample/dummy"), None).unwrap();
294357
seed_injector.visit_file_mut(&mut syntax_tree);
295358

296359
let generated_code = quote!(#syntax_tree).to_string();

0 commit comments

Comments
 (0)