diff --git a/noir_by_example/.gitignore b/noir_by_example/.gitignore index 48ebc7e..eb5a316 100644 --- a/noir_by_example/.gitignore +++ b/noir_by_example/.gitignore @@ -1,3 +1 @@ target -Prover.toml -Verifier.toml diff --git a/noir_by_example/Cargo.lock b/noir_by_example/Cargo.lock index 1b3122b..e0a32f0 100644 --- a/noir_by_example/Cargo.lock +++ b/noir_by_example/Cargo.lock @@ -2,6 +2,10 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "generic_traits" +version = "0.1.0" + [[package]] name = "loops" version = "0.1.0" diff --git a/noir_by_example/Cargo.toml b/noir_by_example/Cargo.toml index a099589..7585070 100644 --- a/noir_by_example/Cargo.toml +++ b/noir_by_example/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "simple_macros/rust", - "loops/rust" + "loops/rust", + "generic_traits/rust" ] resolver = "1" diff --git a/noir_by_example/Nargo.toml b/noir_by_example/Nargo.toml index ee8bc2d..f862b38 100644 --- a/noir_by_example/Nargo.toml +++ b/noir_by_example/Nargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ "simple_macros/noir", - "loops/noir" + "loops/noir", + "generic_traits/noir" ] diff --git a/noir_by_example/generic_traits/noir/Nargo.toml b/noir_by_example/generic_traits/noir/Nargo.toml new file mode 100644 index 0000000..976cb44 --- /dev/null +++ b/noir_by_example/generic_traits/noir/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "generic_traits" +type = "bin" +authors = [""] +compiler_version = ">=0.35.0" + +[dependencies] \ No newline at end of file diff --git a/noir_by_example/generic_traits/noir/Prover.toml b/noir_by_example/generic_traits/noir/Prover.toml new file mode 100644 index 0000000..1aad98a --- /dev/null +++ b/noir_by_example/generic_traits/noir/Prover.toml @@ -0,0 +1,2 @@ +cost = "15" +start = "20" diff --git a/noir_by_example/generic_traits/noir/src/main.nr b/noir_by_example/generic_traits/noir/src/main.nr new file mode 100644 index 0000000..588ed60 --- /dev/null +++ b/noir_by_example/generic_traits/noir/src/main.nr @@ -0,0 +1,84 @@ +// DCI (Data, Context, Interaction) example - keeps objects simple, and binds specific functions to them in their context of use + +// ### DATA ### +struct ValueAccount { + balance: u32, +} + +// Primitive functions for data object +trait ValueFunctions { + fn add(&mut self, val: u32); + fn sub(&mut self, val: u32); +} + +impl ValueFunctions for ValueAccount { + fn add(&mut self, val: u32) { + self.balance += val; + } + fn sub(&mut self, val: u32) { + self.balance -= val; + } +} + +// ### CONTEXT ### + +// Example purchase context +// Buyer role +trait Buyer { + fn make_payment(&mut self, val: u32); +} + +// impl Buyer for T { // This works in rust +// impl Buyer for T where T:ValueFunctions { // Valid noir, specifies value functions, +// but gives compilation error on line that calls Buyer function make_payment +impl Buyer for ValueAccount { // context needs to be aware of data object (not just trait) + fn make_payment(&mut self, val: u32) { + self.sub(val); + } +} + +// Seller role +trait Seller { + fn receive_payment(&mut self, val: u32); +} + +impl Seller for ValueAccount { + fn receive_payment(&mut self, val: u32) { + self.add(val); + } +} + +// Define the purchase context with its generic roles +struct PurchaseContext { + // Roles + buyer: &mut B, + seller: &mut S, +} + +impl PurchaseContext where B: Buyer, S: Seller { + fn do_purchase(&mut self, amount: u32) { + self.buyer.make_payment(amount); // Compilation error "No matching impl found for `B: ValueFunctions`" when Buyer uses generic T + // self.buyer.receive_payment(amount); // correct compiler error, buyer role cannot receive_payment + self.seller.receive_payment(amount); + } +} + +// ### INTERACTION ### + +fn main(start: u32, cost: pub u32) { + assert(start >= cost, "Not enough start balance"); + // create data object, can use all primitive functions + let mut acc1: ValueAccount = ValueAccount { balance: start }; + let mut acc2: ValueAccount = ValueAccount { balance: start }; + + // create context for data object as + let mut purchase_context: PurchaseContext = PurchaseContext:: { buyer: &mut acc1, seller: &mut acc2 }; + purchase_context.do_purchase(cost); + assert(acc1.balance == start - cost, "Didn't spend"); + assert(acc2.balance == start + cost, "Didn't receive"); +} + +#[test] +fn test_generic_traits() { + main(10, 5); +} diff --git a/noir_by_example/generic_traits/rust/Cargo.toml b/noir_by_example/generic_traits/rust/Cargo.toml new file mode 100644 index 0000000..3a2eda8 --- /dev/null +++ b/noir_by_example/generic_traits/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "generic_traits" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/noir_by_example/generic_traits/rust/src/main.rs b/noir_by_example/generic_traits/rust/src/main.rs new file mode 100644 index 0000000..f8705cc --- /dev/null +++ b/noir_by_example/generic_traits/rust/src/main.rs @@ -0,0 +1,110 @@ +// DCI (Data, Context, Interaction) example - keeps objects simple, and binds specific functions to them in their context of use + + +// ### DATA ### +struct ValueAccount { + balance: u32, +} + +// Primitive functions for data object +trait ValueFunctions { + fn add(&mut self, val: u32); + fn sub(&mut self, val: u32); +} + +impl ValueFunctions for ValueAccount { + fn add(&mut self, val: u32) { + self.balance += val; + } + fn sub(&mut self, val: u32) { + self.balance -= val; + } +} + + +// ### CONTEXT ### + +// Example purchase context +// Buyer role +trait Buyer { + fn make_payment(&mut self, val: u32); +} + +impl Buyer for T { + fn make_payment(&mut self, val: u32) { + self.sub(val); + } +} + +// Seller role +trait Seller { + fn receive_payment(&mut self, val: u32); +} + +impl Seller for T { + fn receive_payment(&mut self, val: u32) { + self.add(val); + } +} + +// Define the purchase context with its generic roles +struct PurchaseContext<'b, 's, B, S> { + // Roles + buyer: &'b mut B, + seller: &'s mut S, +} + +impl PurchaseContext<'_, '_, B, S> where B: Buyer, S: Seller { + fn do_purchase(&mut self, amount: u32) { + self.buyer.make_payment(amount); + // self.buyer.receive_payment(amount); // correct compiler error, buyer role cannot receive_payment + self.seller.receive_payment(amount); + } +} + + +// ### INTERACTION ### + +fn purchase_interaction(start: u32, cost: u32) { + assert!(start >= cost, "Not enough start balance"); + // create data object, can use all primitive functions + let mut acc1: ValueAccount = ValueAccount { balance: start }; + let mut acc2: ValueAccount = ValueAccount { balance: start }; + + // create context for data object as + let mut purchase_context: PurchaseContext = + PurchaseContext:: { + buyer: &mut acc1, seller: &mut acc2 + }; + purchase_context.do_purchase(cost); + assert!(acc1.balance == start - cost, "Didn't spend"); + assert!(acc2.balance == start + cost, "Didn't receive"); +} + +// ## Parse args + +fn parse_args(args: Vec) -> (u32, u32) { + // Check if the correct number of arguments is provided + if args.len() != 3 { + eprintln!("Usage: {} ", args[0]); + std::process::exit(1); + } + + let start: u32 = args[1].parse().expect("Please provide a valid number for start"); + let cost: u32 = args[2].parse().expect("Please provide a valid number for cost"); + + (start, cost) +} + +use std::env; + +fn main() { + // Collect command-line arguments into a vector + let (start, cost) = parse_args(env::args().collect()); + purchase_interaction(start, cost); +} + +#[test] +fn test_generic_traits() { + purchase_interaction(10, 5); +} diff --git a/noir_by_example/loops/noir/Prover.toml b/noir_by_example/loops/noir/Prover.toml new file mode 100644 index 0000000..2b3fe8b --- /dev/null +++ b/noir_by_example/loops/noir/Prover.toml @@ -0,0 +1 @@ +loop_length = "5" diff --git a/noir_by_example/simple_macros/noir/Prover.toml b/noir_by_example/simple_macros/noir/Prover.toml new file mode 100644 index 0000000..e69de29