diff --git a/docs/docs/developers/contracts/references/storage/main.md b/docs/docs/developers/contracts/references/storage/main.md index b59350ee35f..89ddac2433b 100644 --- a/docs/docs/developers/contracts/references/storage/main.md +++ b/docs/docs/developers/contracts/references/storage/main.md @@ -59,7 +59,6 @@ type = "contract" [dependencies] aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/aztec" } -safe_math = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/safe-math"} authwit={ git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/authwit"} compressed_string = {git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/compressed-string"} ``` diff --git a/docs/docs/developers/contracts/resources/dependencies.md b/docs/docs/developers/contracts/resources/dependencies.md index fbee858c1da..02debb3a898 100644 --- a/docs/docs/developers/contracts/resources/dependencies.md +++ b/docs/docs/developers/contracts/resources/dependencies.md @@ -44,14 +44,6 @@ protocol_types = { git="https://github.com/AztecProtocol/aztec-packages/", tag=" This library contains types that are used in the Aztec protocol. Find it on [GitHub](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-protocol-circuits/src/crates/types/src). -## Safe math - -```toml -safe_math = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/safe-math" } -``` - -This is a library for safe arithmetic, similar to OpenZeppelin's safe math library. Find it on [GitHub](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/aztec-nr/safe-math). - ## Value note ```toml diff --git a/docs/docs/developers/contracts/setup.md b/docs/docs/developers/contracts/setup.md index 7ea682a2bc5..efe8bd57a3d 100644 --- a/docs/docs/developers/contracts/setup.md +++ b/docs/docs/developers/contracts/setup.md @@ -57,7 +57,7 @@ Your folder should look like: Before writing the contracts, we must add the aztec.nr library. This adds smart contract utility functions for interacting with the Aztec network. -3. Finally, add relevant aztec-nr dependencies that you might use such as `aztec.nr`, `value_note` and `safe_math` libraries. +3. Finally, add relevant aztec-nr dependencies that you might use such as `aztec.nr` and `value_note` libraries. Open Nargo.toml that is in the `contracts/example_contract` folder, and add the dependency section as follows @@ -74,7 +74,6 @@ aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_ # Utility dependencies value_note = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/value-note"} -safe_math = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/safe-math"} ``` :::info diff --git a/docs/docs/developers/tutorials/writing_dapp/contract_deployment.md b/docs/docs/developers/tutorials/writing_dapp/contract_deployment.md index 79cb2293882..8cbaecbc608 100644 --- a/docs/docs/developers/tutorials/writing_dapp/contract_deployment.md +++ b/docs/docs/developers/tutorials/writing_dapp/contract_deployment.md @@ -21,7 +21,6 @@ Then, open the `contracts/token/Nargo.toml` configuration file, and add the `azt [dependencies] aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/aztec" } authwit = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/authwit"} -safe_math = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/safe-math"} compressed_string = {git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/compressed-string"} ``` diff --git a/docs/docs/developers/tutorials/writing_token_contract.md b/docs/docs/developers/tutorials/writing_token_contract.md index 1d6f4098b3d..a232e40fa8b 100644 --- a/docs/docs/developers/tutorials/writing_token_contract.md +++ b/docs/docs/developers/tutorials/writing_token_contract.md @@ -59,7 +59,6 @@ type = "contract" [dependencies] aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/aztec" } -safe_math = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/safe-math"} authwit={ git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/authwit"} compressed_string = {git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="noir-projects/aztec-nr/compressed-string"} ``` diff --git a/noir-projects/aztec-nr/Nargo.toml b/noir-projects/aztec-nr/Nargo.toml index 8edaa66f408..b799d8221ef 100644 --- a/noir-projects/aztec-nr/Nargo.toml +++ b/noir-projects/aztec-nr/Nargo.toml @@ -6,7 +6,6 @@ members = [ "compressed-string", "easy-private-state", "field-note", - "safe-math", "slow-updates-tree", "value-note", ] diff --git a/noir-projects/aztec-nr/README.md b/noir-projects/aztec-nr/README.md index 31b4527801c..3a944619c61 100644 --- a/noir-projects/aztec-nr/README.md +++ b/noir-projects/aztec-nr/README.md @@ -44,7 +44,6 @@ aztec = { git = "https://github.com/AztecProtocol/aztec-nr", tag = "master" , di # Optional libraries easy_private_state = { git = "https://github.com/AztecProtocol/aztec-nr", tag = "master" , directory = "easy-private-state" } -safe_math = { git = "https://github.com/AztecProtocol/aztec-nr", tag = "master" , directory = "safe-math" } value_note = { git = "https://github.com/AztecProtocol/aztec-nr", tag = "master" , directory = "value-note" } ``` diff --git a/noir-projects/aztec-nr/aztec/src/note/note_getter.nr b/noir-projects/aztec-nr/aztec/src/note/note_getter.nr index 4591211c4de..23237bb805f 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_getter.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_getter.nr @@ -1,19 +1,15 @@ use dep::std::option::Option; use dep::protocol_types::{ constants::{ - MAX_READ_REQUESTS_PER_CALL, - GET_NOTE_ORACLE_RETURN_LENGTH, - GET_NOTES_ORACLE_RETURN_LENGTH, - MAX_NOTES_PER_PAGE, - VIEW_NOTE_ORACLE_RETURN_LENGTH, - }, + MAX_READ_REQUESTS_PER_CALL, GET_NOTE_ORACLE_RETURN_LENGTH, GET_NOTES_ORACLE_RETURN_LENGTH, + MAX_NOTES_PER_PAGE, VIEW_NOTE_ORACLE_RETURN_LENGTH +} }; use crate::context::PrivateContext; use crate::note::{ note_getter_options::{NoteGetterOptions, Select, Sort, SortOrder, Comparator, NoteStatus}, - note_interface::NoteInterface, - note_viewer_options::NoteViewerOptions, - utils::compute_note_hash_for_consumption, + note_interface::NoteInterface, note_viewer_options::NoteViewerOptions, + utils::compute_note_hash_for_consumption }; use crate::oracle; @@ -60,7 +56,7 @@ fn check_notes_order( for i in 0..sorts.len { let sort = sorts.get_unchecked(i).unwrap_unchecked(); let eq = fields_0[sort.field_index] == fields_1[sort.field_index]; - let lt = fields_0[sort.field_index] as u120 < fields_1[sort.field_index] as u120; + let lt = fields_0[sort.field_index].lt(fields_1[sort.field_index]); if sort.order == SortOrder.ASC { assert(eq | lt, "Return notes not sorted in ascending order."); } else if !eq { @@ -195,7 +191,7 @@ unconstrained pub fn view_notes( unconstrained fn flatten_options( selects: BoundedVec, N>, sorts: BoundedVec, N> -) -> (u8, [u8; N], [Field; N], [u3; N], [u8; N], [u2; N]) { +) -> (u8, [u8; N], [Field; N], [u8; N], [u8; N], [u8; N]) { let mut num_selects = 0; let mut select_by = [0; N]; let mut select_values = [0; N]; diff --git a/noir-projects/aztec-nr/aztec/src/note/note_getter_options.nr b/noir-projects/aztec-nr/aztec/src/note/note_getter_options.nr index 8b7dc2fe706..bb013ef946e 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_getter_options.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_getter_options.nr @@ -1,16 +1,14 @@ use dep::std::option::Option; -use dep::protocol_types::{ - constants::MAX_READ_REQUESTS_PER_CALL, -}; +use dep::protocol_types::{constants::MAX_READ_REQUESTS_PER_CALL}; use crate::note::note_interface::NoteInterface; struct ComparatorEnum { - EQ: u3, - NEQ: u3, - LT: u3, - LTE: u3, - GT: u3, - GTE: u3, + EQ: u8, + NEQ: u8, + LT: u8, + LTE: u8, + GT: u8, + GTE: u8, } global Comparator = ComparatorEnum { @@ -25,18 +23,18 @@ global Comparator = ComparatorEnum { struct Select { field_index: u8, value: Field, - comparator: u3, + comparator: u8, } impl Select { - pub fn new(field_index: u8, value: Field, comparator: u3) -> Self { + pub fn new(field_index: u8, value: Field, comparator: u8) -> Self { Select { field_index, value, comparator } } } struct SortOrderEnum { - DESC: u2, - ASC: u2, + DESC: u8, + ASC: u8, } global SortOrder = SortOrderEnum { @@ -46,18 +44,18 @@ global SortOrder = SortOrderEnum { struct Sort { field_index: u8, - order: u2, + order: u8, } impl Sort { - pub fn new(field_index: u8, order: u2) -> Self { + pub fn new(field_index: u8, order: u8) -> Self { Sort { field_index, order } } } struct NoteStatusEnum { - ACTIVE: u2, - ACTIVE_OR_NULLIFIED: u2, + ACTIVE: u8, + ACTIVE_OR_NULLIFIED: u8, } global NoteStatus = NoteStatusEnum { @@ -81,7 +79,7 @@ struct NoteGetterOptions { offset: u32, filter: fn ([Option; MAX_READ_REQUESTS_PER_CALL], FILTER_ARGS) -> [Option; MAX_READ_REQUESTS_PER_CALL], filter_args: FILTER_ARGS, - status: u2, + status: u8, } // docs:end:NoteGetterOptions @@ -99,24 +97,24 @@ impl NoteGetterOptions { offset: 0, filter: return_all_notes, filter_args: 0, - status: NoteStatus.ACTIVE, + status: NoteStatus.ACTIVE } } // This function initializes a NoteGetterOptions with a filter, which takes the notes returned from the database and filter_args as its parameters. // `filter_args` allows you to provide additional data or context to the custom filter. pub fn with_filter( - filter: fn ([Option; MAX_READ_REQUESTS_PER_CALL], FILTER_ARGS) -> [Option; MAX_READ_REQUESTS_PER_CALL], - filter_args: FILTER_ARGS, + filter: fn([Option; MAX_READ_REQUESTS_PER_CALL], FILTER_ARGS) -> [Option; MAX_READ_REQUESTS_PER_CALL], + filter_args: FILTER_ARGS ) -> Self where Note: NoteInterface { - NoteGetterOptions { + NoteGetterOptions { selects: BoundedVec::new(Option::none()), sorts: BoundedVec::new(Option::none()), limit: MAX_READ_REQUESTS_PER_CALL as u32, offset: 0, filter, filter_args, - status: NoteStatus.ACTIVE, + status: NoteStatus.ACTIVE } } @@ -124,14 +122,14 @@ impl NoteGetterOptions { // It takes a field_index indicating which field to select, // a value representing the specific value to match in that field, and // a comparator (For possible values of comparators, please see the Comparator enum above) - pub fn select(&mut self, field_index: u8, value: Field, comparator: Option) -> Self { + pub fn select(&mut self, field_index: u8, value: Field, comparator: Option) -> Self { self.selects.push(Option::some(Select::new(field_index, value, comparator.unwrap_or(Comparator.EQ)))); *self } // This method adds a `Sort` criterion to the options. // It takes a field_index indicating which field to sort by and an order (SortOrder) to determine the sorting direction. - pub fn sort(&mut self, field_index: u8, order: u2) -> Self { + pub fn sort(&mut self, field_index: u8, order: u8) -> Self { self.sorts.push(Option::some(Sort::new(field_index, order))); *self } @@ -150,7 +148,7 @@ impl NoteGetterOptions { } // This method sets the status value, which determines whether to retrieve active or nullified notes. - pub fn set_status(&mut self, status: u2) -> Self { + pub fn set_status(&mut self, status: u8) -> Self { self.status = status; *self } diff --git a/noir-projects/aztec-nr/aztec/src/note/note_viewer_options.nr b/noir-projects/aztec-nr/aztec/src/note/note_viewer_options.nr index d778927e744..a291b8f8c5d 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_viewer_options.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_viewer_options.nr @@ -1,8 +1,6 @@ use dep::std::option::Option; use crate::note::note_getter_options::{Select, Sort, Comparator, NoteStatus}; -use dep::protocol_types::{ - constants::MAX_NOTES_PER_PAGE, -}; +use dep::protocol_types::{constants::MAX_NOTES_PER_PAGE}; use crate::note::note_interface::NoteInterface; // docs:start:NoteViewerOptions @@ -11,7 +9,7 @@ struct NoteViewerOptions { sorts: BoundedVec, N>, limit: u32, offset: u32, - status: u2, + status: u8, } // docs:end:NoteViewerOptions @@ -22,7 +20,7 @@ impl NoteViewerOptions { sorts: BoundedVec::new(Option::none()), limit: MAX_NOTES_PER_PAGE as u32, offset: 0, - status: NoteStatus.ACTIVE, + status: NoteStatus.ACTIVE } } @@ -30,12 +28,12 @@ impl NoteViewerOptions { // It takes a field_index indicating which field to select, // a value representing the specific value to match in that field, and // a comparator (For possible values of comparators, please see the Comparator enum from note_getter_options) - pub fn select(&mut self, field_index: u8, value: Field, comparator: Option) -> Self { + pub fn select(&mut self, field_index: u8, value: Field, comparator: Option) -> Self { self.selects.push(Option::some(Select::new(field_index, value, comparator.unwrap_or(Comparator.EQ)))); *self } - pub fn sort(&mut self, field_index: u8, order: u2) -> Self { + pub fn sort(&mut self, field_index: u8, order: u8) -> Self { self.sorts.push(Option::some(Sort::new(field_index, order))); *self } @@ -52,7 +50,7 @@ impl NoteViewerOptions { } // This method sets the status value, which determines whether to retrieve active or nullified notes. - pub fn set_status(&mut self, status: u2) -> Self { + pub fn set_status(&mut self, status: u8) -> Self { self.status = status; *self } diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index 42b2f356ec8..6b91b35392e 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -1,13 +1,7 @@ use dep::std::option::Option; -use crate::note::{ - note_header::NoteHeader, - note_interface::NoteInterface, -}; +use crate::note::{note_header::NoteHeader, note_interface::NoteInterface}; -use dep::protocol_types::{ - address::AztecAddress, - utils::arr_copy_slice, -}; +use dep::protocol_types::{address::AztecAddress, utils::arr_copy_slice}; #[oracle(notifyCreatedNote)] fn notify_created_note_oracle( @@ -39,12 +33,12 @@ fn get_notes_oracle( _num_selects: u8, _select_by: [u8; N], _select_values: [Field; N], - _select_comparators: [u3; N], + _select_comparators: [u8; N], _sort_by: [u8; N], - _sort_order: [u2; N], + _sort_order: [u8; N], _limit: u32, _offset: u32, - _status: u2, + _status: u8, _return_size: u32, _placeholder_fields: [Field; S] ) -> [Field; S] {} @@ -54,12 +48,12 @@ unconstrained fn get_notes_oracle_wrapper( num_selects: u8, select_by: [u8; N], select_values: [Field; N], - select_comparators: [u3; N], + select_comparators: [u8; N], sort_by: [u8; N], - sort_order: [u2; N], + sort_order: [u8; N], limit: u32, offset: u32, - status: u2, + status: u8, mut placeholder_fields: [Field; S] ) -> [Field; S] { let return_size = placeholder_fields.len() as u32; @@ -84,12 +78,12 @@ unconstrained pub fn get_notes( num_selects: u8, select_by: [u8; M], select_values: [Field; M], - select_comparators: [u3; M], + select_comparators: [u8; M], sort_by: [u8; M], - sort_order: [u2; M], + sort_order: [u8; M], limit: u32, offset: u32, - status: u2, + status: u8, mut placeholder_opt_notes: [Option; S], // TODO: Remove it and use `limit` to initialize the note array. placeholder_fields: [Field; NS], // TODO: Remove it and use `limit` to initialize the note array. _placeholder_note_length: [Field; N] // Turbofish hack? Compiler breaks calculating read_offset unless we add this parameter diff --git a/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr b/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr index e9f5c34dcd7..32c393ec941 100644 --- a/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr +++ b/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr @@ -1,13 +1,8 @@ use dep::aztec::{ - protocol_types::address::AztecAddress, - context::Context, - note::note_getter_options::NoteGetterOptions, - state_vars::PrivateSet, -}; -use dep::value_note::{ - filter::filter_notes_min_sum, - value_note::ValueNote, + protocol_types::address::AztecAddress, context::Context, + note::note_getter_options::NoteGetterOptions, state_vars::PrivateSet }; +use dep::value_note::{filter::filter_notes_min_sum, value_note::ValueNote}; struct EasyPrivateUint { context: Context, @@ -17,24 +12,14 @@ struct EasyPrivateUint { // Holds a note that can act similarly to an int. impl EasyPrivateUint { - pub fn new( - context: Context, - storage_slot: Field, - ) -> Self { + pub fn new(context: Context, storage_slot: Field) -> Self { assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); - let set = PrivateSet { - context, - storage_slot - }; - EasyPrivateUint { - context, - set, - storage_slot, - } + let set = PrivateSet { context, storage_slot }; + EasyPrivateUint { context, set, storage_slot } } // Very similar to `value_note::utils::increment`. - pub fn add(self, addend: u120, owner: AztecAddress) { + pub fn add(self, addend: u64, owner: AztecAddress) { // Creates new note for the owner. let mut addend_note = ValueNote::new(addend as Field, owner); @@ -45,13 +30,13 @@ impl EasyPrivateUint { } // Very similar to `value_note::utils::decrement`. - pub fn sub(self, subtrahend: u120, owner: AztecAddress) { + pub fn sub(self, subtrahend: u64, owner: AztecAddress) { // docs:start:get_notes let options = NoteGetterOptions::with_filter(filter_notes_min_sum, subtrahend as Field); let maybe_notes = self.set.get_notes(options); // docs:end:get_notes - let mut minuend: u120 = 0; + let mut minuend: u64 = 0; for i in 0..maybe_notes.len() { if maybe_notes[i].is_some() { let note = maybe_notes[i].unwrap_unchecked(); @@ -65,7 +50,7 @@ impl EasyPrivateUint { self.set.remove(note); // docs:end:remove - minuend += note.value as u120; + minuend += note.value as u64; } } diff --git a/noir-projects/aztec-nr/safe-math/Nargo.toml b/noir-projects/aztec-nr/safe-math/Nargo.toml deleted file mode 100644 index aaa75e68977..00000000000 --- a/noir-projects/aztec-nr/safe-math/Nargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "safe_math" -authors = [""] -compiler_version = ">=0.18.0" -type = "lib" - -[dependencies] -aztec = { path = "../aztec" } \ No newline at end of file diff --git a/noir-projects/aztec-nr/safe-math/src/lib.nr b/noir-projects/aztec-nr/safe-math/src/lib.nr deleted file mode 100644 index 79dd63eb4b8..00000000000 --- a/noir-projects/aztec-nr/safe-math/src/lib.nr +++ /dev/null @@ -1,3 +0,0 @@ -mod safe_u120; - -use crate::safe_u120::{SafeU120, SAFE_U120_SERIALIZED_LEN}; diff --git a/noir-projects/aztec-nr/safe-math/src/safe_u120.nr b/noir-projects/aztec-nr/safe-math/src/safe_u120.nr deleted file mode 100644 index fabd9d53a60..00000000000 --- a/noir-projects/aztec-nr/safe-math/src/safe_u120.nr +++ /dev/null @@ -1,341 +0,0 @@ -use dep::std::cmp::Eq; -use dep::aztec::protocol_types::traits::{Deserialize, Serialize}; - -struct SafeU120 { - value: u120, -} - -impl Eq for SafeU120 { - fn eq( - self: Self, - other: Self - ) -> bool { - self.value == other.value - } -} - -global SAFE_U120_SERIALIZED_LEN: Field = 1; - -impl Serialize for SafeU120 { - fn serialize(value: SafeU120) -> [Field; SAFE_U120_SERIALIZED_LEN] { - [value.value as Field] - } -} - -impl Deserialize for SafeU120 { - // This is safe when reading from storage IF only correct safeu120 was written to storage - fn deserialize(fields: [Field; SAFE_U120_SERIALIZED_LEN]) -> SafeU120 { - let value = fields[0] as u120; - SafeU120 { value } - } -} - -// Holds an integer in public storage -impl SafeU120 { - pub fn min() -> Self { - Self { - value: 0 - } - } - - pub fn max() -> Self { - Self { - value: 0xffffffffffffffffffffffffffffff - } - } - - pub fn new( - value: Field, - ) -> Self { - // Check that it actually will fit. Spending a lot of constraints here :grimacing: - let bytes = value.to_be_bytes(32); - for i in 0..17 { - assert(bytes[i] == 0, "Value too large for SafeU120"); - } - - let value = value as u120; - Self { value } - } - - pub fn is_zero( - self: Self, - ) -> bool { - self.value == 0 - } - - pub fn lt(self: Self, other: Self) -> bool { - self.value < other.value - } - - pub fn le(self: Self, other: Self) -> bool { - self.value <= other.value - } - - pub fn gt(self: Self, other: Self) -> bool { - self.value > other.value - } - - pub fn ge(self: Self, other: Self) -> bool { - self.value >= other.value - } - - pub fn sub( - self: Self, - b: Self, - ) -> Self { - assert(self.value >= b.value, "Underflow"); - Self { - value: self.value - b.value - } - } - - pub fn add( - self: Self, - b: Self, - ) -> Self { - let c: u120 = self.value + b.value; - assert(c >= self.value, "Overflow"); - Self { - value: c - } - } - - pub fn mul( - self: Self, - b: Self, - ) -> Self { - let c: u120 = self.value * b.value; - if !b.is_zero() { - assert(c / b.value == self.value, "Overflow"); - } - Self { - value: c - } - } - - pub fn div( - self: Self, - b: Self, - ) -> Self { - assert(!b.is_zero(), "Divide by zero"); - Self { - value: self.value / b.value - } - } - - pub fn mul_div( - self: Self, - b: Self, - divisor: Self - ) -> Self { - self.mul(b).div(divisor) - } - - pub fn mul_div_up( - self: Self, - b: Self, - divisor: Self - ) -> Self { - let c = self.mul(b); - assert(!divisor.is_zero(), "Divide by zero"); - let adder = ((self.value * b.value % divisor.value) as u120 > 0) as u120; - c.div(divisor).add(Self {value: adder}) - } - - // todo: implement mul_div with 240 bit intermediate values. -} - -#[test] -fn test_init() { - let a = SafeU120::new(1); - assert(a.value == 1); -} - -#[test] -fn test_init_max() { - let a = SafeU120::max(); - assert(a.value == 0xffffffffffffffffffffffffffffff); -} - -#[test] -fn test_init_min() { - let a = SafeU120::min(); - assert(a.value == 0); -} - -#[test] -fn test_is_zero() { - let a = SafeU120::min(); - assert(a.value == 0); - assert(a.is_zero() == true); -} - -#[test] -fn test_eq() { - let a = SafeU120::new(1); - let b = SafeU120::new(1); - assert(a.eq(b)); -} - -#[test] -fn test_lt() { - let a = SafeU120::new(1); - let b = SafeU120::new(2); - assert(a.lt(b)); - assert(b.lt(a) == false); -} - -#[test] -fn test_le() { - let a = SafeU120::new(2); - let b = SafeU120::new(2); - let c = SafeU120::new(5); - assert(a.le(b)); - assert(a.le(c)); - assert(c.le(a) == false); -} - -#[test] -fn test_gt() { - let a = SafeU120::new(1); - let b = SafeU120::new(2); - assert(b.gt(a)); - assert(a.gt(b) == false); -} - -#[test] -fn test_ge() { - let a = SafeU120::new(2); - let b = SafeU120::new(2); - let c = SafeU120::new(5); - assert(a.ge(b)); - assert(a.ge(c) == false); - assert(c.ge(a)); -} - -#[test(should_fail)] -fn test_init_too_large() { - let b = SafeU120::max().value as Field + 1; // max + 1 - let _a = SafeU120::new(b); -} - -#[test] -fn test_add() { - let a = SafeU120::new(1); - let b = SafeU120::new(2); - let c = SafeU120::add(a, b); - assert(c.value == 3); -} - -#[test(should_fail)] -fn test_add_overflow() { - let a = SafeU120::max(); - let b = SafeU120::new(1); - let _c = SafeU120::add(a, b); -} - -#[test] -fn test_sub() { - let a = SafeU120::new(2); - let b = SafeU120::new(1); - let c = SafeU120::sub(a, b); - assert(c.value == 1); -} - -#[test(should_fail)] -fn test_sub_underflow() { - let a = SafeU120::new(1); - let b = SafeU120::new(2); - let _c = SafeU120::sub(a, b); -} - -#[test] -fn test_mul() { - let a = SafeU120::new(2); - let b = SafeU120::new(3); - let c = SafeU120::mul(a, b); - assert(c.value == 6); -} - -#[test(should_fail)] -fn test_mul_overflow() { - let a = SafeU120::max(); - let b = SafeU120::new(2); - let _c = SafeU120::mul(a, b); -} - -#[test] -fn test_div() { - let a = SafeU120::new(6); - let b = SafeU120::new(3); - let c = SafeU120::div(a, b); - assert(c.value == 2); -} - -#[test(should_fail)] -fn test_div_by_zero() { - let a = SafeU120::new(6); - let b = SafeU120::new(0); - let _c = SafeU120::div(a, b); -} - -#[test] -fn test_mul_div() { - let a = SafeU120::new(6); - let b = SafeU120::new(3); - let c = SafeU120::new(2); - let d = SafeU120::mul_div(a, b, c); - assert(d.value == 9); -} - -#[test(should_fail)] -fn test_mul_div_zero_divisor() { - let a = SafeU120::new(6); - let b = SafeU120::new(3); - let c = SafeU120::new(0); - let _d = SafeU120::mul_div(a, b, c); -} - -#[test(should_fail)] -fn test_mul_div_ghost_overflow() { - let a = SafeU120::max(); - let b = SafeU120::new(2); - let c = SafeU120::new(4); - let _d = SafeU120::mul_div(a, b, c); -} - -#[test] -fn test_mul_div_up_rounding() { - let a = SafeU120::new(6); - let b = SafeU120::new(3); - let c = SafeU120::new(5); - let d = SafeU120::mul_div_up(a, b, c); - assert(d.value == 4); -} - -#[test] -fn test_mul_div_up_non_rounding() { - let a = SafeU120::new(6); - let b = SafeU120::new(3); - let c = SafeU120::new(2); - let d = SafeU120::mul_div_up(a, b, c); - assert(d.value == 9); -} - -#[test(should_fail)] -fn test_mul_div_up_ghost_overflow() { - let a = SafeU120::max(); - let b = SafeU120::new(2); - let c = SafeU120::new(9); - let _d = SafeU120::mul_div_up(a, b, c); -} - -// It should not be possible for us to overflow `mul_div_up` through the adder, since that require the divisor to be 1 -// since we otherwise would not be at the max value. If divisor is 1, adder is 0. -#[test(should_fail)] -fn test_mul_div_up_zero_divisor() { - let a = SafeU120::new(6); - let b = SafeU120::new(3); - let c = SafeU120::new(0); - let _d = SafeU120::mul_div_up(a, b, c); -} diff --git a/noir-projects/aztec-nr/slow-updates-tree/src/slow_map.nr b/noir-projects/aztec-nr/slow-updates-tree/src/slow_map.nr index bbfa414e523..641abcc16b4 100644 --- a/noir-projects/aztec-nr/slow-updates-tree/src/slow_map.nr +++ b/noir-projects/aztec-nr/slow-updates-tree/src/slow_map.nr @@ -1,20 +1,16 @@ -use crate::{ - leaf::Leaf, - slow_update_proof::SlowUpdateProof, -}; +use crate::{leaf::Leaf, slow_update_proof::SlowUpdateProof}; use dep::aztec::{ - context::Context, - oracle::storage::{storage_read, storage_write}, - protocol_types::traits::{Serialize, Deserialize}, + context::Context, oracle::storage::{storage_read, storage_write}, + protocol_types::traits::{Serialize, Deserialize} }; use dep::std::hash::pedersen_hash; use dep::std::merkle::compute_merkle_root; // The epoch length is just a random number for now. -global EPOCH_LENGTH: u120 = 100; +global EPOCH_LENGTH: u64 = 100; fn compute_next_change(time: Field) -> Field { - ((time as u120 / EPOCH_LENGTH + 1) * EPOCH_LENGTH) as Field + ((time as u64 / EPOCH_LENGTH + 1) * EPOCH_LENGTH) as Field } // TODO(#4760): Rename slow updates to shared mutable and ideally move the impl to state-vars in aztec-nr. @@ -25,161 +21,153 @@ struct SlowMap { } impl SlowMap { - pub fn new( - context: Context, - storage_slot: Field - ) -> Self { - assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); - Self { - context, - storage_slot, + pub fn new(context: Context, storage_slot: Field) -> Self { + assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); + Self { context, storage_slot } + } + + pub fn read_root(self: Self) -> Leaf { + let fields = storage_read(self.storage_slot); + Leaf::deserialize(fields) } - } - - pub fn read_root(self: Self) -> Leaf { - let fields = storage_read(self.storage_slot); - Leaf::deserialize(fields) - } - - // Beware that the initial root could include much state that is not shown by the public storage! - pub fn initialize(self: Self, initial_root: Field) { - let mut root_object = self.read_root(); - assert(root_object.next_change == 0, "cannot initialize twice"); - root_object = Leaf { + + // Beware that the initial root could include much state that is not shown by the public storage! + pub fn initialize(self: Self, initial_root: Field) { + let mut root_object = self.read_root(); + assert(root_object.next_change == 0, "cannot initialize twice"); + root_object = Leaf { next_change: 0xffffffffffffffffffffffffffffff, before: initial_root, after: initial_root, }; - let fields = root_object.serialize(); - storage_write(self.storage_slot, fields); - } - - // Reads the "CURRENT" value of the root - pub fn current_root(self: Self) -> Field { - let time = self.context.public.unwrap().timestamp() as u120; - let root_object = self.read_root(); - if time <= root_object.next_change as u120 { - root_object.before - } else { - root_object.after + let fields = root_object.serialize(); + storage_write(self.storage_slot, fields); + } + + // Reads the "CURRENT" value of the root + pub fn current_root(self: Self) -> Field { + let time = self.context.public.unwrap().timestamp() as u64; + let root_object = self.read_root(); + if time <= root_object.next_change as u64 { + root_object.before + } else { + root_object.after + } } - } - - // docs:start:read_leaf_at - pub fn read_leaf_at(self: Self, key: Field) -> Leaf { - let derived_storage_slot = pedersen_hash([self.storage_slot, key]); - let fields = storage_read(derived_storage_slot); - Leaf::deserialize(fields) - } - // docs:end:read_leaf_at - - // docs:start:read_at - // Reads the "CURRENT" value of the leaf - pub fn read_at(self: Self, key: Field) -> Field { - let time = self.context.public.unwrap().timestamp() as u120; - let leaf = self.read_leaf_at(key); - if time <= leaf.next_change as u120 { - leaf.before - } else { - leaf.after + + // docs:start:read_leaf_at + pub fn read_leaf_at(self: Self, key: Field) -> Leaf { + let derived_storage_slot = pedersen_hash([self.storage_slot, key]); + let fields = storage_read(derived_storage_slot); + Leaf::deserialize(fields) } - } - // docs:end:read_at - - // Will update values in the "AFTER" tree - // - updates the leaf and root to follow current values, moving from after to before if - // needed. - // - checks that the provided merkle paths match state values - // - update the leaf and compute the net root - // Should only be used when updates from public are desired, since the hashing will be - // costly since done by sequencer. - pub fn update_at(self: Self, p: SlowUpdateProof) { - // The calling function should ensure that the index is within the tree. - // This must be done separately to ensure we are not constraining too tight here. - - let time = self.context.public.unwrap().timestamp() as u120; - let next_change = compute_next_change(time as Field); - - let mut root = self.read_root(); - let mut leaf = self.read_leaf_at(p.index); - - // Move leaf if needed - if time > leaf.next_change as u120 { - leaf.before = leaf.after; + // docs:end:read_leaf_at + + // docs:start:read_at + // Reads the "CURRENT" value of the leaf + pub fn read_at(self: Self, key: Field) -> Field { + let time = self.context.public.unwrap().timestamp() as u64; + let leaf = self.read_leaf_at(key); + if time <= leaf.next_change as u64 { + leaf.before + } else { + leaf.after + } } - - // Move root if needed - if time > root.next_change as u120 { - root.before = root.after; + // docs:end:read_at + + // Will update values in the "AFTER" tree + // - updates the leaf and root to follow current values, moving from after to before if + // needed. + // - checks that the provided merkle paths match state values + // - update the leaf and compute the net root + // Should only be used when updates from public are desired, since the hashing will be + // costly since done by sequencer. + pub fn update_at(self: Self, p: SlowUpdateProof) { + // The calling function should ensure that the index is within the tree. + // This must be done separately to ensure we are not constraining too tight here. + + let time = self.context.public.unwrap().timestamp() as u64; + let next_change = compute_next_change(time as Field); + + let mut root = self.read_root(); + let mut leaf = self.read_leaf_at(p.index); + + // Move leaf if needed + if time > leaf.next_change as u64 { + leaf.before = leaf.after; + } + + // Move root if needed + if time > root.next_change as u64 { + root.before = root.after; + } + + // Ensures that when before is active, it is not altered by this update + assert( + root.before == compute_merkle_root(leaf.before, p.index, p.before.sibling_path), "Before root don't match" + ); + + // Ensures that the provided sibling path is valid for the CURRENT "after" tree. + // Without this check, someone could provide a sibling path for a different tree + // and update the entire "after" tree at once, causing it to be out of sync with leaf storage. + assert( + root.after == compute_merkle_root(leaf.after, p.index, p.after.sibling_path), "After root don't match" + ); + + // Update the leaf + leaf.after = p.new_value; + leaf.next_change = next_change; + + // Update the after root + root.after = compute_merkle_root(leaf.after, p.index, p.after.sibling_path); + root.next_change = next_change; + + self.update_unsafe(p.index, leaf, root); } - // Ensures that when before is active, it is not altered by this update - assert( - root.before == compute_merkle_root(leaf.before, p.index, p.before.sibling_path), - "Before root don't match" - ); - - // Ensures that the provided sibling path is valid for the CURRENT "after" tree. - // Without this check, someone could provide a sibling path for a different tree - // and update the entire "after" tree at once, causing it to be out of sync with leaf storage. - assert( - root.after == compute_merkle_root(leaf.after, p.index, p.after.sibling_path), - "After root don't match" - ); - - // Update the leaf - leaf.after = p.new_value; - leaf.next_change = next_change; - - // Update the after root - root.after = compute_merkle_root(leaf.after, p.index, p.after.sibling_path); - root.next_change = next_change; - - self.update_unsafe(p.index, leaf, root); - } - - // A variation of `update_at` that skips the merkle-membership checks. - // To be used by a contract which has already checked the merkle-membership. - // This allows us to check the merkle-memberships in private and then update - // in public, limiting the cost of the update. - pub fn update_unsafe_at(self: Self, index: Field, leaf_value: Field, new_root: Field) { - // User must ensure that the checks from update_at is performed for safety - let time = self.context.public.unwrap().timestamp() as u120; - let next_change = compute_next_change(time as Field); - - let mut root = self.read_root(); - let mut leaf = self.read_leaf_at(index); - - // Move leaf if needed - if time > leaf.next_change as u120 { - leaf.before = leaf.after; - } - - // Move root if needed - if time > root.next_change as u120 { - root.before = root.after; + // A variation of `update_at` that skips the merkle-membership checks. + // To be used by a contract which has already checked the merkle-membership. + // This allows us to check the merkle-memberships in private and then update + // in public, limiting the cost of the update. + pub fn update_unsafe_at(self: Self, index: Field, leaf_value: Field, new_root: Field) { + // User must ensure that the checks from update_at is performed for safety + let time = self.context.public.unwrap().timestamp() as u64; + let next_change = compute_next_change(time as Field); + + let mut root = self.read_root(); + let mut leaf = self.read_leaf_at(index); + + // Move leaf if needed + if time > leaf.next_change as u64 { + leaf.before = leaf.after; + } + + // Move root if needed + if time > root.next_change as u64 { + root.before = root.after; + } + + // Update the leaf + leaf.after = leaf_value; + leaf.next_change = next_change; + + // Update the root + root.after = new_root; + root.next_change = next_change; + + self.update_unsafe(index, leaf, root); } - // Update the leaf - leaf.after = leaf_value; - leaf.next_change = next_change; - - // Update the root - root.after = new_root; - root.next_change = next_change; - - self.update_unsafe(index, leaf, root); - } - - // Updates the value in the in storage with no checks. - fn update_unsafe(self: Self, index: Field, leaf: Leaf, root: Leaf) { - let derived_storage_slot = pedersen_hash([self.storage_slot, index]); - let fields = leaf.serialize(); - storage_write(derived_storage_slot, fields); - - let fields = root.serialize(); - storage_write(self.storage_slot, fields); - } + // Updates the value in the in storage with no checks. + fn update_unsafe(self: Self, index: Field, leaf: Leaf, root: Leaf) { + let derived_storage_slot = pedersen_hash([self.storage_slot, index]); + let fields = leaf.serialize(); + storage_write(derived_storage_slot, fields); + + let fields = root.serialize(); + storage_write(self.storage_slot, fields); + } } /*pub fn compute_merkle_root(leaf: Field, index: Field, hash_path: [Field; N]) -> Field { diff --git a/noir-projects/aztec-nr/value-note/src/filter.nr b/noir-projects/aztec-nr/value-note/src/filter.nr index 56dd89a5ca9..9d8a51a33bf 100644 --- a/noir-projects/aztec-nr/value-note/src/filter.nr +++ b/noir-projects/aztec-nr/value-note/src/filter.nr @@ -7,12 +7,12 @@ pub fn filter_notes_min_sum( min_sum: Field ) -> [Option; MAX_READ_REQUESTS_PER_CALL] { let mut selected = [Option::none(); MAX_READ_REQUESTS_PER_CALL]; - let mut sum = 0; + let mut sum = U128::from_integer(0); for i in 0..notes.len() { - if notes[i].is_some() & (sum < min_sum as u120) { + if notes[i].is_some() & (sum < U128::from_integer(min_sum)) { let note = notes[i].unwrap_unchecked(); selected[i] = Option::some(note); - sum += note.value as u120; + sum += U128::from_integer(note.value); } } selected diff --git a/noir-projects/aztec-nr/value-note/src/utils.nr b/noir-projects/aztec-nr/value-note/src/utils.nr index af4eacb7680..03a2fd637f3 100644 --- a/noir-projects/aztec-nr/value-note/src/utils.nr +++ b/noir-projects/aztec-nr/value-note/src/utils.nr @@ -3,10 +3,7 @@ use dep::aztec::context::PrivateContext; use dep::aztec::note::note_getter_options::{NoteGetterOptions, SortOrder}; use dep::aztec::oracle::get_public_key::get_public_key; use dep::aztec::state_vars::PrivateSet; -use crate::{ - filter::filter_notes_min_sum, - value_note::{ValueNote, VALUE_NOTE_LEN}, -}; +use crate::{filter::filter_notes_min_sum, value_note::{ValueNote, VALUE_NOTE_LEN}}; use dep::aztec::protocol_types::address::AztecAddress; // Sort the note values (0th field) in descending order. @@ -40,7 +37,11 @@ pub fn decrement(balance: PrivateSet, amount: Field, owner: AztecAddr // equal `amount`. // // It returns the decremented amount, which should be less than or equal to max_amount. -pub fn decrement_by_at_most(balance: PrivateSet, max_amount: Field, owner: AztecAddress) -> Field { +pub fn decrement_by_at_most( + balance: PrivateSet, + max_amount: Field, + owner: AztecAddress +) -> Field { let options = create_note_getter_options_for_decreasing_balance(max_amount); let opt_notes = balance.get_notes(options); @@ -53,7 +54,7 @@ pub fn decrement_by_at_most(balance: PrivateSet, max_amount: Field, o // Add the change value back to the owner's balance. let mut change_value = 0; - if decremented as u120 > max_amount as u120 { + if max_amount.lt(decremented) { change_value = decremented - max_amount; decremented -= change_value; } diff --git a/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr b/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr index 7d5b997daa4..cc41a36f6c8 100644 --- a/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr +++ b/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr @@ -1,45 +1,32 @@ use dep::aztec::{ - protocol_types::{ - address::AztecAddress, - constants::{ - MAX_NOTES_PER_PAGE, - MAX_READ_REQUESTS_PER_CALL, - }, - }, + protocol_types::{address::AztecAddress, constants::{MAX_NOTES_PER_PAGE, MAX_READ_REQUESTS_PER_CALL}}, context::{PrivateContext, PublicContext, Context}, note::{ - note_getter_options::NoteGetterOptions, - note_viewer_options::NoteViewerOptions, - note_getter::view_notes, - }, - state_vars::PrivateSet, + note_getter_options::NoteGetterOptions, note_viewer_options::NoteViewerOptions, + note_getter::view_notes +}, + state_vars::PrivateSet }; use dep::std; -use dep::std::{ - option::Option, -}; -use dep::value_note::{ - value_note::ValueNote, -}; +use dep::std::{option::Option}; +use dep::value_note::{value_note::{ValueNote, VALUE_NOTE_LEN}}; struct Card { - strength: u16, - points: u16, + // We use u32s since u16s are unsupported + strength: u32, + points: u32, } impl Card { pub fn from_field(field: Field) -> Card { let value_bytes = field.to_le_bytes(32); - let strength = (value_bytes[0] as u16) + (value_bytes[1] as u16) * 256; - let points = (value_bytes[2] as u16) + (value_bytes[3] as u16) * 256; - Card { - strength, - points, - } + let strength = (value_bytes[0] as u32) + (value_bytes[1] as u32) * 256; + let points = (value_bytes[2] as u32) + (value_bytes[3] as u32) * 256; + Card { strength, points } } pub fn to_field(self) -> Field { - self.strength as Field + (self.points as Field)*65536 + self.strength as Field + (self.points as Field) * 65536 } pub fn serialize(self) -> [Field; 2] { @@ -60,30 +47,17 @@ struct CardNote { } impl CardNote { - fn new( - strength: u16, - points: u16, - owner: AztecAddress, - ) -> Self { - let card = Card { - strength, - points, - }; + fn new(strength: u32, points: u32, owner: AztecAddress) -> Self { + let card = Card { strength, points }; CardNote::from_card(card, owner) } pub fn from_card(card: Card, owner: AztecAddress) -> CardNote { - CardNote { - card, - note: ValueNote::new(card.to_field(), owner), - } + CardNote { card, note: ValueNote::new(card.to_field(), owner) } } pub fn from_note(note: ValueNote) -> CardNote { - CardNote { - card: Card::from_field(note.value), - note, - } + CardNote { card: Card::from_field(note.value), note } } } @@ -118,21 +92,13 @@ pub fn filter_cards( } impl Deck { - pub fn new( - context: Context, - storage_slot: Field, - ) -> Self { - let set = PrivateSet { - context, - storage_slot, - }; - Deck { - set - } + pub fn new(context: Context, storage_slot: Field) -> Self { + let set = PrivateSet { context, storage_slot }; + Deck { set } } - pub fn add_cards(&mut self, cards: [Card; N], owner: AztecAddress) -> [CardNote]{ - let context = self.set.context.private.unwrap(); + pub fn add_cards(&mut self, cards: [Card; N], owner: AztecAddress) -> [CardNote] { + let _context = self.set.context.private.unwrap(); let mut inserted_cards = []; for card in cards { @@ -150,25 +116,27 @@ impl Deck { let mut found_cards = [Option::none(); N]; for i in 0..maybe_notes.len() { if maybe_notes[i].is_some() { - let card_note = CardNote::from_note( - maybe_notes[i].unwrap_unchecked() - ); + let card_note = CardNote::from_note(maybe_notes[i].unwrap_unchecked()); // Ensure the notes are actually owned by the owner (to prevent user from generating a valid proof while // spending someone else's notes). assert(card_note.note.owner.eq(owner)); for j in 0..cards.len() { - if found_cards[j].is_none() & (cards[j].strength == card_note.card.strength) & (cards[j].points == card_note.card.points) { + if found_cards[j].is_none() + & (cards[j].strength == card_note.card.strength) + & (cards[j].points == card_note.card.points) { found_cards[j] = Option::some(card_note); } } } } - found_cards.map(|card_note: Option| { + found_cards.map( + |card_note: Option| { assert(card_note.is_some(), "Card not found"); card_note.unwrap_unchecked() - }) + } + ) } pub fn remove_cards(&mut self, cards: [Card; N], owner: AztecAddress) { @@ -182,14 +150,13 @@ impl Deck { let options = NoteViewerOptions::new().set_offset(offset); let opt_notes = self.set.view_notes(options); let mut opt_cards = [Option::none(); MAX_NOTES_PER_PAGE]; - + for i in 0..opt_notes.len() { opt_cards[i] = opt_notes[i].map(|note: ValueNote| Card::from_field(note.value)); } opt_cards } - } global PACK_CARDS = 3; // Limited by number of write requests (max 4) @@ -204,8 +171,8 @@ pub fn get_pack_cards(seed: Field, owner: AztecAddress, context: &mut PrivateCon // we generate PACK_CARDS cards assert((PACK_CARDS as u64) < 8, "Cannot generate more than 8 cards"); for i in 0..PACK_CARDS { - let strength = (random_bytes[i] as u16) + (random_bytes[i + 1] as u16) * 256; - let points = (random_bytes[i + 2] as u16) + (random_bytes[i + 3] as u16) * 256; + let strength = (random_bytes[i] as u32) + (random_bytes[i + 1] as u32) * 256; + let points = (random_bytes[i + 2] as u32) + (random_bytes[i + 3] as u32) * 256; cards[i] = Card { strength, points }; diff --git a/noir-projects/noir-contracts/contracts/card_game_contract/src/game.nr b/noir-projects/noir-contracts/contracts/card_game_contract/src/game.nr index f7b388c42b8..1a1415284db 100644 --- a/noir-projects/noir-contracts/contracts/card_game_contract/src/game.nr +++ b/noir-projects/noir-contracts/contracts/card_game_contract/src/game.nr @@ -1,7 +1,4 @@ -use dep::aztec::protocol_types::{ - address::AztecAddress, - traits::{Serialize, Deserialize}, -}; +use dep::aztec::protocol_types::{address::AztecAddress, traits::{Serialize, Deserialize}}; use crate::cards::Card; global NUMBER_OF_PLAYERS = 2; @@ -10,7 +7,7 @@ global NUMBER_OF_CARDS_DECK = 2; struct PlayerEntry { address: AztecAddress, deck_strength: u32, - points: u120, + points: u64, } impl PlayerEntry { @@ -25,7 +22,7 @@ impl Deserialize for PlayerEntry { fn deserialize(fields: [Field; PLAYER_SERIALIZED_LEN]) -> PlayerEntry { let address = AztecAddress::from_field(fields[0]); let deck_strength = fields[1] as u32; - let points = fields[2] as u120; + let points = fields[2] as u64; PlayerEntry { address, @@ -163,7 +160,7 @@ impl Game { for i in 0..NUMBER_OF_PLAYERS { let card = self.rounds_cards[round_offset + i]; - round_points += (card.points as u120); + round_points += (card.points as u64); if card.strength > winner_strength { winner_strength = card.strength; winner_index = i; diff --git a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr index b6db63a8cc7..a7ad7b4888e 100644 --- a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr @@ -2,20 +2,10 @@ contract Counter { // docs:start:imports use dep::aztec::protocol_types::address::AztecAddress; use dep::aztec::{ - context::{PrivateContext, Context}, - note::{ - note_header::NoteHeader, - utils as note_utils, - }, - state_vars::Map, - }; - use dep::value_note::{ - balance_utils, - value_note::{ - ValueNote, - VALUE_NOTE_LEN, - }, + context::{PrivateContext, Context}, note::{note_header::NoteHeader, utils as note_utils}, + state_vars::Map }; + use dep::value_note::{balance_utils, value_note::{ValueNote, VALUE_NOTE_LEN}}; use dep::easy_private_state::EasyPrivateUint; // docs:end:imports @@ -27,7 +17,7 @@ contract Counter { // docs:start:constructor #[aztec(private)] - fn constructor(headstart: u120, owner: AztecAddress) { + fn constructor(headstart: u64, owner: AztecAddress) { let counters = storage.counters; counters.at(owner).add(headstart, owner); } diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr index 9e246f32e80..d78408256a1 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr @@ -13,26 +13,18 @@ mod types; contract DocsExample { // how to import dependencies defined in your workspace - use dep::aztec::protocol_types::{ - abis::function_selector::FunctionSelector, - address::AztecAddress, - }; + use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress}; use dep::aztec::{ note::{ - note_header::NoteHeader, - note_getter_options::{NoteGetterOptions, Comparator}, - note_viewer_options::{NoteViewerOptions}, - utils as note_utils, - }, + note_header::NoteHeader, note_getter_options::{NoteGetterOptions, Comparator}, + note_viewer_options::{NoteViewerOptions}, utils as note_utils + }, context::{PrivateContext, PublicContext, Context}, - state_vars::{Map, PublicMutable, PrivateMutable, PrivateImmutable, PrivateSet, SharedImmutable}, + state_vars::{Map, PublicMutable, PrivateMutable, PrivateImmutable, PrivateSet, SharedImmutable} }; // how to import methods from other files/folders within your workspace use crate::options::create_account_card_getter_options; - use crate::types::{ - card_note::CardNote, - leader::Leader, - }; + use crate::types::{card_note::{CardNote, CARD_NOTE_LEN}, leader::Leader}; struct Storage { // Shows how to create a custom struct in PublicMutable @@ -62,10 +54,7 @@ contract DocsExample { fn init(context: Context) -> Self { Storage { // docs:start:storage-leader-init - leader: PublicMutable::new( - context, - 1 - ), + leader: PublicMutable::new(context, 1), // docs:end:storage-leader-init // docs:start:start_vars_private_mutable legendary_card: PrivateMutable::new(context, 3), @@ -77,7 +66,7 @@ contract DocsExample { 4, |context, slot| { PrivateMutable::new(context, slot) - }, + } ), // docs:end:state_vars-MapPrivateMutable // docs:start:storage-set-init @@ -93,9 +82,8 @@ contract DocsExample { 8, |context, slot| { PublicMutable::new(context, slot) - }, - ), - // docs:end:storage-minters-init + } + )// docs:end:storage-minters-init } } } @@ -153,7 +141,7 @@ contract DocsExample { } // docs:start:state_vars-NoteGetterOptionsComparatorExampleNoir - unconstrained fn read_note(amount: Field, comparator: u3) -> pub [Option; 10] { + unconstrained fn read_note(amount: Field, comparator: u8) -> pub [Option; 10] { let options = NoteViewerOptions::new().select(0, amount, Option::some(comparator)); let notes = storage.set.view_notes(options); diff --git a/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr index 0d44ff2b3b3..513026f3360 100644 --- a/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/easy_private_token_contract/src/main.nr @@ -1,17 +1,8 @@ // docs:start:easy_private_token_contract contract EasyPrivateToken { use dep::aztec::protocol_types::address::AztecAddress; - use dep::aztec::{ - note::{ - note_header::NoteHeader, - utils as note_utils, - }, - state_vars::Map, - }; - use dep::value_note::{ - balance_utils, - value_note::ValueNote, - }; + use dep::aztec::{note::{note_header::NoteHeader, utils as note_utils}, state_vars::Map}; + use dep::value_note::{balance_utils, value_note::ValueNote}; use dep::easy_private_state::EasyPrivateUint; struct Storage { @@ -22,7 +13,7 @@ contract EasyPrivateToken { * initialize the contract's initial state variables. */ #[aztec(private)] - fn constructor(initial_supply: u120, owner: AztecAddress) { + fn constructor(initial_supply: u64, owner: AztecAddress) { let balances = storage.balances; balances.at(owner).add(initial_supply, owner); @@ -30,7 +21,7 @@ contract EasyPrivateToken { // Mints `amount` of tokens to `owner`. #[aztec(private)] - fn mint(amount: u120, owner: AztecAddress) { + fn mint(amount: u64, owner: AztecAddress) { let balances = storage.balances; balances.at(owner).add(amount, owner); @@ -38,7 +29,7 @@ contract EasyPrivateToken { // Transfers `amount` of tokens from `sender` to a `recipient`. #[aztec(private)] - fn transfer(amount: u120, sender: AztecAddress, recipient: AztecAddress) { + fn transfer(amount: u64, sender: AztecAddress, recipient: AztecAddress) { let balances = storage.balances; balances.at(sender).sub(amount, sender); diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/fpc_contract/Nargo.toml index 86d8a0c7a9f..c3013a8b79a 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/fpc_contract/Nargo.toml @@ -6,5 +6,4 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } -safe_math = { path = "../../../aztec-nr/safe-math" } authwit = { path = "../../../aztec-nr/authwit" } diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/src/fee.nr b/noir-projects/noir-contracts/contracts/fpc_contract/src/fee.nr index 708a6b08d23..84f6bdcee04 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/fee.nr +++ b/noir-projects/noir-contracts/contracts/fpc_contract/src/fee.nr @@ -1,6 +1,5 @@ -use dep::safe_math::SafeU120; use dep::aztec::context::PublicContext; -pub fn calculate_fee(_context: PublicContext) -> SafeU120 { - SafeU120::new(1) +pub fn calculate_fee(_context: PublicContext) -> U128 { + U128::from_integer(1) } diff --git a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr index c181caa1807..70333ebcd7e 100644 --- a/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/fpc_contract/src/main.nr @@ -3,7 +3,6 @@ mod interfaces; contract FPC { use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress}; use dep::aztec::state_vars::SharedImmutable; - use dep::safe_math::SafeU120; use crate::interfaces::Token; diff --git a/noir-projects/noir-contracts/contracts/gas_token_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/gas_token_contract/Nargo.toml index 6bbdeb817f3..ffacc4d65a7 100644 --- a/noir-projects/noir-contracts/contracts/gas_token_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/gas_token_contract/Nargo.toml @@ -6,5 +6,4 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } -safe_math = { path = "../../../aztec-nr/safe-math" } authwit = { path = "../../../aztec-nr/authwit" } diff --git a/noir-projects/noir-contracts/contracts/gas_token_contract/src/lib.nr b/noir-projects/noir-contracts/contracts/gas_token_contract/src/lib.nr index 5ae455dfe42..9d7dbf3b74e 100644 --- a/noir-projects/noir-contracts/contracts/gas_token_contract/src/lib.nr +++ b/noir-projects/noir-contracts/contracts/gas_token_contract/src/lib.nr @@ -1,9 +1,8 @@ -use dep::safe_math::SafeU120; use dep::aztec::context::PublicContext; use dep::aztec::protocol_types::{address::{AztecAddress, EthAddress}, hash::sha256_to_field}; -pub fn calculate_fee(_context: PublicContext) -> SafeU120 { - SafeU120::new(1) +pub fn calculate_fee(_context: PublicContext) -> U128 { + U128::from_integer(1) } pub fn get_bridge_gas_msg_hash(owner: AztecAddress, amount: Field, canceller: EthAddress) -> Field { diff --git a/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr index 15a3ea7f62d..663c2cfbde2 100644 --- a/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/gas_token_contract/src/main.nr @@ -4,12 +4,10 @@ contract GasToken { use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::{AztecAddress, EthAddress}}; use dep::aztec::{hash::{compute_secret_hash}, state_vars::{PublicMutable, Map}}; - use dep::safe_math::SafeU120; - use crate::lib::{calculate_fee, get_bridge_gas_msg_hash}; struct Storage { - balances: Map>, + balances: Map>, } #[aztec(private)] @@ -22,34 +20,34 @@ contract GasToken { // Consume message and emit nullifier context.consume_l1_to_l2_message(msg_key, content_hash, secret, context.this_portal_address()); - let new_balance = storage.balances.at(to).read().add(SafeU120::new(amount)); + let new_balance = storage.balances.at(to).read() + U128::from_integer(amount); storage.balances.at(to).write(new_balance); } #[aztec(public)] fn check_balance(fee_limit: Field) { - let fee_limit_u120 = SafeU120::new(fee_limit); - assert(storage.balances.at(context.msg_sender()).read().ge(fee_limit_u120), "Balance too low"); + let fee_limit_u120 = U128::from_integer(fee_limit); + assert(storage.balances.at(context.msg_sender()).read() >= fee_limit_u120, "Balance too low"); } #[aztec(public)] fn pay_fee(fee_limit: Field) -> Field { - let fee_limit_u120 = SafeU120::new(fee_limit); + let fee_limit_u128 = U128::from_integer(fee_limit); let fee = calculate_fee(context); - assert(fee.le(fee_limit_u120), "Fee too high"); + assert(fee <= fee_limit_u128, "Fee too high"); - let sender_new_balance = storage.balances.at(context.msg_sender()).read().sub(fee); + let sender_new_balance = storage.balances.at(context.msg_sender()).read() - fee; storage.balances.at(context.msg_sender()).write(sender_new_balance); - let recipient_new_balance = storage.balances.at(context.fee_recipient()).read().add(fee); + let recipient_new_balance = storage.balances.at(context.fee_recipient()).read() + fee; storage.balances.at(context.fee_recipient()).write(recipient_new_balance); - let rebate = fee_limit_u120.sub(fee); - rebate.value as Field + let rebate = fee_limit_u128 - fee; + rebate.to_field() } // utility function for testing unconstrained fn balance_of_public(owner: AztecAddress) -> pub Field { - storage.balances.at(owner).read().value as Field + storage.balances.at(owner).read().to_field() } } diff --git a/noir-projects/noir-contracts/contracts/lending_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/lending_contract/Nargo.toml index dba6d562715..a23ee238cdb 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/lending_contract/Nargo.toml @@ -6,4 +6,3 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } -safe_math = { path = "../../../aztec-nr/safe-math" } diff --git a/noir-projects/noir-contracts/contracts/lending_contract/src/asset.nr b/noir-projects/noir-contracts/contracts/lending_contract/src/asset.nr index a3ef3db1695..88f95f6e848 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/src/asset.nr +++ b/noir-projects/noir-contracts/contracts/lending_contract/src/asset.nr @@ -1,7 +1,4 @@ -use dep::aztec::protocol_types::{ - address::AztecAddress, - traits::{Deserialize, Serialize} -}; +use dep::aztec::protocol_types::{address::AztecAddress, traits::{Deserialize, Serialize}}; // Struct to be used to represent "totals". Generally, there should be one per asset. // It stores the global values that are shared among all users, such as an accumulator @@ -9,9 +6,9 @@ use dep::aztec::protocol_types::{ // In practice, it should also point to an oracle and have more fields related to // loan to value ratios and other things, but we did not have enough reads/writes for this. struct Asset { - interest_accumulator: u120, - last_updated_ts: u120, - loan_to_value: u120, + interest_accumulator: U128, + last_updated_ts: u64, + loan_to_value: U128, oracle: AztecAddress, } @@ -20,9 +17,9 @@ global ASSET_SERIALIZED_LEN: Field = 4; impl Serialize for Asset { fn serialize(asset: Asset) -> [Field; ASSET_SERIALIZED_LEN] { [ - asset.interest_accumulator as Field, + asset.interest_accumulator.to_integer(), asset.last_updated_ts as Field, - asset.loan_to_value as Field, + asset.loan_to_value.to_integer(), asset.oracle.to_field() ] } @@ -32,9 +29,9 @@ impl Deserialize for Asset { // Right now we are wasting so many writes. If changing last_updated_ts // we will end up rewriting all of them, wasting writes. fn deserialize(fields: [Field; ASSET_SERIALIZED_LEN]) -> Asset { - let interest_accumulator = fields[0] as u120; - let last_updated_ts = fields[1] as u120; - let loan_to_value = fields[2] as u120; + let interest_accumulator = U128::from_integer(fields[0]); + let last_updated_ts = fields[1] as u64; + let loan_to_value = U128::from_integer(fields[2]); let oracle = AztecAddress::from_field(fields[3]); Asset { diff --git a/noir-projects/noir-contracts/contracts/lending_contract/src/helpers.nr b/noir-projects/noir-contracts/contracts/lending_contract/src/helpers.nr index 63e72fc7aa2..7cfba1fc667 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/src/helpers.nr +++ b/noir-projects/noir-contracts/contracts/lending_contract/src/helpers.nr @@ -1,5 +1,4 @@ use crate::interest_math::compute_multiplier; -use dep::safe_math::SafeU120; use dep::aztec::hash::pedersen_hash; // Utility used to easily get a "id" for a private user that sits in the same @@ -16,63 +15,63 @@ pub fn compute_identifier(secret: Field, on_behalf_of: Field, self: Field) -> Fi } pub fn covered_by_collateral( - price: u120, - loan_to_value: u120, - collateral: u120, - increase: u120, - decrease: u120 -) -> u120 { - let price_precision = SafeU120 { value: 1000000000 }; - let ltv_precision = SafeU120 { value: 10000 }; + price: U128, + loan_to_value: U128, + collateral: U128, + increase: U128, + decrease: U128 +) -> U128 { + let price_precision = U128::from_integer(1000000000); + let ltv_precision = U128::from_integer(10000); - let price = SafeU120 { value: price }; - let collateral = SafeU120 { value: collateral }.add(SafeU120 { value: increase }).sub(SafeU120 { value: decrease }); - let loan_to_value = SafeU120 { value: loan_to_value }; + let collateral = (collateral + increase) - decrease; - let collateral_value = collateral.mul_div(price, price_precision); - let debt_covered = collateral_value.mul_div(loan_to_value, ltv_precision); + let collateral_value = (collateral * price) / price_precision; + let debt_covered = (collateral_value * loan_to_value) / ltv_precision; - debt_covered.value + debt_covered } struct DebtReturn { - debt_value: u120, - static_debt: u120, + debt_value: U128, + static_debt: U128, +} + +fn div_up(a: U128, b: U128) -> U128 { + let div = a / b; + if div * b < a { + div + U128::from_integer(1) + } else { + div + } } pub fn debt_updates( - interest_accumulator: u120, - static_debt: u120, - increase: u120, - decrease: u120 + interest_accumulator: U128, + static_debt: U128, + increase: U128, + decrease: U128 ) -> DebtReturn { - assert(interest_accumulator > 0); - let accumulator_precision = SafeU120 { value: 1000000000 }; - - let static_debt = SafeU120 { value: static_debt }; - let interest_accumulator = SafeU120 { value: interest_accumulator }; - let increase = SafeU120 { value: increase }; - let decrease = SafeU120 { value: decrease }; + assert(interest_accumulator > U128::from_integer(0)); + let accumulator_precision = U128::from_integer(1000000000); - let current_debt_value = static_debt.mul_div(interest_accumulator, accumulator_precision); + let current_debt_value = (static_debt * interest_accumulator) / accumulator_precision; let new_debt_value = current_debt_value.add(increase).sub(decrease); // static_debt_increase = amount / accumulator // rounding up new debt. - let static_debt_increase = increase.mul_div_up(accumulator_precision, interest_accumulator); + let static_debt_increase = div_up(increase * accumulator_precision, interest_accumulator); // rounding down repayment. - let static_debt_decrease = decrease.mul_div(accumulator_precision, interest_accumulator); + let static_debt_decrease = (decrease * accumulator_precision) / interest_accumulator; // We need to allow repaying of the entire debt as well etc. This is very prone to failing // if you try to repay exact due to time diff between sim and execution. let new_static_debt = static_debt.add(static_debt_increase).sub(static_debt_decrease); - DebtReturn { debt_value: new_debt_value.value, static_debt: new_static_debt.value } + DebtReturn { debt_value: new_debt_value, static_debt: new_static_debt } } -pub fn debt_value(static_debt: u120, interest_accumulator: u120) -> u120 { - let static_debt = SafeU120 { value: static_debt }; - let accumulator_precision = SafeU120 { value: 1000000000 }; - let interest_accumulator = SafeU120 { value: interest_accumulator }; - static_debt.mul_div_up(interest_accumulator, accumulator_precision).value +pub fn debt_value(static_debt: U128, interest_accumulator: U128) -> U128 { + let accumulator_precision = U128::from_integer(1000000000); + div_up(static_debt * interest_accumulator, accumulator_precision) } diff --git a/noir-projects/noir-contracts/contracts/lending_contract/src/interest_math.nr b/noir-projects/noir-contracts/contracts/lending_contract/src/interest_math.nr index 9f9d3c518fb..a678da6baf0 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/src/interest_math.nr +++ b/noir-projects/noir-contracts/contracts/lending_contract/src/interest_math.nr @@ -1,5 +1,3 @@ -use dep::safe_math::SafeU120; - // Binomial approximation of exponential // using lower than desired precisions for everything due to u120 limit // (1+x)^n = 1+n*x+[n/2*(n-1)]*x^2+[n/6*(n-1)*(n-2)*x^3]... @@ -7,23 +5,24 @@ use dep::safe_math::SafeU120; // dividing with 31536000 (seconds per year). // rate must be measured with higher precision than 10^9. // we use e18, and rates >= 4% yearly. Otherwise need more precision -pub fn compute_multiplier(rate_per_second: u120, dt: SafeU120) -> SafeU120 { - let base = SafeU120 { value: 1000000000 }; // 1e9 - let WAD = SafeU120 { value: 1000000000000000000 }; // 1e18 +pub fn compute_multiplier(rate_per_second: U128, dt: u64) -> U128 { + let base = U128::from_integer(1000000000); // 1e9 + let WAD = U128::from_integer(1000000000000000000); // 1e18 let diff = WAD.div(base); let mut res = base; - if (!dt.is_zero()) { - let exp_minus_one = SafeU120 { value: dt.value - 1 }; - let exp_minus_two = SafeU120 { value: if (dt.value > 2) { dt.value - 2 } else { 0 } }; + if dt != 0 { + let exp_minus_one = U128::from_integer(dt - 1); + let exp_minus_two = U128::from_integer(if (dt > 2) { dt - 2 } else { 0 }); + let dt = U128::from_integer(dt); // if rate_per_second < sqrt(WAD), then base_power_two and base_power_three = 0 - let rate = SafeU120 { value: rate_per_second }; - let base_power_two = rate.mul_div(rate, WAD); - let base_power_three = base_power_two.mul_div(rate, WAD); + let rate = rate_per_second; + let base_power_two = (rate * rate) / WAD; + let base_power_three = (base_power_two * rate) / WAD; let temp = dt.mul(exp_minus_one); - let second_term = temp.mul(base_power_two).div(SafeU120 { value: 2 }); - let third_term = temp.mul(exp_minus_two).mul(base_power_three).div(SafeU120 { value: 6 }); + let second_term = temp.mul(base_power_two).div(U128::from_integer(2)); + let third_term = temp.mul(exp_minus_two).mul(base_power_three).div(U128::from_integer(6)); // throwing away precision to keep us under u120 :sob: let offset = dt.mul(rate).add(second_term).add(third_term).div(diff); diff --git a/noir-projects/noir-contracts/contracts/lending_contract/src/interfaces.nr b/noir-projects/noir-contracts/contracts/lending_contract/src/interfaces.nr index c65d8033a9d..631f5eed5ca 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/src/interfaces.nr +++ b/noir-projects/noir-contracts/contracts/lending_contract/src/interfaces.nr @@ -1,33 +1,26 @@ -use dep::aztec::context::{ - PrivateContext, - PublicContext -}; +use dep::aztec::context::{PrivateContext, PublicContext}; use crate::asset::Asset; -use dep::aztec::protocol_types::{ - abis::function_selector::FunctionSelector, - address::AztecAddress, - constants::RETURN_VALUES_LENGTH, -}; +use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress, constants::RETURN_VALUES_LENGTH}; struct PriceFeed { address: AztecAddress, } impl PriceFeed { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } - - pub fn get_price(self: Self, context: PublicContext) -> u120 { - let return_values = context.call_public_function( - self.address, - FunctionSelector::from_signature("get_price(Field)"), - [0] - ); - - return_values[0] as u120 - } + pub fn at(address: AztecAddress) -> Self { + Self { address } + } + + pub fn get_price(self: Self, context: PublicContext) -> U128 { + let return_values = context.call_public_function( + self.address, + FunctionSelector::from_signature("get_price(Field)"), + [0] + ); + + U128::from_integer(return_values[0]) + } } struct Token { @@ -35,50 +28,76 @@ struct Token { } impl Token { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } - - pub fn transfer_public(self: Self, context: PublicContext, from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { - context.call_public_function( - self.address, - FunctionSelector::from_signature("transfer_public((Field),(Field),Field,Field)"), - [from.to_field(), to.to_field(), amount, nonce] - ); - } - - pub fn mint_public(self: Self, context: PublicContext, to: AztecAddress, amount: Field) { - context.call_public_function( - self.address, - FunctionSelector::from_signature("mint_public((Field),Field)"), - [to.to_field(), amount] - ); - } - - pub fn burn_public(self: Self, context: PublicContext, from: AztecAddress, amount: Field, nonce: Field) { - context.call_public_function( - self.address, - FunctionSelector::from_signature("burn_public((Field),Field,Field)"), - [from.to_field(), amount, nonce] - ); - } - - // Private - pub fn unshield(self: Self, context: &mut PrivateContext, from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) -> [Field; RETURN_VALUES_LENGTH] { - context.call_private_function( - self.address, - FunctionSelector::from_signature("unshield((Field),(Field),Field,Field)"), - [from.to_field(), to.to_field(), amount, nonce] - ) - } - - pub fn burn(self: Self, context: &mut PrivateContext, from: AztecAddress, amount: Field, nonce: Field) -> [Field; RETURN_VALUES_LENGTH] { - context.call_private_function( - self.address, - FunctionSelector::from_signature("burn((Field),Field,Field)"), - [from.to_field(), amount, nonce] - ) - } + pub fn at(address: AztecAddress) -> Self { + Self { address } + } + + pub fn transfer_public( + self: Self, + context: PublicContext, + from: AztecAddress, + to: AztecAddress, + amount: Field, + nonce: Field + ) { + context.call_public_function( + self.address, + FunctionSelector::from_signature("transfer_public((Field),(Field),Field,Field)"), + [from.to_field(), to.to_field(), amount, nonce] + ); + } + + pub fn mint_public(self: Self, context: PublicContext, to: AztecAddress, amount: Field) { + context.call_public_function( + self.address, + FunctionSelector::from_signature("mint_public((Field),Field)"), + [to.to_field(), amount] + ); + } + + pub fn burn_public( + self: Self, + context: PublicContext, + from: AztecAddress, + amount: Field, + nonce: Field + ) { + context.call_public_function( + self.address, + FunctionSelector::from_signature("burn_public((Field),Field,Field)"), + [from.to_field(), amount, nonce] + ); + } + + // Private + pub fn unshield( + self: Self, + context: &mut PrivateContext, + from: AztecAddress, + to: AztecAddress, + amount: Field, + nonce: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + context.call_private_function( + self.address, + FunctionSelector::from_signature("unshield((Field),(Field),Field,Field)"), + [from.to_field(), to.to_field(), amount, nonce] + ) + } + + pub fn burn( + self: Self, + context: &mut PrivateContext, + from: AztecAddress, + amount: Field, + nonce: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + context.call_private_function( + self.address, + FunctionSelector::from_signature("burn((Field),Field,Field)"), + [from.to_field(), amount, nonce] + ) + } } struct Lending { @@ -86,16 +105,16 @@ struct Lending { } impl Lending { - pub fn at(address: AztecAddress) -> Self { - Self { address } - } - - pub fn update_accumulator(self: Self, context: PublicContext) -> Asset { - let return_values = context.call_public_function_no_args( - self.address, - FunctionSelector::from_signature("update_accumulator()"), - ); - - Asset::deserialize(return_values) - } + pub fn at(address: AztecAddress) -> Self { + Self { address } + } + + pub fn update_accumulator(self: Self, context: PublicContext) -> Asset { + let return_values = context.call_public_function_no_args( + self.address, + FunctionSelector::from_signature("update_accumulator()") + ); + + Asset::deserialize(return_values) + } } diff --git a/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr b/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr index 6f63a85e52c..bc039a499be 100644 --- a/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/lending_contract/src/main.nr @@ -11,19 +11,9 @@ mod interfaces; // - A way to repay all debt at once // - Liquidations contract Lending { - use dep::aztec::protocol_types::{ - abis::function_selector::FunctionSelector, - address::AztecAddress, - }; - use dep::safe_math::SafeU120; + use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress}; use dep::std::option::Option; - use dep::aztec::{ - context::{PrivateContext, PublicContext, Context}, - state_vars::{ - Map, - PublicMutable, - } - }; + use dep::aztec::{context::{PrivateContext, PublicContext, Context}, state_vars::{Map, PublicMutable}}; use crate::asset::Asset; use crate::interest_math::compute_multiplier; use crate::helpers::{covered_by_collateral, DebtReturn, debt_updates, debt_value, compute_identifier}; @@ -59,14 +49,17 @@ contract Lending { let asset_loc = storage.assets.at(0); let asset = asset_loc.read(); - assert(loan_to_value as u120 <= 10000); + let loan_to_value = U128::from_integer(loan_to_value); + + assert(loan_to_value <= U128::from_integer(10000)); assert(asset.last_updated_ts == 0); - assert(asset.interest_accumulator == 0); + assert(asset.interest_accumulator == U128::from_integer(0)); - let last_updated_ts = context.timestamp() as u120; - let loan_to_value = loan_to_value as u120; + let last_updated_ts = context.timestamp() as u64; - asset_loc.write(Asset { interest_accumulator: 1000000000, last_updated_ts, loan_to_value, oracle }); + asset_loc.write( + Asset { interest_accumulator: U128::from_integer(1000000000), last_updated_ts, loan_to_value, oracle } + ); storage.collateral_asset.write(collateral_asset); storage.stable_coin.write(stable_coin); @@ -78,19 +71,19 @@ contract Lending { let asset_loc = storage.assets.at(0); let mut asset = asset_loc.read(); - let timestamp = context.timestamp() as u120; - let dt: SafeU120 = SafeU120 { value: timestamp }.sub(SafeU120 { value: asset.last_updated_ts }); + let timestamp = context.timestamp() as u64; + let dt = timestamp - asset.last_updated_ts; // Only update if time has passed. - if (!dt.is_zero()) { - let precision: SafeU120 = SafeU120 { value: 1000000000 }; - let rate_per_second: u120 = 1268391679; // 4% yearly rate / (60 * 60 * 24 * 365) + if !(dt == 0) { + let precision = U128::from_integer(1000000000); + let rate_per_second = U128::from_integer(1268391679); // 4% yearly rate / (60 * 60 * 24 * 365) // if rate_per_second < sqrt(WAD) our approx is eq precision + rate * dt let multiplier = compute_multiplier(rate_per_second, dt); // accumulator *= multiplier, and multiplier >= 1 - asset.interest_accumulator = SafeU120{value: asset.interest_accumulator}.mul_div(multiplier, precision).value; - asset.last_updated_ts = context.timestamp() as u120; + asset.interest_accumulator = (asset.interest_accumulator * multiplier) / precision; + asset.last_updated_ts = context.timestamp() as u64; asset_loc.write(asset); } @@ -184,11 +177,16 @@ contract Lending { let debt_covered = covered_by_collateral( price, asset.loan_to_value, - collateral as u120, - 0, - amount as u120 + U128::from_integer(collateral), + U128::from_integer(0), + U128::from_integer(amount) + ); + let debt_returns = debt_updates( + asset.interest_accumulator, + U128::from_integer(static_debt), + U128::from_integer(0), + U128::from_integer(0) ); - let debt_returns = debt_updates(asset.interest_accumulator, static_debt as u120, 0, 0); assert(debt_returns.debt_value < debt_covered); @@ -226,15 +224,26 @@ contract Lending { let price = PriceFeed::at(asset.oracle).get_price(context); // Fetch collateral and static_debt, compute health of current position - let collateral = storage.collateral.at(owner).read() as u120; - let static_debt = storage.static_debt.at(owner).read() as u120; + let collateral = U128::from_integer(storage.collateral.at(owner).read()); + let static_debt = U128::from_integer(storage.static_debt.at(owner).read()); - let debt_covered = covered_by_collateral(price, asset.loan_to_value, collateral, 0, 0); - let debt_returns = debt_updates(asset.interest_accumulator, static_debt, amount as u120, 0); + let debt_covered = covered_by_collateral( + price, + asset.loan_to_value, + collateral, + U128::from_integer(0), + U128::from_integer(0) + ); + let debt_returns = debt_updates( + asset.interest_accumulator, + static_debt, + U128::from_integer(amount), + U128::from_integer(0) + ); assert(debt_returns.debt_value < debt_covered); - storage.static_debt.at(owner).write(debt_returns.static_debt as Field); + storage.static_debt.at(owner).write(debt_returns.static_debt.to_integer()); // @todo @LHerskind Need to support both private and public minting. let stable_coin = storage.stable_coin.read(); @@ -278,10 +287,15 @@ contract Lending { // To ensure that private is using the correct token. assert(stable_coin.eq(storage.stable_coin.read())); - let static_debt = storage.static_debt.at(owner).read() as u120; - let debt_returns = debt_updates(asset.interest_accumulator, static_debt, 0, amount as u120); + let static_debt = U128::from_integer(storage.static_debt.at(owner).read()); + let debt_returns = debt_updates( + asset.interest_accumulator, + static_debt, + U128::from_integer(0), + U128::from_integer(amount) + ); - storage.static_debt.at(owner).write(debt_returns.static_debt as Field); + storage.static_debt.at(owner).write(debt_returns.static_debt.to_integer()); } unconstrained fn get_asset(assetId: Field) -> pub Asset { @@ -292,7 +306,10 @@ contract Lending { let collateral = storage.collateral.at(owner).read(); let static_debt = storage.static_debt.at(owner).read(); let asset = storage.assets.at(0).read(); - let debt = debt_value(static_debt as u120, asset.interest_accumulator as u120) as Field; + let debt = debt_value( + U128::from_integer(static_debt), + U128::from_integer(asset.interest_accumulator) + ).to_integer(); Position { collateral, static_debt, debt } } diff --git a/noir-projects/noir-contracts/contracts/price_feed_contract/src/asset.nr b/noir-projects/noir-contracts/contracts/price_feed_contract/src/asset.nr index b795fdf68a3..0f34a0429b1 100644 --- a/noir-projects/noir-contracts/contracts/price_feed_contract/src/asset.nr +++ b/noir-projects/noir-contracts/contracts/price_feed_contract/src/asset.nr @@ -1,20 +1,20 @@ use dep::aztec::protocol_types::traits::{Serialize, Deserialize}; struct Asset { - price: u120, + price: U128, } global ASSET_SERIALIZED_LEN: Field = 1; impl Serialize for Asset { fn serialize(asset: Asset) -> [Field; ASSET_SERIALIZED_LEN] { - [asset.price as Field] + [asset.price.to_integer()] } } impl Deserialize for Asset { fn deserialize(fields: [Field; ASSET_SERIALIZED_LEN]) -> Asset { - let price = fields[0] as u120; + let price = U128::from_integer(fields[0]); Asset { price } } } diff --git a/noir-projects/noir-contracts/contracts/price_feed_contract/src/main.nr b/noir-projects/noir-contracts/contracts/price_feed_contract/src/main.nr index dd9a743ed3c..afd14a72400 100644 --- a/noir-projects/noir-contracts/contracts/price_feed_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/price_feed_contract/src/main.nr @@ -2,13 +2,7 @@ mod asset; contract PriceFeed { use dep::std::option::Option; - use dep::aztec::{ - context::{PrivateContext, PublicContext, Context}, - state_vars::{ - Map, - PublicMutable, - }, - }; + use dep::aztec::{context::{PrivateContext, PublicContext, Context}, state_vars::{Map, PublicMutable}}; use dep::aztec::protocol_types::address::AztecAddress; use crate::asset::Asset; @@ -21,9 +15,9 @@ contract PriceFeed { fn constructor() {} #[aztec(public)] - fn set_price(asset_id: Field, price: u120) { + fn set_price(asset_id: Field, price: Field) { let asset = storage.assets.at(asset_id); - asset.write(Asset { price }); + asset.write(Asset { price: U128::from_integer(price) }); } #[aztec(public)] diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/token_blacklist_contract/Nargo.toml index a742335f1ef..d178cd2ba2d 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/Nargo.toml @@ -6,6 +6,5 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } -safe_math = { path = "../../../aztec-nr/safe-math" } field_note = { path = "../../../aztec-nr/field-note" } authwit = { path = "../../../aztec-nr/authwit" } diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr index 29c8bff7c70..7ee25b3797c 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr @@ -16,37 +16,18 @@ contract TokenBlacklist { // Libs use dep::std::option::Option; - use dep::safe_math::SafeU120; - use dep::aztec::protocol_types::{ - abis::function_selector::FunctionSelector, - address::AztecAddress, - }; + use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress}; use dep::aztec::{ - note::{ - note_getter_options::NoteGetterOptions, - note_header::NoteHeader, - utils as note_utils, - }, - context::{PrivateContext, PublicContext, Context}, - hash::{compute_secret_hash}, - state_vars::{Map, PublicMutable, PrivateSet, PrivateImmutable}, + note::{note_getter_options::NoteGetterOptions, note_header::NoteHeader, utils as note_utils}, + context::{PrivateContext, PublicContext, Context}, hash::{compute_secret_hash}, + state_vars::{Map, PublicMutable, PrivateSet, PrivateImmutable} }; use dep::field_note::field_note::FieldNote; - use dep::authwit::{ - auth::{ - assert_current_call_valid_authwit, - assert_current_call_valid_authwit_public, - }, - }; + use dep::authwit::{auth::{assert_current_call_valid_authwit, assert_current_call_valid_authwit_public}}; - use crate::types::{ - transparent_note::TransparentNote, - token_note::TokenNote, - balances_map::{BalancesMap}, - roles::UserFlags, - }; + use crate::types::{transparent_note::TransparentNote, token_note::TokenNote, balances_map::{BalancesMap}, roles::UserFlags}; // docs:start:interface use crate::interfaces::SlowMap; // docs:end:interface @@ -54,9 +35,9 @@ contract TokenBlacklist { struct Storage { admin: PublicMutable, balances: BalancesMap, - total_supply: PublicMutable, + total_supply: PublicMutable, pending_shields: PrivateSet, - public_balances: Map>, + public_balances: Map>, slow_update: PrivateImmutable, public_slow_update: PublicMutable, } @@ -83,7 +64,7 @@ contract TokenBlacklist { #[aztec(private)] fn init_slow_tree(user: AztecAddress) { - let roles = UserFlags { is_admin: true, is_minter: false, is_blacklisted: false }.get_value() as Field; + let roles = UserFlags { is_admin: true, is_minter: false, is_blacklisted: false }.get_value().to_field(); // docs:start:get_and_update_private let slow = SlowMap::at(AztecAddress::from_field(storage.slow_update.get_note().value)); slow.update_at_private(&mut context, user.to_field(), roles); @@ -118,7 +99,7 @@ contract TokenBlacklist { // docs:start:slowmap_at let slow = SlowMap::at(AztecAddress::from_field(storage.slow_update.get_note().value)); // docs:end:slowmap_at - let caller_roles = UserFlags::new(slow.read_at(&mut context, context.msg_sender().to_field()) as u120); + let caller_roles = UserFlags::new(U128::from_integer(slow.read_at(&mut context, context.msg_sender().to_field()))); assert(caller_roles.is_admin, "caller is not admin"); slow.update_at_private(&mut context, user.to_field(), roles); @@ -130,14 +111,14 @@ contract TokenBlacklist { let slow = SlowMap::at(storage.public_slow_update.read()); // docs:end:get_public // docs:start:read_at_pub - let to_roles = UserFlags::new(slow.read_at_pub(context, to.to_field()) as u120); + let to_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(context, to.to_field()))); // docs:end:read_at_pub assert(!to_roles.is_blacklisted, "Blacklisted: Recipient"); - let caller_roles = UserFlags::new(slow.read_at_pub(context, context.msg_sender().to_field()) as u120); + let caller_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(context, context.msg_sender().to_field()))); assert(caller_roles.is_minter, "caller is not minter"); - let amount = SafeU120::new(amount); + let amount = U128::from_integer(amount); let new_balance = storage.public_balances.at(to).read().add(amount); let supply = storage.total_supply.read().add(amount); @@ -148,12 +129,12 @@ contract TokenBlacklist { #[aztec(public)] fn mint_private(amount: Field, secret_hash: Field) { let slow = SlowMap::at(storage.public_slow_update.read()); - let caller_roles = UserFlags::new(slow.read_at_pub(context, context.msg_sender().to_field()) as u120); + let caller_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(context, context.msg_sender().to_field()))); assert(caller_roles.is_minter, "caller is not minter"); let pending_shields = storage.pending_shields; let mut note = TransparentNote::new(amount, secret_hash); - let supply = storage.total_supply.read().add(SafeU120::new(amount)); + let supply = storage.total_supply.read().add(U128::from_integer(amount)); storage.total_supply.write(supply); pending_shields.insert_from_public(&mut note); @@ -162,7 +143,7 @@ contract TokenBlacklist { #[aztec(public)] fn shield(from: AztecAddress, amount: Field, secret_hash: Field, nonce: Field) { let slow = SlowMap::at(storage.public_slow_update.read()); - let from_roles = UserFlags::new(slow.read_at_pub(context, from.to_field()) as u120); + let from_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(context, from.to_field()))); assert(!from_roles.is_blacklisted, "Blacklisted: Sender"); if (!from.eq(context.msg_sender())) { @@ -172,11 +153,11 @@ contract TokenBlacklist { assert(nonce == 0, "invalid nonce"); } - let amount = SafeU120::new(amount); + let amount = U128::from_integer(amount); let from_balance = storage.public_balances.at(from).read().sub(amount); let pending_shields = storage.pending_shields; - let mut note = TransparentNote::new(amount.value as Field, secret_hash); + let mut note = TransparentNote::new(amount.to_field(), secret_hash); storage.public_balances.at(from).write(from_balance); pending_shields.insert_from_public(&mut note); @@ -185,9 +166,9 @@ contract TokenBlacklist { #[aztec(public)] fn transfer_public(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { let slow = SlowMap::at(storage.public_slow_update.read()); - let from_roles = UserFlags::new(slow.read_at_pub(context, from.to_field()) as u120); + let from_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(context, from.to_field()))); assert(!from_roles.is_blacklisted, "Blacklisted: Sender"); - let to_roles = UserFlags::new(slow.read_at_pub(context, to.to_field()) as u120); + let to_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(context, to.to_field()))); assert(!to_roles.is_blacklisted, "Blacklisted: Recipient"); if (!from.eq(context.msg_sender())) { @@ -196,7 +177,7 @@ contract TokenBlacklist { assert(nonce == 0, "invalid nonce"); } - let amount = SafeU120::new(amount); + let amount = U128::from_integer(amount); let from_balance = storage.public_balances.at(from).read().sub(amount); storage.public_balances.at(from).write(from_balance); @@ -207,7 +188,7 @@ contract TokenBlacklist { #[aztec(public)] fn burn_public(from: AztecAddress, amount: Field, nonce: Field) { let slow = SlowMap::at(storage.public_slow_update.read()); - let from_roles = UserFlags::new(slow.read_at_pub(context, from.to_field()) as u120); + let from_roles = UserFlags::new(U128::from_integer(slow.read_at_pub(context, from.to_field()))); assert(!from_roles.is_blacklisted, "Blacklisted: Sender"); if (!from.eq(context.msg_sender())) { @@ -216,7 +197,7 @@ contract TokenBlacklist { assert(nonce == 0, "invalid nonce"); } - let amount = SafeU120::new(amount); + let amount = U128::from_integer(amount); let from_balance = storage.public_balances.at(from).read().sub(amount); storage.public_balances.at(from).write(from_balance); @@ -228,7 +209,7 @@ contract TokenBlacklist { fn redeem_shield(to: AztecAddress, amount: Field, secret: Field) { let slow = SlowMap::at(AztecAddress::from_field(storage.slow_update.get_note().value)); // docs:start:slowmap_read_at - let to_roles = UserFlags::new(slow.read_at(&mut context, to.to_field()) as u120); + let to_roles = UserFlags::new(U128::from_integer(slow.read_at(&mut context, to.to_field()))); // docs:end:slowmap_read_at assert(!to_roles.is_blacklisted, "Blacklisted: Recipient"); @@ -243,15 +224,15 @@ contract TokenBlacklist { pending_shields.remove(note); // Add the token note to user's balances set - storage.balances.add(to, SafeU120::new(amount)); + storage.balances.add(to, U128::from_integer(amount)); } #[aztec(private)] fn unshield(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { let slow = SlowMap::at(AztecAddress::from_field(storage.slow_update.get_note().value)); - let from_roles = UserFlags::new(slow.read_at(&mut context, from.to_field()) as u120); + let from_roles = UserFlags::new(U128::from_integer(slow.read_at(&mut context, from.to_field()))); assert(!from_roles.is_blacklisted, "Blacklisted: Sender"); - let to_roles = UserFlags::new(slow.read_at(&mut context, to.to_field()) as u120); + let to_roles = UserFlags::new(U128::from_integer(slow.read_at(&mut context, to.to_field()))); assert(!to_roles.is_blacklisted, "Blacklisted: Recipient"); if (!from.eq(context.msg_sender())) { @@ -260,7 +241,7 @@ contract TokenBlacklist { assert(nonce == 0, "invalid nonce"); } - storage.balances.sub(from, SafeU120::new(amount)); + storage.balances.sub(from, U128::from_integer(amount)); let selector = FunctionSelector::from_signature("_increase_public_balance((Field),Field)"); context.call_public_function(context.this_address(), selector, [to.to_field(), amount]); @@ -270,9 +251,9 @@ contract TokenBlacklist { #[aztec(private)] fn transfer(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { let slow = SlowMap::at(AztecAddress::from_field(storage.slow_update.get_note().value)); - let from_roles = UserFlags::new(slow.read_at(&mut context, from.to_field()) as u120); + let from_roles = UserFlags::new(U128::from_integer(slow.read_at(&mut context, from.to_field()))); assert(!from_roles.is_blacklisted, "Blacklisted: Sender"); - let to_roles = UserFlags::new(slow.read_at(&mut context, to.to_field()) as u120); + let to_roles = UserFlags::new(U128::from_integer(slow.read_at(&mut context, to.to_field()))); assert(!to_roles.is_blacklisted, "Blacklisted: Recipient"); // docs:end:transfer_private @@ -282,7 +263,7 @@ contract TokenBlacklist { assert(nonce == 0, "invalid nonce"); } - let amount = SafeU120::new(amount); + let amount = U128::from_integer(amount); storage.balances.sub(from, amount); storage.balances.add(to, amount); } @@ -290,7 +271,7 @@ contract TokenBlacklist { #[aztec(private)] fn burn(from: AztecAddress, amount: Field, nonce: Field) { let slow = SlowMap::at(AztecAddress::from_field(storage.slow_update.get_note().value)); - let from_roles = UserFlags::new(slow.read_at(&mut context, from.to_field()) as u120); + let from_roles = UserFlags::new(U128::from_integer(slow.read_at(&mut context, from.to_field()))); assert(!from_roles.is_blacklisted, "Blacklisted: Sender"); if (!from.eq(context.msg_sender())) { @@ -299,7 +280,7 @@ contract TokenBlacklist { assert(nonce == 0, "invalid nonce"); } - storage.balances.sub(from, SafeU120::new(amount)); + storage.balances.sub(from, U128::from_integer(amount)); let selector = FunctionSelector::from_signature("_reduce_total_supply(Field)"); context.call_public_function(context.this_address(), selector, [amount]); @@ -309,28 +290,28 @@ contract TokenBlacklist { #[aztec(public)] internal fn _increase_public_balance(to: AztecAddress, amount: Field) { - let new_balance = storage.public_balances.at(to).read().add(SafeU120::new(amount)); + let new_balance = storage.public_balances.at(to).read().add(U128::from_integer(amount)); storage.public_balances.at(to).write(new_balance); } #[aztec(public)] internal fn _reduce_total_supply(amount: Field) { // Only to be called from burn. - let new_supply = storage.total_supply.read().sub(SafeU120::new(amount)); + let new_supply = storage.total_supply.read().sub(U128::from_integer(amount)); storage.total_supply.write(new_supply); } /// Unconstrained /// - unconstrained fn total_supply() -> pub u120 { - storage.total_supply.read().value + unconstrained fn total_supply() -> pub Field { + storage.total_supply.read().to_field() } - unconstrained fn balance_of_private(owner: AztecAddress) -> pub u120 { - storage.balances.balance_of(owner).value + unconstrained fn balance_of_private(owner: AztecAddress) -> pub Field { + storage.balances.balance_of(owner).to_field() } - unconstrained fn balance_of_public(owner: AztecAddress) -> pub u120 { - storage.public_balances.at(owner).read().value + unconstrained fn balance_of_public(owner: AztecAddress) -> pub Field { + storage.public_balances.at(owner).read().to_field() } } diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/balances_map.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/balances_map.nr index b6ba0ef697e..a8ca9bf21e2 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/balances_map.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/balances_map.nr @@ -1,5 +1,4 @@ use dep::std::option::Option; -use dep::safe_math::SafeU120; use dep::aztec::{ context::Context, protocol_types::{address::AztecAddress, constants::MAX_READ_REQUESTS_PER_CALL}, state_vars::{PrivateSet, Map}, @@ -26,10 +25,7 @@ impl BalancesMap { } } - unconstrained pub fn balance_of( - self: Self, - owner: AztecAddress - ) -> SafeU120 where T: NoteInterface + OwnedNote { + unconstrained pub fn balance_of(self: Self, owner: AztecAddress) -> U128 where T: NoteInterface + OwnedNote { self.balance_of_with_offset(owner, 0) } @@ -37,9 +33,9 @@ impl BalancesMap { self: Self, owner: AztecAddress, offset: u32 - ) -> SafeU120 where T: NoteInterface + OwnedNote { + ) -> U128 where T: NoteInterface + OwnedNote { // Same as SafeU120::new(0), but fewer constraints because no check. - let mut balance = SafeU120::min(); + let mut balance = U128::from_integer(0); // docs:start:view_notes let options = NoteViewerOptions::new().set_offset(offset); let opt_notes = self.map.at(owner).view_notes(options); @@ -47,11 +43,11 @@ impl BalancesMap { let len = opt_notes.len(); for i in 0..len { if opt_notes[i].is_some() { - balance = balance.add(opt_notes[i].unwrap_unchecked().get_amount()); + balance = balance + opt_notes[i].unwrap_unchecked().get_amount(); } } if (opt_notes[len - 1].is_some()) { - balance = balance.add(self.balance_of_with_offset(owner, offset + opt_notes.len() as u32)); + balance = balance + self.balance_of_with_offset(owner, offset + opt_notes.len() as u32); } balance @@ -60,7 +56,7 @@ impl BalancesMap { pub fn add( self: Self, owner: AztecAddress, - addend: SafeU120 + addend: U128 ) where T: NoteInterface + OwnedNote { let mut addend_note = T::new(addend, owner); @@ -72,14 +68,14 @@ impl BalancesMap { pub fn sub( self: Self, owner: AztecAddress, - subtrahend: SafeU120 + subtrahend: U128 ) where T: NoteInterface + OwnedNote { // docs:start:get_notes let options = NoteGetterOptions::with_filter(filter_notes_min_sum, subtrahend); let maybe_notes = self.map.at(owner).get_notes(options); // docs:end:get_notes - let mut minuend: SafeU120 = SafeU120::min(); + let mut minuend: U128 = U128::from_integer(0); for i in 0..maybe_notes.len() { if maybe_notes[i].is_some() { let note = maybe_notes[i].unwrap_unchecked(); @@ -92,27 +88,27 @@ impl BalancesMap { self.map.at(owner).remove(note); // docs:end:remove - minuend = minuend.add(note.get_amount()); + minuend = minuend + note.get_amount(); } } // This is to provide a nicer error msg, // without it minuend-subtrahend would still catch it, but more generic error then. // without the == true, it includes 'minuend.ge(subtrahend)' as part of the error. - assert(minuend.ge(subtrahend) == true, "Balance too low"); + assert(minuend >= subtrahend, "Balance too low"); - self.add(owner, minuend.sub(subtrahend)); + self.add(owner, minuend - subtrahend); } } pub fn filter_notes_min_sum( notes: [Option; MAX_READ_REQUESTS_PER_CALL], - min_sum: SafeU120 + min_sum: U128 ) -> [Option; MAX_READ_REQUESTS_PER_CALL] where T: NoteInterface + OwnedNote { let mut selected = [Option::none(); MAX_READ_REQUESTS_PER_CALL]; - let mut sum = SafeU120::min(); + let mut sum = U128::from_integer(0); for i in 0..notes.len() { - if notes[i].is_some() & sum.lt(min_sum) { + if notes[i].is_some() & sum < min_sum { let note = notes[i].unwrap_unchecked(); selected[i] = Option::some(note); sum = sum.add(note.get_amount()); diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/roles.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/roles.nr index 793674e996d..57dab6e890d 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/roles.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/roles.nr @@ -1,6 +1,6 @@ -global BLACKLIST_FLAG: u120 = 1; -global MINTER_FLAG: u120 = 2; -global ADMIN_FLAG: u120 = 4; +global BLACKLIST_FLAG: U128 = U128::from_integer(1); +global MINTER_FLAG: U128 = U128::from_integer(2); +global ADMIN_FLAG: U128 = U128::from_integer(4); struct UserFlags { is_admin: bool, @@ -10,7 +10,7 @@ struct UserFlags { impl UserFlags { - pub fn new(value: u120) -> Self { + pub fn new(value: U128) -> Self { let is_admin = value & ADMIN_FLAG == ADMIN_FLAG; let is_minter = value & MINTER_FLAG == MINTER_FLAG; let is_blacklisted = value & BLACKLIST_FLAG == BLACKLIST_FLAG; @@ -22,8 +22,8 @@ impl UserFlags { } } - pub fn get_value(self) -> u120 { - let mut value: u120 = 0; + pub fn get_value(self) -> U128 { + let mut value: U128 = U128::from_integer(0); if self.is_admin { value = value | ADMIN_FLAG; diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr index 1c9ddb96e3b..2b03bb3c7fd 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr @@ -1,15 +1,14 @@ use dep::aztec::{ - protocol_types::{address::AztecAddress, constants::{MAX_READ_REQUESTS_PER_CALL}}, + protocol_types::{address::AztecAddress, constants::MAX_READ_REQUESTS_PER_CALL}, note::{note_header::NoteHeader, note_interface::NoteInterface, utils::compute_note_hash_for_consumption}, context::PrivateContext, log::emit_encrypted_log, hash::pedersen_hash }; use dep::aztec::oracle::{rand::rand, nullifier_key::get_nullifier_secret_key, get_public_key::get_public_key}; -use dep::safe_math::SafeU120; use dep::std::option::Option; trait OwnedNote { - fn new(amount: SafeU120, owner: AztecAddress) -> Self; - fn get_amount(self) -> SafeU120; + fn new(amount: U128, owner: AztecAddress) -> Self; + fn get_amount(self) -> U128; fn get_owner(self) -> AztecAddress; } @@ -17,7 +16,7 @@ global TOKEN_NOTE_LEN: Field = 3; // 3 plus a header. struct TokenNote { // the amount of tokens in the note - amount: SafeU120, + amount: U128, // the provider of secrets for the nullifier. The owner (recipient) to ensure that the note // can be privately spent. When nullifier secret and encryption private key is same // we can simply use the owner for this one. @@ -31,19 +30,20 @@ struct TokenNote { impl NoteInterface for TokenNote { fn serialize_content(self) -> [Field; TOKEN_NOTE_LEN] { - [self.amount.value as Field, self.owner.to_field(), self.randomness] + [self.amount.to_field(), self.owner.to_field(), self.randomness] } fn deserialize_content(serialized_note: [Field; TOKEN_NOTE_LEN]) -> Self { Self { - amount: SafeU120::new(serialized_note[0]), + // TODO: check this type cast is right + amount: U128::from_integer(serialized_note[0]), owner: AztecAddress::from_field(serialized_note[1]), randomness: serialized_note[2], header: NoteHeader::empty(), } } - - fn compute_note_content_hash(self) -> Field { + + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. pedersen_hash(self.serialize_content(), 0) } @@ -76,14 +76,14 @@ impl NoteInterface for TokenNote { self.header = header; } - fn get_header(note: TokenNote) -> NoteHeader { - note.header + fn get_header(self) -> NoteHeader { + self.header } // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field) { // We only bother inserting the note if non-empty to save funds on gas. - if !self.amount.is_zero() { + if !(self.amount == U128::from_integer(0)) { let encryption_pub_key = get_public_key(self.owner); emit_encrypted_log( context, @@ -104,7 +104,7 @@ impl NoteInterface for TokenNote { } impl OwnedNote for TokenNote { - fn new(amount: SafeU120, owner: AztecAddress) -> Self { + fn new(amount: U128, owner: AztecAddress) -> Self { Self { amount, owner, @@ -113,7 +113,7 @@ impl OwnedNote for TokenNote { } } - fn get_amount(self) -> SafeU120 { + fn get_amount(self) -> U128 { self.amount } diff --git a/noir-projects/noir-contracts/contracts/token_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/token_contract/Nargo.toml index f8409f858b9..2c06fc4c8b5 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/Nargo.toml +++ b/noir-projects/noir-contracts/contracts/token_contract/Nargo.toml @@ -6,6 +6,5 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } -safe_math = { path = "../../../aztec-nr/safe-math" } compressed_string = { path = "../../../aztec-nr/compressed-string" } authwit = { path = "../../../aztec-nr/authwit" } diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index 6c2f351a97f..7a20030245a 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -13,37 +13,19 @@ contract Token { // Libs use dep::std::option::Option; - use dep::safe_math::SafeU120; use dep::compressed_string::FieldCompressedString; use dep::aztec::{ - note::{ - note_getter_options::NoteGetterOptions, - note_header::NoteHeader, - utils as note_utils, - }, - hash::{compute_secret_hash}, - state_vars::{Map, PublicMutable, SharedImmutable, PrivateSet}, - protocol_types::{ - abis::function_selector::FunctionSelector, - address::AztecAddress - } + note::{note_getter_options::NoteGetterOptions, note_header::NoteHeader, utils as note_utils}, + hash::{compute_secret_hash}, state_vars::{Map, PublicMutable, SharedImmutable, PrivateSet}, + protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress} }; // docs:start:import_authwit - use dep::authwit::{ - auth::{ - assert_current_call_valid_authwit, - assert_current_call_valid_authwit_public, - }, - }; + use dep::authwit::{auth::{assert_current_call_valid_authwit, assert_current_call_valid_authwit_public}}; // docs:end:import_authwit - use crate::types::{ - transparent_note::TransparentNote, - token_note::TokenNote, - balances_map::BalancesMap - }; + use crate::types::{transparent_note::TransparentNote, token_note::{TokenNote, TOKEN_NOTE_LEN}, balances_map::BalancesMap}; // docs:end::imports // docs:start:storage_struct @@ -57,11 +39,11 @@ contract Token { // docs:start:storage_balances balances: BalancesMap, // docs:end:storage_balances - total_supply: PublicMutable, + total_supply: PublicMutable, // docs:start:storage_pending_shields pending_shields: PrivateSet, // docs:end:storage_pending_shields - public_balances: Map>, + public_balances: Map>, symbol: SharedImmutable, name: SharedImmutable, // docs:start:storage_decimals @@ -158,7 +140,7 @@ contract Token { // docs:start:read_minter assert(storage.minters.at(context.msg_sender()).read(), "caller is not minter"); // docs:end:read_minter - let amount = SafeU120::new(amount); + let amount = U128::from_integer(amount); let new_balance = storage.public_balances.at(to).read().add(amount); let supply = storage.total_supply.read().add(amount); @@ -173,7 +155,7 @@ contract Token { assert(storage.minters.at(context.msg_sender()).read(), "caller is not minter"); let pending_shields = storage.pending_shields; let mut note = TransparentNote::new(amount, secret_hash); - let supply = storage.total_supply.read().add(SafeU120::new(amount)); + let supply = storage.total_supply.read().add(U128::from_integer(amount)); storage.total_supply.write(supply); // docs:start:insert_from_public @@ -184,15 +166,19 @@ contract Token { #[aztec(private)] fn privately_mint_private_note(amount: Field) { - storage.balances.add(context.msg_sender(), SafeU120::new(amount)); + storage.balances.add(context.msg_sender(), U128::from_integer(amount)); let selector = FunctionSelector::from_signature("assert_minter_and_mint((Field),Field)"); - let _void = context.call_public_function(context.this_address(), selector, [context.msg_sender().to_field(), amount]); + let _void = context.call_public_function( + context.this_address(), + selector, + [context.msg_sender().to_field(), amount] + ); } #[aztec(public)] internal fn assert_minter_and_mint(minter: AztecAddress, amount: Field) { assert(storage.minters.at(minter).read(), "caller is not minter"); - let supply = storage.total_supply.read().add(SafeU120::new(amount)); + let supply = storage.total_supply.read() + U128::from_integer(amount); storage.total_supply.write(supply); } @@ -206,11 +192,11 @@ contract Token { assert(nonce == 0, "invalid nonce"); } - let amount = SafeU120::new(amount); + let amount = U128::from_integer(amount); let from_balance = storage.public_balances.at(from).read().sub(amount); let pending_shields = storage.pending_shields; - let mut note = TransparentNote::new(amount.value as Field, secret_hash); + let mut note = TransparentNote::new(amount.to_integer(), secret_hash); storage.public_balances.at(from).write(from_balance); pending_shields.insert_from_public(&mut note); @@ -226,7 +212,7 @@ contract Token { assert(nonce == 0, "invalid nonce"); } - let amount = SafeU120::new(amount); + let amount = U128::from_integer(amount); let from_balance = storage.public_balances.at(from).read().sub(amount); storage.public_balances.at(from).write(from_balance); @@ -246,7 +232,7 @@ contract Token { } // docs:end:assert_current_call_valid_authwit_public - let amount = SafeU120::new(amount); + let amount = U128::from_integer(amount); let from_balance = storage.public_balances.at(from).read().sub(amount); storage.public_balances.at(from).write(from_balance); @@ -269,7 +255,7 @@ contract Token { pending_shields.remove(note); // Add the token note to user's balances set - storage.balances.add(to, SafeU120::new(amount)); + storage.balances.add(to, U128::from_integer(amount)); } // docs:end:redeem_shield @@ -282,7 +268,7 @@ contract Token { assert(nonce == 0, "invalid nonce"); } - storage.balances.sub(from, SafeU120::new(amount)); + storage.balances.sub(from, U128::from_integer(amount)); let selector = FunctionSelector::from_signature("_increase_public_balance((Field),Field)"); let _void = context.call_public_function(context.this_address(), selector, [to.to_field(), amount]); @@ -300,7 +286,7 @@ contract Token { } // docs:end:assert_current_call_valid_authwit - let amount = SafeU120::new(amount); + let amount = U128::from_integer(amount); storage.balances.sub(from, amount); // docs:start:increase_private_balance storage.balances.add(to, amount); @@ -317,7 +303,7 @@ contract Token { assert(nonce == 0, "invalid nonce"); } - storage.balances.sub(from, SafeU120::new(amount)); + storage.balances.sub(from, U128::from_integer(amount)); let selector = FunctionSelector::from_signature("_reduce_total_supply(Field)"); let _void = context.call_public_function(context.this_address(), selector, [amount]); @@ -348,7 +334,7 @@ contract Token { // docs:start:increase_public_balance #[aztec(public)] internal fn _increase_public_balance(to: AztecAddress, amount: Field) { - let new_balance = storage.public_balances.at(to).read().add(SafeU120::new(amount)); + let new_balance = storage.public_balances.at(to).read().add(U128::from_integer(amount)); storage.public_balances.at(to).write(new_balance); } // docs:end:increase_public_balance @@ -357,7 +343,7 @@ contract Token { #[aztec(public)] internal fn _reduce_total_supply(amount: Field) { // Only to be called from burn. - let new_supply = storage.total_supply.read().sub(SafeU120::new(amount)); + let new_supply = storage.total_supply.read().sub(U128::from_integer(amount)); storage.total_supply.write(new_supply); } // docs:end:reduce_total_supply @@ -377,20 +363,20 @@ contract Token { // docs:end:is_minter // docs:start:total_supply - unconstrained fn total_supply() -> pub u120 { - storage.total_supply.read().value + unconstrained fn total_supply() -> pub Field { + storage.total_supply.read().to_integer() } // docs:end:total_supply // docs:start:balance_of_private - unconstrained fn balance_of_private(owner: AztecAddress) -> pub u120 { - storage.balances.balance_of(owner).value + unconstrained fn balance_of_private(owner: AztecAddress) -> pub Field { + storage.balances.balance_of(owner).to_integer() } // docs:end:balance_of_private // docs:start:balance_of_public - unconstrained fn balance_of_public(owner: AztecAddress) -> pub u120 { - storage.public_balances.at(owner).read().value + unconstrained fn balance_of_public(owner: AztecAddress) -> pub Field { + storage.public_balances.at(owner).read().to_integer() } // docs:end:balance_of_public } diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr index 7e868cdf513..40e526d18d2 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr @@ -1,5 +1,4 @@ use dep::std::option::Option; -use dep::safe_math::SafeU120; use dep::aztec::{ context::{PrivateContext, PublicContext, Context}, hash::pedersen_hash, protocol_types::{address::AztecAddress, constants::MAX_READ_REQUESTS_PER_CALL}, @@ -30,7 +29,7 @@ impl BalancesMap { unconstrained pub fn balance_of( self: Self, owner: AztecAddress - ) -> SafeU120 where T: NoteInterface + OwnedNote { + ) -> U128 where T: NoteInterface + OwnedNote { self.balance_of_with_offset(owner, 0) } @@ -38,9 +37,9 @@ impl BalancesMap { self: Self, owner: AztecAddress, offset: u32 - ) -> SafeU120 where T: NoteInterface + OwnedNote { + ) -> U128 where T: NoteInterface + OwnedNote { // Same as SafeU120::new(0), but fewer constraints because no check. - let mut balance = SafeU120::min(); + let mut balance = U128::from_integer(0); // docs:start:view_notes let options = NoteViewerOptions::new().set_offset(offset); let opt_notes = self.map.at(owner).view_notes(options); @@ -48,11 +47,11 @@ impl BalancesMap { let len = opt_notes.len(); for i in 0..len { if opt_notes[i].is_some() { - balance = balance.add(opt_notes[i].unwrap_unchecked().get_amount()); + balance = balance + opt_notes[i].unwrap_unchecked().get_amount(); } } if (opt_notes[len - 1].is_some()) { - balance = balance.add(self.balance_of_with_offset(owner, offset + opt_notes.len() as u32)); + balance = balance + self.balance_of_with_offset(owner, offset + opt_notes.len() as u32); } balance @@ -61,7 +60,7 @@ impl BalancesMap { pub fn add( self: Self, owner: AztecAddress, - addend: SafeU120 + addend: U128 ) where T: NoteInterface + OwnedNote { let mut addend_note = T::new(addend, owner); @@ -73,14 +72,14 @@ impl BalancesMap { pub fn sub( self: Self, owner: AztecAddress, - subtrahend: SafeU120 + subtrahend: U128 ) where T: NoteInterface + OwnedNote { // docs:start:get_notes let options = NoteGetterOptions::with_filter(filter_notes_min_sum, subtrahend); let maybe_notes = self.map.at(owner).get_notes(options); // docs:end:get_notes - let mut minuend: SafeU120 = SafeU120::min(); + let mut minuend: U128 = U128::from_integer(0); for i in 0..maybe_notes.len() { if maybe_notes[i].is_some() { let note = maybe_notes[i].unwrap_unchecked(); @@ -93,27 +92,27 @@ impl BalancesMap { self.map.at(owner).remove(note); // docs:end:remove - minuend = minuend.add(note.get_amount()); + minuend = minuend + note.get_amount(); } } // This is to provide a nicer error msg, // without it minuend-subtrahend would still catch it, but more generic error then. // without the == true, it includes 'minuend.ge(subtrahend)' as part of the error. - assert(minuend.ge(subtrahend) == true, "Balance too low"); + assert(minuend >= subtrahend, "Balance too low"); - self.add(owner, minuend.sub(subtrahend)); + self.add(owner, minuend - subtrahend); } } pub fn filter_notes_min_sum( notes: [Option; MAX_READ_REQUESTS_PER_CALL], - min_sum: SafeU120 + min_sum: U128 ) -> [Option; MAX_READ_REQUESTS_PER_CALL] where T: NoteInterface + OwnedNote { let mut selected = [Option::none(); MAX_READ_REQUESTS_PER_CALL]; - let mut sum = SafeU120::min(); + let mut sum = U128::from_integer(0); for i in 0..notes.len() { - if notes[i].is_some() & sum.lt(min_sum) { + if notes[i].is_some() & sum < min_sum { let note = notes[i].unwrap_unchecked(); selected[i] = Option::some(note); sum = sum.add(note.get_amount()); diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr index 0039ad02257..2b03bb3c7fd 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr @@ -4,12 +4,11 @@ use dep::aztec::{ context::PrivateContext, log::emit_encrypted_log, hash::pedersen_hash }; use dep::aztec::oracle::{rand::rand, nullifier_key::get_nullifier_secret_key, get_public_key::get_public_key}; -use dep::safe_math::SafeU120; use dep::std::option::Option; trait OwnedNote { - fn new(amount: SafeU120, owner: AztecAddress) -> Self; - fn get_amount(self) -> SafeU120; + fn new(amount: U128, owner: AztecAddress) -> Self; + fn get_amount(self) -> U128; fn get_owner(self) -> AztecAddress; } @@ -17,7 +16,7 @@ global TOKEN_NOTE_LEN: Field = 3; // 3 plus a header. struct TokenNote { // the amount of tokens in the note - amount: SafeU120, + amount: U128, // the provider of secrets for the nullifier. The owner (recipient) to ensure that the note // can be privately spent. When nullifier secret and encryption private key is same // we can simply use the owner for this one. @@ -31,12 +30,13 @@ struct TokenNote { impl NoteInterface for TokenNote { fn serialize_content(self) -> [Field; TOKEN_NOTE_LEN] { - [self.amount.value as Field, self.owner.to_field(), self.randomness] + [self.amount.to_field(), self.owner.to_field(), self.randomness] } fn deserialize_content(serialized_note: [Field; TOKEN_NOTE_LEN]) -> Self { Self { - amount: SafeU120::new(serialized_note[0]), + // TODO: check this type cast is right + amount: U128::from_integer(serialized_note[0]), owner: AztecAddress::from_field(serialized_note[1]), randomness: serialized_note[2], header: NoteHeader::empty(), @@ -83,7 +83,7 @@ impl NoteInterface for TokenNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field) { // We only bother inserting the note if non-empty to save funds on gas. - if !self.amount.is_zero() { + if !(self.amount == U128::from_integer(0)) { let encryption_pub_key = get_public_key(self.owner); emit_encrypted_log( context, @@ -104,7 +104,7 @@ impl NoteInterface for TokenNote { } impl OwnedNote for TokenNote { - fn new(amount: SafeU120, owner: AztecAddress) -> Self { + fn new(amount: U128, owner: AztecAddress) -> Self { Self { amount, owner, @@ -113,7 +113,7 @@ impl OwnedNote for TokenNote { } } - fn get_amount(self) -> SafeU120 { + fn get_amount(self) -> U128 { self.amount } diff --git a/noir-projects/noir-protocol-circuits/src/crates/rollup-lib/src/tests/merkle_tree_utils.nr b/noir-projects/noir-protocol-circuits/src/crates/rollup-lib/src/tests/merkle_tree_utils.nr index 54941933816..31491802803 100644 --- a/noir-projects/noir-protocol-circuits/src/crates/rollup-lib/src/tests/merkle_tree_utils.nr +++ b/noir-projects/noir-protocol-circuits/src/crates/rollup-lib/src/tests/merkle_tree_utils.nr @@ -41,15 +41,10 @@ impl MerkleTree { node_index = (current_index_at_layer / 2) + layer_offset; current_width = current_width / 2; } - } pub fn sibling_index(index: u64) -> u64 { - if index % 2 == 0 { - index + 1 - } else { - index - 1 - } + if index % 2 == 0 { index + 1 } else { index - 1 } } } @@ -86,10 +81,14 @@ impl NonEmptyMerkl non_zero_leaves: [Field; SUBTREE_ITEMS], _tree_height: [Field; TREE_HEIGHT], _supertree_height: [Field; SUPERTREE_HEIGHT], - _subtree_height: [Field; SUBTREE_HEIGHT], + _subtree_height: [Field; SUBTREE_HEIGHT] ) -> Self { - assert_eq(TREE_HEIGHT, SUPERTREE_HEIGHT + SUBTREE_HEIGHT, "tree height must be the sum of supertree and subtree height"); - assert_eq(SUBTREE_ITEMS as u120, 1 << (SUBTREE_HEIGHT as u120), "subtree items must be 2^subtree height"); + assert_eq( + TREE_HEIGHT, SUPERTREE_HEIGHT + SUBTREE_HEIGHT, "tree height must be the sum of supertree and subtree height" + ); + assert_eq( + U128::from_integer(SUBTREE_ITEMS), U128::from_integer(1 as u32 << SUBTREE_HEIGHT), "subtree items must be 2^subtree height" + ); let subtree = MerkleTree::new(non_zero_leaves); let zero_hashes = compute_zero_hashes(_tree_height); @@ -97,15 +96,11 @@ impl NonEmptyMerkl let mut left_supertree_branch = [0; SUPERTREE_HEIGHT]; left_supertree_branch[0] = dep::std::hash::pedersen_hash([subtree.get_root(), zero_hashes[SUBTREE_HEIGHT-1]]); for i in 1..left_supertree_branch.len() { - left_supertree_branch[i] = dep::std::hash::pedersen_hash([left_supertree_branch[i-1], zero_hashes[SUBTREE_HEIGHT-1+i]]); - } - - NonEmptyMerkleTree { - subtree, - zero_hashes, - left_supertree_branch, - _phantom_subtree_height: _subtree_height + // TODO(md): far right of this yuck + left_supertree_branch[i] = dep::std::hash::pedersen_hash([left_supertree_branch[i-1], zero_hashes[(SUBTREE_HEIGHT-1 as u32) + i as u32]]); } + + NonEmptyMerkleTree { subtree, zero_hashes, left_supertree_branch, _phantom_subtree_height: _subtree_height } } pub fn get_sibling_path(self, leaf_index: Field) -> [Field; TREE_HEIGHT] { @@ -131,12 +126,10 @@ impl NonEmptyMerkl if sibling_index < subtree_width { path[i] = self.subtree.nodes[subtree_offset + sibling_index]; - }else { - if sibling_index == 0 { - path[i] = self.left_supertree_branch[i-1-SUBTREE_HEIGHT]; - }else { - path[i] = self.zero_hashes[i-1]; - } + } else if sibling_index == 0 { + path[i] = self.left_supertree_branch[i-1-SUBTREE_HEIGHT]; + } else { + path[i] = self.zero_hashes[i-1]; } subtree_offset += subtree_width; @@ -153,11 +146,11 @@ impl NonEmptyMerkl self.left_supertree_branch[0] = dep::std::hash::pedersen_hash([self.subtree.get_root(), self.zero_hashes[SUBTREE_HEIGHT-1]]); for i in 1..self.left_supertree_branch.len() { self.left_supertree_branch[i] = dep::std::hash::pedersen_hash([self.left_supertree_branch[i-1], self.zero_hashes[SUBTREE_HEIGHT-1+i]]); - } + } } pub fn get_root(self) -> Field { - self.left_supertree_branch[SUPERTREE_HEIGHT-1] + self.left_supertree_branch[SUPERTREE_HEIGHT - 1] } pub fn get_next_available_index(self) -> Field { diff --git a/noir-projects/noir-protocol-circuits/src/crates/types/src/traits.nr b/noir-projects/noir-protocol-circuits/src/crates/types/src/traits.nr index 65d734c73c2..ffb4ceecadb 100644 --- a/noir-projects/noir-protocol-circuits/src/crates/types/src/traits.nr +++ b/noir-projects/noir-protocol-circuits/src/crates/types/src/traits.nr @@ -19,9 +19,9 @@ impl Empty for Field { fn empty() -> Self {0} } impl Empty for u1 { fn empty() -> Self {0} } impl Empty for u8 { fn empty() -> Self {0} } -impl Empty for u16 { fn empty() -> Self {0} } impl Empty for u32 { fn empty() -> Self {0} } impl Empty for u64 { fn empty() -> Self {0} } +impl Empty for U128 { fn empty() -> Self {U128::from_integer(0)} } pub fn is_empty(item: T) -> bool where T: Empty + Eq { item.eq(T::empty()) @@ -45,6 +45,12 @@ impl ToField for Field { } } +impl ToField for U128 { + fn to_field(self) -> Field { + self.to_integer() + } +} + // docs:start:serialize trait Serialize { fn serialize(self) -> [Field; N]; diff --git a/noir-projects/noir-protocol-circuits/src/crates/types/src/type_serialization.nr b/noir-projects/noir-protocol-circuits/src/crates/types/src/type_serialization.nr index e817c228c7e..9b4929fc63d 100644 --- a/noir-projects/noir-protocol-circuits/src/crates/types/src/type_serialization.nr +++ b/noir-projects/noir-protocol-circuits/src/crates/types/src/type_serialization.nr @@ -52,3 +52,16 @@ impl Deserialize for bool { fields[0] as bool } } + +impl Serialize<1> for U128 { + fn serialize(self) -> [Field; 1] { + [self.to_integer()] + } + +} + +impl Deserialize<1> for U128 { + fn deserialize(fields: [Field; 1]) -> Self { + U128::from_integer(fields[0]) + } +} diff --git a/noir/.github/actions/setup/action.yml b/noir/.github/actions/setup/action.yml index b265a63d29a..d0e83dedf67 100644 --- a/noir/.github/actions/setup/action.yml +++ b/noir/.github/actions/setup/action.yml @@ -7,7 +7,7 @@ runs: - uses: actions/setup-node@v4 id: node with: - node-version: 18.17.1 + node-version: 18.19.0 cache: 'yarn' cache-dependency-path: 'yarn.lock' diff --git a/noir/.github/workflows/release.yml b/noir/.github/workflows/release.yml index 71a0ab6d894..83e8e479181 100644 --- a/noir/.github/workflows/release.yml +++ b/noir/.github/workflows/release.yml @@ -45,7 +45,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 18.17.1 + node-version: 18.19.0 cache: 'yarn' cache-dependency-path: 'yarn.lock' diff --git a/noir/.gitrepo b/noir/.gitrepo index 240e767e6f2..8b34412d5de 100644 --- a/noir/.gitrepo +++ b/noir/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/noir-lang/noir branch = master - commit = 78ef0134b82e76a73dadb6c7975def22290e3a1a - parent = e23d048e916fa12966fe01d1a8c0d3bfb50c2943 + commit = c44ef14847a436733206b6dd9590a7ab214ecd97 + parent = 382626cddaa175041695e2eb70ad3c350351ffe3 method = merge cmdver = 0.4.6 diff --git a/noir/aztec_macros/src/lib.rs b/noir/aztec_macros/src/lib.rs index 91efd74ddaa..07dca820fd9 100644 --- a/noir/aztec_macros/src/lib.rs +++ b/noir/aztec_macros/src/lib.rs @@ -1143,7 +1143,10 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac add_array_to_hasher( &id, &UnresolvedType { - typ: UnresolvedTypeData::Integer(Signedness::Unsigned, 32), + typ: UnresolvedTypeData::Integer( + Signedness::Unsigned, + noirc_frontend::IntegerBitSize::ThirtyTwo, + ), span: None, }, ) diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 84b719f29aa..7697d7e65fa 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -619,23 +619,29 @@ impl<'block> BrilligBlock<'block> { ); } Instruction::RangeCheck { value, max_bit_size, assert_message } => { - let left = self.convert_ssa_register_value(*value, dfg); - let max = BigUint::from(2_u128).pow(*max_bit_size); - let right = self.brillig_context.allocate_register(); - self.brillig_context.const_instruction( - right, + let value = self.convert_ssa_register_value(*value, dfg); + // Cast original value to field + let left = self.brillig_context.allocate_register(); + self.convert_cast(left, value, &Type::field()); + + // Create a field constant with the max + let max = BigUint::from(2_u128).pow(*max_bit_size) - BigUint::from(1_u128); + let right = self.brillig_context.make_constant( FieldElement::from_be_bytes_reduce(&max.to_bytes_be()).into(), FieldElement::max_num_bits(), ); + // Check if lte max let brillig_binary_op = BrilligBinaryOp::Integer { - op: BinaryIntOp::LessThan, - bit_size: max_bit_size + 1, + op: BinaryIntOp::LessThanEquals, + bit_size: FieldElement::max_num_bits(), }; let condition = self.brillig_context.allocate_register(); self.brillig_context.binary_instruction(left, right, condition, brillig_binary_op); + self.brillig_context.constrain_instruction(condition, assert_message.clone()); self.brillig_context.deallocate_register(condition); + self.brillig_context.deallocate_register(left); self.brillig_context.deallocate_register(right); } Instruction::IncrementRc { value } => { diff --git a/noir/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/noir/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index 6ee7f312660..845ffd15413 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -219,8 +219,8 @@ impl<'a> FunctionContext<'a> { let element_types = Self::convert_type(element).flatten(); Type::Array(Rc::new(element_types), *len as usize) } - ast::Type::Integer(Signedness::Signed, bits) => Type::signed(*bits), - ast::Type::Integer(Signedness::Unsigned, bits) => Type::unsigned(*bits), + ast::Type::Integer(Signedness::Signed, bits) => Type::signed((*bits).into()), + ast::Type::Integer(Signedness::Unsigned, bits) => Type::unsigned((*bits).into()), ast::Type::Bool => Type::unsigned(1), ast::Type::String(len) => Type::Array(Rc::new(vec![Type::char()]), *len as usize), ast::Type::FmtString(_, _) => { diff --git a/noir/compiler/noirc_frontend/src/ast/mod.rs b/noir/compiler/noirc_frontend/src/ast/mod.rs index 1223f822af3..29edbaca594 100644 --- a/noir/compiler/noirc_frontend/src/ast/mod.rs +++ b/noir/compiler/noirc_frontend/src/ast/mod.rs @@ -28,6 +28,55 @@ use crate::{ }; use iter_extended::vecmap; +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Ord, PartialOrd)] +pub enum IntegerBitSize { + One, + Eight, + ThirtyTwo, + SixtyFour, +} + +impl IntegerBitSize { + pub fn allowed_sizes() -> Vec { + vec![Self::One, Self::Eight, Self::ThirtyTwo, Self::SixtyFour] + } +} + +impl From for u32 { + fn from(size: IntegerBitSize) -> u32 { + use IntegerBitSize::*; + match size { + One => 1, + Eight => 8, + ThirtyTwo => 32, + SixtyFour => 64, + } + } +} + +pub struct InvalidIntegerBitSizeError(pub u32); + +impl TryFrom for IntegerBitSize { + type Error = InvalidIntegerBitSizeError; + + fn try_from(value: u32) -> Result { + use IntegerBitSize::*; + match value { + 1 => Ok(One), + 8 => Ok(Eight), + 32 => Ok(ThirtyTwo), + 64 => Ok(SixtyFour), + _ => Err(InvalidIntegerBitSizeError(value)), + } + } +} + +impl core::fmt::Display for IntegerBitSize { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", u32::from(*self)) + } +} + /// The parser parses types as 'UnresolvedType's which /// require name resolution to resolve any type names used /// for structs within, but are otherwise identical to Types. @@ -35,7 +84,7 @@ use iter_extended::vecmap; pub enum UnresolvedTypeData { FieldElement, Array(Option, Box), // [4]Witness = Array(4, Witness) - Integer(Signedness, u32), // u32 = Integer(unsigned, 32) + Integer(Signedness, IntegerBitSize), // u32 = Integer(unsigned, ThirtyTwo) Bool, Expression(UnresolvedTypeExpression), String(Option), @@ -197,11 +246,17 @@ impl UnresolvedType { } impl UnresolvedTypeData { - pub fn from_int_token(token: IntType) -> UnresolvedTypeData { + pub fn from_int_token( + token: IntType, + ) -> Result { use {IntType::*, UnresolvedTypeData::Integer}; match token { - Signed(num_bits) => Integer(Signedness::Signed, num_bits), - Unsigned(num_bits) => Integer(Signedness::Unsigned, num_bits), + Signed(num_bits) => { + Ok(Integer(Signedness::Signed, IntegerBitSize::try_from(num_bits)?)) + } + Unsigned(num_bits) => { + Ok(Integer(Signedness::Unsigned, IntegerBitSize::try_from(num_bits)?)) + } } } diff --git a/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs index bef5e113428..d4aae133b35 100644 --- a/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -29,7 +29,7 @@ use crate::hir::def_map::{LocalModuleId, ModuleDefId, TryFromModuleDefId, MAIN_F use crate::hir_def::stmt::{HirAssignStatement, HirForStatement, HirLValue, HirPattern}; use crate::node_interner::{ DefinitionId, DefinitionKind, DependencyId, ExprId, FuncId, GlobalId, NodeInterner, StmtId, - StructId, TraitId, TraitImplId, TraitMethodId, + StructId, TraitId, TraitImplId, TraitMethodId, TypeAliasId, }; use crate::{ hir::{def_map::CrateDefMap, resolution::path_resolver::PathResolver}, @@ -39,9 +39,9 @@ use crate::{ use crate::{ ArrayLiteral, ContractFunctionType, Distinctness, ForRange, FunctionDefinition, FunctionReturnType, FunctionVisibility, Generics, LValue, NoirStruct, NoirTypeAlias, Param, - Path, PathKind, Pattern, Shared, StructType, Type, TypeAliasType, TypeVariable, - TypeVariableKind, UnaryOp, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, - UnresolvedTypeData, UnresolvedTypeExpression, Visibility, ERROR_IDENT, + Path, PathKind, Pattern, Shared, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, + UnaryOp, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, + UnresolvedTypeExpression, Visibility, ERROR_IDENT, }; use fm::FileId; use iter_extended::vecmap; @@ -573,16 +573,19 @@ impl<'a> Resolver<'a> { let span = path.span(); let mut args = vecmap(args, |arg| self.resolve_type_inner(arg, new_variables)); - if let Some(type_alias_type) = self.lookup_type_alias(path.clone()) { - let expected_generic_count = type_alias_type.generics.len(); - let type_alias_string = type_alias_type.to_string(); - let id = type_alias_type.id; + if let Some(type_alias) = self.lookup_type_alias(path.clone()) { + let type_alias = type_alias.borrow(); + let expected_generic_count = type_alias.generics.len(); + let type_alias_string = type_alias.to_string(); + let id = type_alias.id; self.verify_generics_count(expected_generic_count, &mut args, span, || { type_alias_string }); - let result = self.interner.get_type_alias(id).get_type(&args); + if let Some(item) = self.current_item { + self.interner.add_type_alias_dependency(item, id); + } // Collecting Type Alias references [Location]s to be used by LSP in order // to resolve the definition of the type alias @@ -593,9 +596,8 @@ impl<'a> Resolver<'a> { // equal to another type alias. Fixing this fully requires an analysis to create a DFG // of definition ordering, but for now we have an explicit check here so that we at // least issue an error that the type was not found instead of silently passing. - if result != Type::Error { - return result; - } + let alias = self.interner.get_type_alias(id); + return Type::Alias(alias, args); } match self.lookup_struct_or_error(path) { @@ -752,12 +754,15 @@ impl<'a> Resolver<'a> { resolved_type } - pub fn resolve_type_aliases( + pub fn resolve_type_alias( mut self, unresolved: NoirTypeAlias, + alias_id: TypeAliasId, ) -> (Type, Generics, Vec) { let generics = self.add_generics(&unresolved.generics); self.resolve_local_globals(); + + self.current_item = Some(DependencyId::Alias(alias_id)); let typ = self.resolve_type(unresolved.typ); (typ, generics, self.errors) @@ -1120,6 +1125,17 @@ impl<'a> Resolver<'a> { } } } + Type::Alias(alias, generics) => { + for (i, generic) in generics.iter().enumerate() { + if let Type::NamedGeneric(type_variable, name) = generic { + if alias.borrow().generic_is_numeric(i) { + found.insert(name.to_string(), type_variable.clone()); + } + } else { + Self::find_numeric_generics_in_type(generic, found); + } + } + } Type::MutableReference(element) => Self::find_numeric_generics_in_type(element, found), Type::String(length) => { if let Type::NamedGeneric(type_variable, name) = length.as_ref() { @@ -1791,7 +1807,7 @@ impl<'a> Resolver<'a> { } } - fn lookup_type_alias(&mut self, path: Path) -> Option<&TypeAliasType> { + fn lookup_type_alias(&mut self, path: Path) -> Option> { self.lookup(path).ok().map(|id| self.interner.get_type_alias(id)) } diff --git a/noir/compiler/noirc_frontend/src/hir/resolution/type_aliases.rs b/noir/compiler/noirc_frontend/src/hir/resolution/type_aliases.rs index f66f6c8dfa7..2e5ce611a7f 100644 --- a/noir/compiler/noirc_frontend/src/hir/resolution/type_aliases.rs +++ b/noir/compiler/noirc_frontend/src/hir/resolution/type_aliases.rs @@ -17,7 +17,7 @@ pub(crate) fn resolve_type_aliases( crate_id: CrateId, ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; - for (type_id, unresolved_typ) in type_aliases { + for (alias_id, unresolved_typ) in type_aliases { let path_resolver = StandardPathResolver::new(ModuleId { local_id: unresolved_typ.module_id, krate: crate_id, @@ -25,9 +25,9 @@ pub(crate) fn resolve_type_aliases( let file = unresolved_typ.file_id; let (typ, generics, resolver_errors) = Resolver::new(&mut context.def_interner, &path_resolver, &context.def_maps, file) - .resolve_type_aliases(unresolved_typ.type_alias_def); + .resolve_type_alias(unresolved_typ.type_alias_def, alias_id); errors.extend(resolver_errors.iter().cloned().map(|e| (e.into(), file))); - context.def_interner.set_type_alias(type_id, typ, generics); + context.def_interner.set_type_alias(alias_id, typ, generics); } errors } diff --git a/noir/compiler/noirc_frontend/src/hir/type_check/errors.rs b/noir/compiler/noirc_frontend/src/hir/type_check/errors.rs index 3967d7642f7..96d30100d8b 100644 --- a/noir/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/noir/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -8,6 +8,7 @@ use crate::hir_def::expr::HirBinaryOp; use crate::hir_def::types::Type; use crate::BinaryOpKind; use crate::FunctionReturnType; +use crate::IntegerBitSize; use crate::Signedness; #[derive(Error, Debug, Clone, PartialEq, Eq)] @@ -67,7 +68,7 @@ pub enum TypeCheckError { #[error("Integers must have the same signedness LHS is {sign_x:?}, RHS is {sign_y:?}")] IntegerSignedness { sign_x: Signedness, sign_y: Signedness, span: Span }, #[error("Integers must have the same bit width LHS is {bit_width_x}, RHS is {bit_width_y}")] - IntegerBitWidth { bit_width_x: u32, bit_width_y: u32, span: Span }, + IntegerBitWidth { bit_width_x: IntegerBitSize, bit_width_y: IntegerBitSize, span: Span }, #[error("{kind} cannot be used in an infix operation")] InvalidInfixOp { kind: &'static str, span: Span }, #[error("{kind} cannot be used in a unary operation")] diff --git a/noir/compiler/noirc_frontend/src/hir/type_check/expr.rs b/noir/compiler/noirc_frontend/src/hir/type_check/expr.rs index b3180e0dd20..96a79152f69 100644 --- a/noir/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/noir/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -565,6 +565,7 @@ impl<'interner> TypeChecker<'interner> { Type::Integer(..) | Type::FieldElement | Type::TypeVariable(_, TypeVariableKind::IntegerOrField) + | Type::TypeVariable(_, TypeVariableKind::Integer) | Type::Bool => (), Type::TypeVariable(_, _) => { @@ -805,7 +806,7 @@ impl<'interner> TypeChecker<'interner> { // Matches on TypeVariable must be first to follow any type // bindings. - (TypeVariable(int, _), other) | (other, TypeVariable(int, _)) => { + (TypeVariable(int, int_kind), other) | (other, TypeVariable(int, int_kind)) => { if let TypeBinding::Bound(binding) = &*int.borrow() { return self.comparator_operand_type_rules(other, binding, op, span); } @@ -823,7 +824,13 @@ impl<'interner> TypeChecker<'interner> { } let mut bindings = TypeBindings::new(); - if other.try_bind_to_polymorphic_int(int, &mut bindings).is_ok() + if other + .try_bind_to_polymorphic_int( + int, + &mut bindings, + *int_kind == TypeVariableKind::Integer, + ) + .is_ok() || other == &Type::Error { Type::apply_type_bindings(bindings); @@ -837,6 +844,10 @@ impl<'interner> TypeChecker<'interner> { }) } } + (Alias(alias, args), other) | (other, Alias(alias, args)) => { + let alias = alias.borrow().get_type(args); + self.comparator_operand_type_rules(&alias, other, op, span) + } (Integer(sign_x, bit_width_x), Integer(sign_y, bit_width_y)) => { if sign_x != sign_y { return Err(TypeCheckError::IntegerSignedness { @@ -1081,7 +1092,7 @@ impl<'interner> TypeChecker<'interner> { // Matches on TypeVariable must be first so that we follow any type // bindings. - (TypeVariable(int, _), other) | (other, TypeVariable(int, _)) => { + (TypeVariable(int, int_kind), other) | (other, TypeVariable(int, int_kind)) => { if let TypeBinding::Bound(binding) = &*int.borrow() { return self.infix_operand_type_rules(binding, op, other, span); } @@ -1114,7 +1125,13 @@ impl<'interner> TypeChecker<'interner> { } let mut bindings = TypeBindings::new(); - if other.try_bind_to_polymorphic_int(int, &mut bindings).is_ok() + if other + .try_bind_to_polymorphic_int( + int, + &mut bindings, + *int_kind == TypeVariableKind::Integer, + ) + .is_ok() || other == &Type::Error { Type::apply_type_bindings(bindings); @@ -1128,6 +1145,10 @@ impl<'interner> TypeChecker<'interner> { }) } } + (Alias(alias, args), other) | (other, Alias(alias, args)) => { + let alias = alias.borrow().get_type(args); + self.infix_operand_type_rules(&alias, op, other, span) + } (Integer(sign_x, bit_width_x), Integer(sign_y, bit_width_y)) => { if sign_x != sign_y { return Err(TypeCheckError::IntegerSignedness { diff --git a/noir/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/noir/compiler/noirc_frontend/src/hir/type_check/stmt.rs index 1bd6c16277b..03d61b93e0c 100644 --- a/noir/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/noir/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -93,7 +93,7 @@ impl<'interner> TypeChecker<'interner> { match pattern { HirPattern::Identifier(ident) => self.interner.push_definition_type(ident.id, typ), HirPattern::Mutable(pattern, _) => self.bind_pattern(pattern, typ), - HirPattern::Tuple(fields, location) => match typ { + HirPattern::Tuple(fields, location) => match typ.follow_bindings() { Type::Tuple(field_types) if field_types.len() == fields.len() => { for (field, field_type) in fields.iter().zip(field_types) { self.bind_pattern(field, field_type); @@ -120,12 +120,12 @@ impl<'interner> TypeChecker<'interner> { source: Source::Assignment, }); - if let Type::Struct(struct_type, generics) = struct_type { + if let Type::Struct(struct_type, generics) = struct_type.follow_bindings() { let struct_type = struct_type.borrow(); for (field_name, field_pattern) in fields { if let Some((type_field, _)) = - struct_type.get_field(&field_name.0.contents, generics) + struct_type.get_field(&field_name.0.contents, &generics) { self.bind_pattern(field_pattern, type_field); } @@ -351,6 +351,7 @@ impl<'interner> TypeChecker<'interner> { HirExpression::Literal(HirLiteral::Integer(value, false)) => { let v = value.to_u128(); if let Type::Integer(_, bit_count) = annotated_type { + let bit_count: u32 = (*bit_count).into(); let max = 1 << bit_count; if v >= max { self.errors.push(TypeCheckError::OverflowingAssignment { diff --git a/noir/compiler/noirc_frontend/src/hir_def/types.rs b/noir/compiler/noirc_frontend/src/hir_def/types.rs index 30ca7054a77..98b47f17cd4 100644 --- a/noir/compiler/noirc_frontend/src/hir_def/types.rs +++ b/noir/compiler/noirc_frontend/src/hir_def/types.rs @@ -8,6 +8,7 @@ use std::{ use crate::{ hir::type_check::TypeCheckError, node_interner::{ExprId, NodeInterner, TraitId, TypeAliasId}, + IntegerBitSize, }; use iter_extended::vecmap; use noirc_errors::{Location, Span}; @@ -27,8 +28,8 @@ pub enum Type { Array(Box, Box), /// A primitive integer type with the given sign and bit count. - /// E.g. `u32` would be `Integer(Unsigned, 32)` - Integer(Signedness, u32), + /// E.g. `u32` would be `Integer(Unsigned, ThirtyTwo)` + Integer(Signedness, IntegerBitSize), /// The primitive `bool` type. Bool, @@ -44,13 +45,18 @@ pub enum Type { /// The unit type `()`. Unit, + /// A tuple type with the given list of fields in the order they appear in source code. + Tuple(Vec), + /// A user-defined struct type. The `Shared` field here refers to /// the shared definition for each instance of this struct type. The `Vec` /// represents the generic arguments (if any) to this struct type. Struct(Shared, Vec), - /// A tuple type with the given list of fields in the order they appear in source code. - Tuple(Vec), + /// A user-defined alias to another type. Similar to a Struct, this carries a shared + /// reference to the definition of the alias along with any generics that may have + /// been applied to the alias. + Alias(Shared, Vec), /// TypeVariables are stand-in variables for some type which is not yet known. /// They are not to be confused with NamedGenerics. While the later mostly works @@ -116,11 +122,16 @@ impl Type { let typ = typ.as_ref(); (length as u32) * typ.field_count() } - Type::Struct(ref def, args) => { + Type::Struct(def, args) => { let struct_type = def.borrow(); let fields = struct_type.get_fields(args); fields.iter().fold(0, |acc, (_, field_type)| acc + field_type.field_count()) } + Type::Alias(def, _) => { + // It is safe to access `typ` without instantiating generics here since generics + // cannot change the number of fields in `typ`. + def.borrow().typ.field_count() + } Type::Tuple(fields) => { fields.iter().fold(0, |acc, field_typ| acc + field_typ.field_count()) } @@ -309,7 +320,7 @@ impl std::fmt::Display for StructType { /// Wrap around an unsolved type #[derive(Debug, Clone, Eq)] -pub struct TypeAliasType { +pub struct TypeAlias { pub name: Ident, pub id: TypeAliasId, pub typ: Type, @@ -317,40 +328,33 @@ pub struct TypeAliasType { pub location: Location, } -impl std::hash::Hash for TypeAliasType { +impl std::hash::Hash for TypeAlias { fn hash(&self, state: &mut H) { self.id.hash(state); } } -impl PartialEq for TypeAliasType { +impl PartialEq for TypeAlias { fn eq(&self, other: &Self) -> bool { self.id == other.id } } -impl std::fmt::Display for TypeAliasType { +impl std::fmt::Display for TypeAlias { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.name)?; - - if !self.generics.is_empty() { - let generics = vecmap(&self.generics, |binding| binding.borrow().to_string()); - write!(f, "{}", generics.join(", "))?; - } - - Ok(()) + write!(f, "{}", self.name) } } -impl TypeAliasType { +impl TypeAlias { pub fn new( id: TypeAliasId, name: Ident, location: Location, typ: Type, generics: Generics, - ) -> TypeAliasType { - TypeAliasType { id, typ, name, location, generics } + ) -> TypeAlias { + TypeAlias { id, typ, name, location, generics } } pub fn set_type_and_generics(&mut self, new_typ: Type, new_generics: Generics) { @@ -371,6 +375,14 @@ impl TypeAliasType { self.typ.substitute(&substitutions) } + + /// True if the given index is the same index as a generic type of this alias + /// which is expected to be a numeric generic. + /// This is needed because we infer type kinds in Noir and don't have extensive kind checking. + pub fn generic_is_numeric(&self, index_of_generic: usize) -> bool { + let target_id = self.generics[index_of_generic].0; + self.typ.contains_numeric_typevar(target_id) + } } /// A shared, mutable reference to some T. @@ -441,6 +453,10 @@ pub enum TypeVariableKind { /// type annotations on each integer literal. IntegerOrField, + /// A generic integer type. This is a more specific kind of TypeVariable + /// that can only be bound to Type::Integer, or other polymorphic integers. + Integer, + /// A potentially constant array size. This will only bind to itself, Type::NotConstant, or /// Type::Constant(n) with a matching size. This defaults to Type::Constant(n) if still unbound /// during monomorphization. @@ -538,7 +554,7 @@ impl Type { } pub fn default_range_loop_type() -> Type { - Type::Integer(Signedness::Unsigned, 64) + Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour) } pub fn type_variable(id: TypeVariableId) -> Type { @@ -637,6 +653,13 @@ impl Type { } }) } + Type::Alias(alias, generics) => generics.iter().enumerate().any(|(i, generic)| { + if named_generic_id_matches_target(generic) { + alias.borrow().generic_is_numeric(i) + } else { + generic.contains_numeric_typevar(target_id) + } + }), Type::MutableReference(element) => element.contains_numeric_typevar(target_id), Type::String(length) => named_generic_id_matches_target(length), Type::FmtString(length, elements) => { @@ -673,6 +696,11 @@ impl Type { | Type::TraitAsType(..) | Type::NotConstant => false, + // This function is called during name resolution before we've verified aliases + // are not cyclic. As a result, it wouldn't be safe to check this alias' definition + // to see if the aliased type is valid. + Type::Alias(..) => false, + Type::Array(length, element) => { length.is_valid_for_program_input() && element.is_valid_for_program_input() } @@ -746,6 +774,13 @@ impl std::fmt::Display for Type { Signedness::Unsigned => write!(f, "u{num_bits}"), }, Type::TypeVariable(var, TypeVariableKind::Normal) => write!(f, "{}", var.borrow()), + Type::TypeVariable(binding, TypeVariableKind::Integer) => { + if let TypeBinding::Unbound(_) = &*binding.borrow() { + write!(f, "{}", TypeVariableKind::Integer.default_type()) + } else { + write!(f, "{}", binding.borrow()) + } + } Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { if let TypeBinding::Unbound(_) = &*binding.borrow() { // Show a Field by default if this TypeVariableKind::IntegerOrField is unbound, since that is @@ -772,6 +807,14 @@ impl std::fmt::Display for Type { write!(f, "{}<{}>", s.borrow(), args.join(", ")) } } + Type::Alias(alias, args) => { + let args = vecmap(args, |arg| arg.to_string()); + if args.is_empty() { + write!(f, "{}", alias.borrow()) + } else { + write!(f, "{}<{}>", alias.borrow(), args.join(", ")) + } + } Type::TraitAsType(_id, name, generics) => { write!(f, "impl {}", name)?; if !generics.is_empty() { @@ -863,7 +906,7 @@ impl Type { TypeBinding::Unbound(id) => *id, }; - let this = self.substitute(bindings); + let this = self.substitute(bindings).follow_bindings(); match &this { Type::Constant(length) if *length == target_length => { @@ -910,6 +953,7 @@ impl Type { Ok(()) } TypeVariableKind::IntegerOrField => Err(UnificationError), + TypeVariableKind::Integer => Err(UnificationError), }, } } @@ -924,13 +968,14 @@ impl Type { &self, var: &TypeVariable, bindings: &mut TypeBindings, + only_integer: bool, ) -> Result<(), UnificationError> { let target_id = match &*var.borrow() { TypeBinding::Bound(_) => unreachable!(), TypeBinding::Unbound(id) => *id, }; - let this = self.substitute(bindings); + let this = self.substitute(bindings).follow_bindings(); match &this { Type::FieldElement | Type::Integer(..) => { bindings.insert(target_id, (var.clone(), this)); @@ -939,7 +984,30 @@ impl Type { Type::TypeVariable(self_var, TypeVariableKind::IntegerOrField) => { let borrow = self_var.borrow(); match &*borrow { - TypeBinding::Bound(typ) => typ.try_bind_to_polymorphic_int(var, bindings), + TypeBinding::Bound(typ) => { + typ.try_bind_to_polymorphic_int(var, bindings, only_integer) + } + // Avoid infinitely recursive bindings + TypeBinding::Unbound(id) if *id == target_id => Ok(()), + TypeBinding::Unbound(new_target_id) => { + if only_integer { + // Integer is more specific than IntegerOrField so we bind the type + // variable to Integer instead. + let clone = Type::TypeVariable(var.clone(), TypeVariableKind::Integer); + bindings.insert(*new_target_id, (self_var.clone(), clone)); + } else { + bindings.insert(target_id, (var.clone(), this.clone())); + } + Ok(()) + } + } + } + Type::TypeVariable(self_var, TypeVariableKind::Integer) => { + let borrow = self_var.borrow(); + match &*borrow { + TypeBinding::Bound(typ) => { + typ.try_bind_to_polymorphic_int(var, bindings, only_integer) + } // Avoid infinitely recursive bindings TypeBinding::Unbound(id) if *id == target_id => Ok(()), TypeBinding::Unbound(_) => { @@ -948,18 +1016,23 @@ impl Type { } } } - Type::TypeVariable(binding, TypeVariableKind::Normal) => { - let borrow = binding.borrow(); + Type::TypeVariable(self_var, TypeVariableKind::Normal) => { + let borrow = self_var.borrow(); match &*borrow { - TypeBinding::Bound(typ) => typ.try_bind_to_polymorphic_int(var, bindings), + TypeBinding::Bound(typ) => { + typ.try_bind_to_polymorphic_int(var, bindings, only_integer) + } // Avoid infinitely recursive bindings TypeBinding::Unbound(id) if *id == target_id => Ok(()), TypeBinding::Unbound(new_target_id) => { - // IntegerOrField is more specific than TypeVariable so we bind the type - // variable to IntegerOrField instead. - let clone = - Type::TypeVariable(var.clone(), TypeVariableKind::IntegerOrField); - bindings.insert(*new_target_id, (binding.clone(), clone)); + // Bind to the most specific type variable kind + let clone_kind = if only_integer { + TypeVariableKind::Integer + } else { + TypeVariableKind::IntegerOrField + }; + let clone = Type::TypeVariable(var.clone(), clone_kind); + bindings.insert(*new_target_id, (self_var.clone(), clone)); Ok(()) } } @@ -1046,10 +1119,24 @@ impl Type { match (self, other) { (Error, _) | (_, Error) => Ok(()), + (Alias(alias, args), other) | (other, Alias(alias, args)) => { + let alias = alias.borrow().get_type(args); + alias.try_unify(other, bindings) + } + (TypeVariable(var, Kind::IntegerOrField), other) | (other, TypeVariable(var, Kind::IntegerOrField)) => { other.try_unify_to_type_variable(var, bindings, |bindings| { - other.try_bind_to_polymorphic_int(var, bindings) + let only_integer = false; + other.try_bind_to_polymorphic_int(var, bindings, only_integer) + }) + } + + (TypeVariable(var, Kind::Integer), other) + | (other, TypeVariable(var, Kind::Integer)) => { + other.try_unify_to_type_variable(var, bindings, |bindings| { + let only_integer = true; + other.try_bind_to_polymorphic_int(var, bindings, only_integer) }) } @@ -1407,7 +1494,7 @@ impl Type { Type::NamedGeneric(binding, _) | Type::TypeVariable(binding, _) => { substitute_binding(binding) } - // Do not substitute_helper fields, it ca, substitute_bound_typevarsn lead to infinite recursion + // Do not substitute_helper fields, it can lead to infinite recursion // and we should not match fields when type checking anyway. Type::Struct(fields, args) => { let args = vecmap(args, |arg| { @@ -1415,6 +1502,12 @@ impl Type { }); Type::Struct(fields.clone(), args) } + Type::Alias(alias, args) => { + let args = vecmap(args, |arg| { + arg.substitute_helper(type_bindings, substitute_bound_typevars) + }); + Type::Alias(alias.clone(), args) + } Type::Tuple(fields) => { let fields = vecmap(fields, |field| { field.substitute_helper(type_bindings, substitute_bound_typevars) @@ -1463,7 +1556,9 @@ impl Type { let field_occurs = fields.occurs(target_id); len_occurs || field_occurs } - Type::Struct(_, generic_args) => generic_args.iter().any(|arg| arg.occurs(target_id)), + Type::Struct(_, generic_args) | Type::Alias(_, generic_args) => { + generic_args.iter().any(|arg| arg.occurs(target_id)) + } Type::Tuple(fields) => fields.iter().any(|field| field.occurs(target_id)), Type::NamedGeneric(binding, _) | Type::TypeVariable(binding, _) => { match &*binding.borrow() { @@ -1514,6 +1609,11 @@ impl Type { let args = vecmap(args, |arg| arg.follow_bindings()); Struct(def.clone(), args) } + Alias(def, args) => { + // We don't need to vecmap(args, follow_bindings) since we're recursively + // calling follow_bindings here already. + def.borrow().get_type(args).follow_bindings() + } Tuple(args) => Tuple(vecmap(args, |arg| arg.follow_bindings())), TypeVariable(var, _) | NamedGeneric(var, _) => { if let TypeBinding::Bound(typ) = &*var.borrow() { @@ -1598,6 +1698,7 @@ impl TypeVariableKind { pub(crate) fn default_type(&self) -> Type { match self { TypeVariableKind::IntegerOrField | TypeVariableKind::Normal => Type::default_int_type(), + TypeVariableKind::Integer => Type::default_range_loop_type(), TypeVariableKind::Constant(length) => Type::Constant(*length), } } @@ -1621,8 +1722,14 @@ impl From<&Type> for PrintableType { PrintableType::Array { length, typ: Box::new(typ.into()) } } Type::Integer(sign, bit_width) => match sign { - Signedness::Unsigned => PrintableType::UnsignedInteger { width: *bit_width }, - Signedness::Signed => PrintableType::SignedInteger { width: *bit_width }, + Signedness::Unsigned => { + PrintableType::UnsignedInteger { width: (*bit_width).into() } + } + Signedness::Signed => PrintableType::SignedInteger { width: (*bit_width).into() }, + }, + Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() { + TypeBinding::Bound(typ) => typ.into(), + TypeBinding::Unbound(_) => Type::default_range_loop_type().into(), }, Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { match &*binding.borrow() { @@ -1645,6 +1752,7 @@ impl From<&Type> for PrintableType { let fields = vecmap(fields, |(name, typ)| (name, typ.into())); PrintableType::Struct { fields, name: struct_type.name.to_string() } } + Type::Alias(alias, args) => alias.borrow().get_type(args).into(), Type::TraitAsType(_, _, _) => unreachable!(), Type::Tuple(types) => PrintableType::Tuple { types: vecmap(types, |typ| typ.into()) }, Type::TypeVariable(_, _) => unreachable!(), @@ -1682,15 +1790,26 @@ impl std::fmt::Debug for Type { Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { write!(f, "IntOrField{:?}", binding) } + Type::TypeVariable(binding, TypeVariableKind::Integer) => { + write!(f, "Int{:?}", binding) + } Type::TypeVariable(binding, TypeVariableKind::Constant(n)) => { write!(f, "{}{:?}", n, binding) } Type::Struct(s, args) => { let args = vecmap(args, |arg| format!("{:?}", arg)); if args.is_empty() { - write!(f, "{:?}", s.borrow()) + write!(f, "{}", s.borrow()) + } else { + write!(f, "{}<{}>", s.borrow(), args.join(", ")) + } + } + Type::Alias(alias, args) => { + let args = vecmap(args, |arg| format!("{:?}", arg)); + if args.is_empty() { + write!(f, "{}", alias.borrow()) } else { - write!(f, "{:?}<{}>", s.borrow(), args.join(", ")) + write!(f, "{}<{}>", alias.borrow(), args.join(", ")) } } Type::TraitAsType(_id, name, generics) => { diff --git a/noir/compiler/noirc_frontend/src/lexer/errors.rs b/noir/compiler/noirc_frontend/src/lexer/errors.rs index a2a4056f1d0..35a07c11e0a 100644 --- a/noir/compiler/noirc_frontend/src/lexer/errors.rs +++ b/noir/compiler/noirc_frontend/src/lexer/errors.rs @@ -17,8 +17,6 @@ pub enum LexerErrorKind { InvalidIntegerLiteral { span: Span, found: String }, #[error("{:?} is not a valid attribute", found)] MalformedFuncAttribute { span: Span, found: String }, - #[error("Integer type is larger than the maximum supported size of u127")] - TooManyBits { span: Span, max: u32, got: u32 }, #[error("Logical and used instead of bitwise and")] LogicalAnd { span: Span }, #[error("Unterminated block comment")] @@ -45,7 +43,6 @@ impl LexerErrorKind { LexerErrorKind::NotADoubleChar { span, .. } => *span, LexerErrorKind::InvalidIntegerLiteral { span, .. } => *span, LexerErrorKind::MalformedFuncAttribute { span, .. } => *span, - LexerErrorKind::TooManyBits { span, .. } => *span, LexerErrorKind::LogicalAnd { span } => *span, LexerErrorKind::UnterminatedBlockComment { span } => *span, LexerErrorKind::UnterminatedStringLiteral { span } => *span, @@ -85,13 +82,6 @@ impl LexerErrorKind { format!(" {found} is not a valid attribute"), *span, ), - LexerErrorKind::TooManyBits { span, max, got } => ( - "Integer literal too large".to_string(), - format!( - "The maximum number of bits needed to represent a field is {max}, This integer type needs {got} bits" - ), - *span, - ), LexerErrorKind::LogicalAnd { span } => ( "Noir has no logical-and (&&) operator since short-circuiting is much less efficient when compiling to circuits".to_string(), "Try `&` instead, or use `if` only if you require short-circuiting".to_string(), diff --git a/noir/compiler/noirc_frontend/src/lexer/lexer.rs b/noir/compiler/noirc_frontend/src/lexer/lexer.rs index fd8168e36c6..cf66ece0c30 100644 --- a/noir/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/noir/compiler/noirc_frontend/src/lexer/lexer.rs @@ -307,7 +307,7 @@ impl<'a> Lexer<'a> { // Check if word an int type // if no error occurred, then it is either a valid integer type or it is not an int type - let parsed_token = IntType::lookup_int_type(&word, Span::inclusive(start, end))?; + let parsed_token = IntType::lookup_int_type(&word)?; // Check if it is an int type if let Some(int_type_token) = parsed_token { diff --git a/noir/compiler/noirc_frontend/src/lexer/token.rs b/noir/compiler/noirc_frontend/src/lexer/token.rs index 5d08ab03ad3..f7c07c5f5db 100644 --- a/noir/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/compiler/noirc_frontend/src/lexer/token.rs @@ -306,7 +306,7 @@ impl IntType { // XXX: Result // Is not the best API. We could split this into two functions. One that checks if the the // word is a integer, which only returns an Option - pub(crate) fn lookup_int_type(word: &str, span: Span) -> Result, LexerErrorKind> { + pub(crate) fn lookup_int_type(word: &str) -> Result, LexerErrorKind> { // Check if the first string is a 'u' or 'i' let is_signed = if word.starts_with('i') { @@ -324,12 +324,6 @@ impl IntType { Err(_) => return Ok(None), }; - let max_bits = FieldElement::max_num_bits() / 2; - - if str_as_u32 > max_bits { - return Err(LexerErrorKind::TooManyBits { span, max: max_bits, got: str_as_u32 }); - } - if is_signed { Ok(Some(Token::IntType(IntType::Signed(str_as_u32)))) } else { diff --git a/noir/compiler/noirc_frontend/src/monomorphization/ast.rs b/noir/compiler/noirc_frontend/src/monomorphization/ast.rs index 73e7ef372ab..e4e619d5d92 100644 --- a/noir/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/noir/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -6,7 +6,8 @@ use noirc_errors::{ }; use crate::{ - hir_def::function::FunctionSignature, BinaryOpKind, Distinctness, Signedness, Visibility, + hir_def::function::FunctionSignature, BinaryOpKind, Distinctness, IntegerBitSize, Signedness, + Visibility, }; /// The monomorphized AST is expression-based, all statements are also @@ -217,8 +218,8 @@ pub struct Function { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum Type { Field, - Array(/*len:*/ u64, Box), // Array(4, Field) = [Field; 4] - Integer(Signedness, /*bits:*/ u32), // u32 = Integer(unsigned, 32) + Array(/*len:*/ u64, Box), // Array(4, Field) = [Field; 4] + Integer(Signedness, /*bits:*/ IntegerBitSize), // u32 = Integer(unsigned, ThirtyTwo) Bool, String(/*len:*/ u64), // String(4) = str[4] FmtString(/*len:*/ u64, Box), diff --git a/noir/compiler/noirc_frontend/src/monomorphization/mod.rs b/noir/compiler/noirc_frontend/src/monomorphization/mod.rs index 21c095eb877..f691a0c9065 100644 --- a/noir/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -27,8 +27,8 @@ use crate::{ }, node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKind, TraitMethodId}, token::FunctionAttribute, - ContractFunctionType, FunctionKind, Type, TypeBinding, TypeBindings, TypeVariable, - TypeVariableKind, UnaryOp, Visibility, + ContractFunctionType, FunctionKind, IntegerBitSize, Type, TypeBinding, TypeBindings, + TypeVariable, TypeVariableKind, UnaryOp, Visibility, }; use self::ast::{Definition, FuncId, Function, LocalId, Program}; @@ -354,6 +354,7 @@ impl<'interner> Monomorphizer<'interner> { match typ { ast::Type::Field => Literal(Integer(-value, typ, location)), ast::Type::Integer(_, bit_size) => { + let bit_size: u32 = bit_size.into(); let base = 1_u128 << bit_size; Literal(Integer(FieldElement::from(base) - value, typ, location)) } @@ -802,12 +803,14 @@ impl<'interner> Monomorphizer<'interner> { // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - let default = - if self.is_range_loop && matches!(kind, TypeVariableKind::IntegerOrField) { - Type::default_range_loop_type() - } else { - kind.default_type() - }; + let default = if self.is_range_loop + && (matches!(kind, TypeVariableKind::IntegerOrField) + || matches!(kind, TypeVariableKind::Integer)) + { + Type::default_range_loop_type() + } else { + kind.default_type() + }; let monomorphized_default = self.convert_type(&default); binding.bind(default); @@ -820,6 +823,8 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::Tuple(fields) } + HirType::Alias(def, args) => self.convert_type(&def.borrow().get_type(args)), + HirType::Tuple(fields) => { let fields = vecmap(fields, |x| self.convert_type(x)); ast::Type::Tuple(fields) @@ -1109,19 +1114,19 @@ impl<'interner> Monomorphizer<'interner> { } "modulus_le_bits" => { let bits = FieldElement::modulus().to_radix_le(2); - Some(self.modulus_array_literal(bits, 1, location)) + Some(self.modulus_array_literal(bits, IntegerBitSize::One, location)) } "modulus_be_bits" => { let bits = FieldElement::modulus().to_radix_be(2); - Some(self.modulus_array_literal(bits, 1, location)) + Some(self.modulus_array_literal(bits, IntegerBitSize::One, location)) } "modulus_be_bytes" => { let bytes = FieldElement::modulus().to_bytes_be(); - Some(self.modulus_array_literal(bytes, 8, location)) + Some(self.modulus_array_literal(bytes, IntegerBitSize::Eight, location)) } "modulus_le_bytes" => { let bytes = FieldElement::modulus().to_bytes_le(); - Some(self.modulus_array_literal(bytes, 8, location)) + Some(self.modulus_array_literal(bytes, IntegerBitSize::Eight, location)) } _ => None, }; @@ -1133,7 +1138,7 @@ impl<'interner> Monomorphizer<'interner> { fn modulus_array_literal( &self, bytes: Vec, - arr_elem_bits: u32, + arr_elem_bits: IntegerBitSize, location: Location, ) -> ast::Expression { use ast::*; diff --git a/noir/compiler/noirc_frontend/src/node_interner.rs b/noir/compiler/noirc_frontend/src/node_interner.rs index 1841c4b790c..c2eeb2c0c50 100644 --- a/noir/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/compiler/noirc_frontend/src/node_interner.rs @@ -29,7 +29,7 @@ use crate::hir_def::{ use crate::token::{Attributes, SecondaryAttribute}; use crate::{ BinaryOpKind, ContractFunctionType, FunctionDefinition, FunctionVisibility, Generics, Shared, - TypeAliasType, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, + TypeAlias, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, }; /// An arbitrary number to limit the recursion depth when searching for trait impls. @@ -90,11 +90,12 @@ pub struct NodeInterner { structs: HashMap>, struct_attributes: HashMap, - // Type Aliases map. + + // Maps TypeAliasId -> Shared // // Map type aliases to the actual type. // When resolving types, check against this map to see if a type alias is defined. - pub(crate) type_aliases: Vec, + pub(crate) type_aliases: Vec>, // Trait map. // @@ -604,13 +605,13 @@ impl NodeInterner { pub fn push_type_alias(&mut self, typ: &UnresolvedTypeAlias) -> TypeAliasId { let type_id = TypeAliasId(self.type_aliases.len()); - self.type_aliases.push(TypeAliasType::new( + self.type_aliases.push(Shared::new(TypeAlias::new( type_id, typ.type_alias_def.name.clone(), Location::new(typ.type_alias_def.span, typ.file_id), Type::Error, vecmap(&typ.type_alias_def.generics, |_| TypeVariable::unbound(TypeVariableId(0))), - )); + ))); type_id } @@ -632,7 +633,7 @@ impl NodeInterner { pub fn set_type_alias(&mut self, type_id: TypeAliasId, typ: Type, generics: Generics) { let type_alias_type = &mut self.type_aliases[type_id.0]; - type_alias_type.set_type_and_generics(typ, generics); + type_alias_type.borrow_mut().set_type_and_generics(typ, generics); } /// Returns the interned statement corresponding to `stmt_id` @@ -957,8 +958,8 @@ impl NodeInterner { self.traits.get(&id) } - pub fn get_type_alias(&self, id: TypeAliasId) -> &TypeAliasType { - &self.type_aliases[id.0] + pub fn get_type_alias(&self, id: TypeAliasId) -> Shared { + self.type_aliases[id.0].clone() } pub fn get_global(&self, global_id: GlobalId) -> &GlobalInfo { @@ -1539,6 +1540,10 @@ impl NodeInterner { self.add_dependency(dependent, DependencyId::Function(dependency)); } + pub fn add_type_alias_dependency(&mut self, dependent: DependencyId, dependency: TypeAliasId) { + self.add_dependency(dependent, DependencyId::Alias(dependency)); + } + fn add_dependency(&mut self, dependent: DependencyId, dependency: DependencyId) { let dependent_index = self.get_or_insert_dependency(dependent); let dependency_index = self.get_or_insert_dependency(dependency); @@ -1585,6 +1590,12 @@ impl NodeInterner { } DependencyId::Alias(alias_id) => { let alias = self.get_type_alias(alias_id); + // If type aliases form a cycle, we have to manually break the cycle + // here to prevent infinite recursion in the type checker. + alias.borrow_mut().typ = Type::Error; + + // push_error will borrow the alias so we have to drop the mutable borrow + let alias = alias.borrow(); push_error(alias.name.to_string(), &scc, i, alias.location); break; } @@ -1606,7 +1617,7 @@ impl NodeInterner { DependencyId::Struct(id) => Cow::Owned(self.get_struct(id).borrow().name.to_string()), DependencyId::Function(id) => Cow::Borrowed(self.function_name(&id)), DependencyId::Alias(id) => { - Cow::Borrowed(self.get_type_alias(id).name.0.contents.as_ref()) + Cow::Owned(self.get_type_alias(id).borrow().name.to_string()) } DependencyId::Global(id) => { Cow::Borrowed(self.get_global(id).ident.0.contents.as_ref()) @@ -1700,6 +1711,7 @@ fn get_type_method_key(typ: &Type) -> Option { Type::Array(_, _) => Some(Array), Type::Integer(_, _) => Some(FieldOrInt), Type::TypeVariable(_, TypeVariableKind::IntegerOrField) => Some(FieldOrInt), + Type::TypeVariable(_, TypeVariableKind::Integer) => Some(FieldOrInt), Type::Bool => Some(Bool), Type::String(_) => Some(String), Type::FmtString(_, _) => Some(FmtString), @@ -1708,6 +1720,7 @@ fn get_type_method_key(typ: &Type) -> Option { Type::Function(_, _, _) => Some(Function), Type::NamedGeneric(_, _) => Some(Generic), Type::MutableReference(element) => get_type_method_key(element), + Type::Alias(alias, _) => get_type_method_key(&alias.borrow().typ), // We do not support adding methods to these types Type::TypeVariable(_, _) diff --git a/noir/compiler/noirc_frontend/src/parser/errors.rs b/noir/compiler/noirc_frontend/src/parser/errors.rs index 9158c68db72..43a1f96f13f 100644 --- a/noir/compiler/noirc_frontend/src/parser/errors.rs +++ b/noir/compiler/noirc_frontend/src/parser/errors.rs @@ -1,6 +1,7 @@ use crate::lexer::errors::LexerErrorKind; use crate::lexer::token::Token; use crate::Expression; +use crate::IntegerBitSize; use small_ord_set::SmallOrdSet; use thiserror::Error; @@ -40,8 +41,8 @@ pub enum ParserErrorReason { NoFunctionAttributesAllowedOnStruct, #[error("Assert statements can only accept string literals")] AssertMessageNotString, - #[error("Integer bit size {0} won't be supported")] - DeprecatedBitSize(u32), + #[error("Integer bit size {0} isn't supported")] + InvalidBitSize(u32), #[error("{0}")] Lexer(LexerErrorKind), } @@ -132,8 +133,6 @@ impl std::fmt::Display for ParserError { } } -pub(crate) static ALLOWED_INTEGER_BIT_SIZES: &[u32] = &[1, 8, 32, 64]; - impl From for Diagnostic { fn from(error: ParserError) -> Diagnostic { match error.reason { @@ -149,9 +148,9 @@ impl From for Diagnostic { "The 'comptime' keyword has been deprecated. It can be removed without affecting your program".into(), error.span, ), - ParserErrorReason::DeprecatedBitSize(bit_size) => Diagnostic::simple_warning( - format!("Use of deprecated bit size {}", bit_size), - format!("Bit sizes for integers will be restricted to {}", ALLOWED_INTEGER_BIT_SIZES.iter().map(|n| n.to_string()).collect::>().join(", ")), + ParserErrorReason::InvalidBitSize(bit_size) => Diagnostic::simple_error( + format!("Use of invalid bit size {}", bit_size), + format!("Allowed bit sizes for integers are {}", IntegerBitSize::allowed_sizes().iter().map(|n| n.to_string()).collect::>().join(", ")), error.span, ), ParserErrorReason::ExperimentalFeature(_) => Diagnostic::simple_warning( diff --git a/noir/compiler/noirc_frontend/src/parser/parser.rs b/noir/compiler/noirc_frontend/src/parser/parser.rs index b1ec18f5ec5..8bcd7670716 100644 --- a/noir/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/compiler/noirc_frontend/src/parser/parser.rs @@ -23,7 +23,6 @@ //! prevent other parsers from being tried afterward since there is no longer an error. Thus, they should //! be limited to cases like the above `fn` example where it is clear we shouldn't back out of the //! current parser to try alternative parsers in a `choice` expression. -use super::errors::ALLOWED_INTEGER_BIT_SIZES; use super::{ foldl_with_span, labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery, parenthesized, then_commit, then_commit_ignore, top_level_statement_recovery, ExprParser, @@ -36,7 +35,7 @@ use crate::ast::{ }; use crate::lexer::Lexer; use crate::parser::{force, ignore_then_commit, statement_recovery}; -use crate::token::{Attribute, Attributes, IntType, Keyword, SecondaryAttribute, Token, TokenKind}; +use crate::token::{Attribute, Attributes, Keyword, SecondaryAttribute, Token, TokenKind}; use crate::{ BinaryOp, BinaryOpKind, BlockExpression, ConstrainKind, ConstrainStatement, Distinctness, ForLoopStatement, ForRange, FunctionDefinition, FunctionReturnType, FunctionVisibility, Ident, @@ -1093,19 +1092,14 @@ fn int_type() -> impl NoirParser { Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) } })) - .validate(|int_type, span, emit| { - let bit_size = match int_type.1 { - IntType::Signed(bit_size) | IntType::Unsigned(bit_size) => bit_size, - }; - if !ALLOWED_INTEGER_BIT_SIZES.contains(&bit_size) { - emit(ParserError::with_reason( - ParserErrorReason::DeprecatedBitSize(bit_size), - span, - )); - } - int_type + .validate(|(_, token), span, emit| { + UnresolvedTypeData::from_int_token(token) + .map(|data| data.with_span(span)) + .unwrap_or_else(|err| { + emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); + UnresolvedType::error(span) + }) }) - .map_with_span(|(_, token), span| UnresolvedTypeData::from_int_token(token).with_span(span)) } fn named_type(type_parser: impl NoirParser) -> impl NoirParser { diff --git a/noir/compiler/noirc_frontend/src/resolve_locations.rs b/noir/compiler/noirc_frontend/src/resolve_locations.rs index cfb88966b9d..b5f1b1d0c64 100644 --- a/noir/compiler/noirc_frontend/src/resolve_locations.rs +++ b/noir/compiler/noirc_frontend/src/resolve_locations.rs @@ -212,6 +212,8 @@ impl NodeInterner { self.type_alias_ref .iter() .find(|(_, named_type_location)| named_type_location.span.contains(&location.span)) - .map(|(type_alias_id, _found_location)| self.get_type_alias(*type_alias_id).location) + .map(|(type_alias_id, _found_location)| { + self.get_type_alias(*type_alias_id).borrow().location + }) } } diff --git a/noir/compiler/noirc_frontend/src/tests.rs b/noir/compiler/noirc_frontend/src/tests.rs index 1deff446d7e..8a56b337398 100644 --- a/noir/compiler/noirc_frontend/src/tests.rs +++ b/noir/compiler/noirc_frontend/src/tests.rs @@ -1184,4 +1184,26 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { "#; assert_eq!(get_program_errors(src).len(), 1); } + + #[test] + fn deny_cyclic_type_aliases() { + let src = r#" + type A = B; + type B = A; + fn main() {} + "#; + assert_eq!(get_program_errors(src).len(), 1); + } + + #[test] + fn ensure_nested_type_aliases_type_check() { + let src = r#" + type A = B; + type B = u8; + fn main() { + let _a: A = 0 as u16; + } + "#; + assert_eq!(get_program_errors(src).len(), 1); + } } diff --git a/noir/cspell.json b/noir/cspell.json index 19e9a175ce0..2acca0633d3 100644 --- a/noir/cspell.json +++ b/noir/cspell.json @@ -106,6 +106,7 @@ "lvalue", "Maddiaa", "mathbb", + "memfs", "merkle", "metas", "minreq", diff --git a/noir/docs/docs/noir/concepts/data_types/index.md b/noir/docs/docs/noir/concepts/data_types/index.md index 3c9cd4c2437..97b3b2cb094 100644 --- a/noir/docs/docs/noir/concepts/data_types/index.md +++ b/noir/docs/docs/noir/concepts/data_types/index.md @@ -91,6 +91,20 @@ fn main() { } ``` +Type aliases can even refer to other aliases. An error will be issued if they form a cycle: + +```rust +// Ok! +type A = B; +type B = Field; + +type Bad1 = Bad2; + +// error: Dependency cycle found +type Bad2 = Bad1; +// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 +``` + ### BigInt You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/noir/docs/docs/noir/concepts/data_types/integers.md b/noir/docs/docs/noir/concepts/data_types/integers.md index 30135d76e4a..4d58d96fed5 100644 --- a/noir/docs/docs/noir/concepts/data_types/integers.md +++ b/noir/docs/docs/noir/concepts/data_types/integers.md @@ -5,7 +5,7 @@ keywords: [noir, integer types, methods, examples, arithmetic] sidebar_position: 1 --- -An integer type is a range constrained field type. The Noir frontend supports arbitrarily-sized, both unsigned and signed integer types. +An integer type is a range constrained field type. The Noir frontend supports both unsigned and signed integer types. The allowed sizes are 1, 8, 32 and 64 bits. :::info @@ -45,13 +45,6 @@ fn main() { The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). -:::tip - -If you are using the default proving backend with Noir, both even (e.g. _u2_, _i2_) and odd (e.g. _u3_, _i3_) arbitrarily-sized integer types up to 127 bits (i.e. _u127_ and _i127_) are supported. - -::: - - ## 128 bits Unsigned Integers The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: diff --git a/noir/noir_stdlib/src/uint128.nr b/noir/noir_stdlib/src/uint128.nr index c8c6217de90..e2ee8013d48 100644 --- a/noir/noir_stdlib/src/uint128.nr +++ b/noir/noir_stdlib/src/uint128.nr @@ -180,7 +180,7 @@ impl Sub for U128 { let borrow = (low == lo) as Field; let high = self.hi - b.hi - borrow; let hi = high as u64 as Field; - assert(hi == high, "attempt to subtract with overflow"); + assert(hi == high, "attempt to subtract with underflow"); U128 { lo, hi, diff --git a/noir/test_programs/compile_failure/integer_literal_overflow/src/main.nr b/noir/test_programs/compile_failure/integer_literal_overflow/src/main.nr index d89505c0085..e4d21b5c3b9 100644 --- a/noir/test_programs/compile_failure/integer_literal_overflow/src/main.nr +++ b/noir/test_programs/compile_failure/integer_literal_overflow/src/main.nr @@ -2,4 +2,4 @@ fn main() { foo(1234) } -fn foo(_x: u4) {} +fn foo(_x: u8) {} diff --git a/noir/test_programs/execution_success/regression_2854/Nargo.toml b/noir/test_programs/compile_failure/restricted_bit_sizes/Nargo.toml similarity index 63% rename from noir/test_programs/execution_success/regression_2854/Nargo.toml rename to noir/test_programs/compile_failure/restricted_bit_sizes/Nargo.toml index fb2b3c42fdd..36f8253e8e7 100644 --- a/noir/test_programs/execution_success/regression_2854/Nargo.toml +++ b/noir/test_programs/compile_failure/restricted_bit_sizes/Nargo.toml @@ -1,6 +1,5 @@ [package] -name = "regression_2854" +name = "restricted_bit_sizes" type = "bin" authors = [""] - [dependencies] diff --git a/noir/test_programs/compile_failure/restricted_bit_sizes/src/main.nr b/noir/test_programs/compile_failure/restricted_bit_sizes/src/main.nr new file mode 100644 index 00000000000..01e72bfcfd7 --- /dev/null +++ b/noir/test_programs/compile_failure/restricted_bit_sizes/src/main.nr @@ -0,0 +1,5 @@ +use dep::std::assert_constant; + +fn main() -> pub u63 { + 5 +} diff --git a/noir/test_programs/execution_success/regression_2854/Prover.toml b/noir/test_programs/execution_success/regression_2854/Prover.toml deleted file mode 100644 index 07890234a19..00000000000 --- a/noir/test_programs/execution_success/regression_2854/Prover.toml +++ /dev/null @@ -1 +0,0 @@ -x = "3" diff --git a/noir/test_programs/execution_success/regression_2854/src/main.nr b/noir/test_programs/execution_success/regression_2854/src/main.nr deleted file mode 100644 index eccff8225b6..00000000000 --- a/noir/test_programs/execution_success/regression_2854/src/main.nr +++ /dev/null @@ -1,3 +0,0 @@ -fn main(x: Field) -> pub i127 { - x as i127 -} diff --git a/noir/tooling/noirc_abi/src/lib.rs b/noir/tooling/noirc_abi/src/lib.rs index 1fc257c1676..26feab65d83 100644 --- a/noir/tooling/noirc_abi/src/lib.rs +++ b/noir/tooling/noirc_abi/src/lib.rs @@ -142,14 +142,13 @@ impl AbiType { Signedness::Signed => Sign::Signed, }; - Self::Integer { sign, width: *bit_width } - } - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { - match &*binding.borrow() { - TypeBinding::Bound(typ) => Self::from_type(context, typ), - TypeBinding::Unbound(_) => Self::from_type(context, &Type::default_int_type()), - } + Self::Integer { sign, width: (*bit_width).into() } } + Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) + | Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() { + TypeBinding::Bound(typ) => Self::from_type(context, typ), + TypeBinding::Unbound(_) => Self::from_type(context, &Type::default_int_type()), + }, Type::Bool => Self::Boolean, Type::String(size) => { let size = size @@ -158,7 +157,7 @@ impl AbiType { Self::String { length: size } } - Type::Struct(def, ref args) => { + Type::Struct(def, args) => { let struct_type = def.borrow(); let fields = struct_type.get_fields(args); let fields = vecmap(fields, |(name, typ)| (name, Self::from_type(context, &typ))); @@ -167,6 +166,7 @@ impl AbiType { context.fully_qualified_struct_path(context.root_crate_id(), struct_type.id); Self::Struct { fields, path } } + Type::Alias(def, args) => Self::from_type(context, &def.borrow().get_type(args)), Type::Tuple(fields) => { let fields = vecmap(fields, |typ| Self::from_type(context, typ)); Self::Tuple { fields } diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts index 4825c04e63a..7f6b7c2e34b 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts @@ -20,6 +20,7 @@ import { SlowTreeContract, TokenBlacklistContract, TokenContract } from '@aztec/ import { jest } from '@jest/globals'; +import { BITSIZE_TOO_BIG_ERROR, U128_OVERFLOW_ERROR, U128_UNDERFLOW_ERROR } from './fixtures/fixtures.js'; import { setup } from './fixtures/utils.js'; import { TokenSimulator } from './simulators/token_simulator.js'; @@ -357,24 +358,24 @@ describe('e2e_blacklist_token_contract', () => { ).rejects.toThrowError('Assertion failed: caller is not minter'); }); - it('mint >u120 tokens to overflow', async () => { - const amount = 2n ** 120n; // SafeU120::max() + 1; + it('mint >u128 tokens to overflow', async () => { + const amount = 2n ** 128n; // U128::max() + 1; await expect(asset.methods.mint_public(accounts[0].address, amount).simulate()).rejects.toThrowError( - 'Assertion failed: Value too large for SafeU120', + BITSIZE_TOO_BIG_ERROR, ); }); - it('mint u120', async () => { - const amount = 2n ** 120n - tokenSim.balanceOfPublic(accounts[0].address); + it('mint u128', async () => { + const amount = 2n ** 128n - tokenSim.balanceOfPublic(accounts[0].address); await expect(asset.methods.mint_public(accounts[0].address, amount).simulate()).rejects.toThrowError( - 'Assertion failed: Overflow', + U128_OVERFLOW_ERROR, ); }); - it('mint u120', async () => { - const amount = 2n ** 120n - tokenSim.balanceOfPublic(accounts[0].address); + it('mint u128', async () => { + const amount = 2n ** 128n - tokenSim.balanceOfPublic(accounts[0].address); await expect(asset.methods.mint_public(accounts[1].address, amount).simulate()).rejects.toThrowError( - 'Assertion failed: Overflow', + U128_OVERFLOW_ERROR, ); }); @@ -440,26 +441,26 @@ describe('e2e_blacklist_token_contract', () => { ).rejects.toThrowError('Assertion failed: caller is not minter'); }); - it('mint >u120 tokens to overflow', async () => { - const amount = 2n ** 120n; // SafeU120::max() + 1; + it('mint >u128 tokens to overflow', async () => { + const amount = 2n ** 128n; // U128::max() + 1; await expect(asset.methods.mint_private(amount, secretHash).simulate()).rejects.toThrowError( - 'Assertion failed: Value too large for SafeU120', + BITSIZE_TOO_BIG_ERROR, ); }); - it('mint u120', async () => { + it('mint u128', async () => { // @todo @LHerskind this one don't make sense. It fails because of total supply overflowing. - const amount = 2n ** 120n - tokenSim.balanceOfPrivate(accounts[0].address); - expect(amount).toBeLessThan(2n ** 120n); + const amount = 2n ** 128n - tokenSim.balanceOfPrivate(accounts[0].address); + expect(amount).toBeLessThan(2n ** 128n); await expect(asset.methods.mint_private(amount, secretHash).simulate()).rejects.toThrowError( - 'Assertion failed: Overflow', + U128_OVERFLOW_ERROR, ); }); - it('mint u120', async () => { - const amount = 2n ** 120n - tokenSim.totalSupply; + it('mint u128', async () => { + const amount = 2n ** 128n - tokenSim.totalSupply; await expect(asset.methods.mint_private(amount, secretHash).simulate()).rejects.toThrowError( - 'Assertion failed: Overflow', + U128_OVERFLOW_ERROR, ); }); @@ -536,7 +537,7 @@ describe('e2e_blacklist_token_contract', () => { const nonce = 0; await expect( asset.methods.transfer_public(accounts[0].address, accounts[1].address, amount, nonce).simulate(), - ).rejects.toThrowError('Assertion failed: Underflow'); + ).rejects.toThrowError(U128_UNDERFLOW_ERROR); }); it('transfer on behalf of self with non-zero nonce', async () => { @@ -576,7 +577,7 @@ describe('e2e_blacklist_token_contract', () => { await wallets[0].setPublicAuth(messageHash, true).send().wait(); // Perform the transfer - await expect(action.simulate()).rejects.toThrowError('Assertion failed: Underflow'); + await expect(action.simulate()).rejects.toThrowError(U128_UNDERFLOW_ERROR); expect(await asset.methods.balance_of_public(accounts[0].address).view()).toEqual(balance0); expect(await asset.methods.balance_of_public(accounts[1].address).view()).toEqual(balance1); @@ -944,7 +945,7 @@ describe('e2e_blacklist_token_contract', () => { expect(amount).toBeGreaterThan(0n); await expect(asset.methods.shield(accounts[0].address, amount, secretHash, 0).simulate()).rejects.toThrowError( - 'Assertion failed: Underflow', + U128_UNDERFLOW_ERROR, ); }); @@ -969,7 +970,7 @@ describe('e2e_blacklist_token_contract', () => { const messageHash = computeAuthWitMessageHash(accounts[1].address, action.request()); await wallets[0].setPublicAuth(messageHash, true).send().wait(); - await expect(action.simulate()).rejects.toThrowError('Assertion failed: Underflow'); + await expect(action.simulate()).rejects.toThrowError(U128_UNDERFLOW_ERROR); }); it('on behalf of other (wrong designated caller)', async () => { @@ -1218,7 +1219,7 @@ describe('e2e_blacklist_token_contract', () => { const amount = balance0 + 1n; const nonce = 0; await expect(asset.methods.burn_public(accounts[0].address, amount, nonce).simulate()).rejects.toThrowError( - 'Assertion failed: Underflow', + U128_UNDERFLOW_ERROR, ); }); @@ -1252,7 +1253,7 @@ describe('e2e_blacklist_token_contract', () => { const messageHash = computeAuthWitMessageHash(accounts[1].address, action.request()); await wallets[0].setPublicAuth(messageHash, true).send().wait(); - await expect(action.simulate()).rejects.toThrowError('Assertion failed: Underflow'); + await expect(action.simulate()).rejects.toThrowError(U128_UNDERFLOW_ERROR); }); it('burn on behalf of other, wrong designated caller', async () => { diff --git a/yarn-project/end-to-end/src/e2e_token_contract.test.ts b/yarn-project/end-to-end/src/e2e_token_contract.test.ts index bb667a39179..f8cfb7db8ef 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract.test.ts @@ -17,6 +17,7 @@ import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { jest } from '@jest/globals'; +import { BITSIZE_TOO_BIG_ERROR, U128_OVERFLOW_ERROR, U128_UNDERFLOW_ERROR } from './fixtures/fixtures.js'; import { setup } from './fixtures/utils.js'; import { TokenSimulator } from './simulators/token_simulator.js'; @@ -239,24 +240,24 @@ describe('e2e_token_contract', () => { ).rejects.toThrowError('Assertion failed: caller is not minter'); }); - it('mint >u120 tokens to overflow', async () => { - const amount = 2n ** 120n; // SafeU120::max() + 1; + it('mint >u128 tokens to overflow', async () => { + const amount = 2n ** 128n; // U128::max() + 1; await expect(asset.methods.mint_public(accounts[0].address, amount).simulate()).rejects.toThrowError( - 'Assertion failed: Value too large for SafeU120', + BITSIZE_TOO_BIG_ERROR, ); }); - it('mint u120', async () => { - const amount = 2n ** 120n - tokenSim.balanceOfPublic(accounts[0].address); + it('mint u128', async () => { + const amount = 2n ** 128n - tokenSim.balanceOfPublic(accounts[0].address); await expect(asset.methods.mint_public(accounts[0].address, amount).simulate()).rejects.toThrowError( - 'Assertion failed: Overflow', + U128_OVERFLOW_ERROR, ); }); - it('mint u120', async () => { - const amount = 2n ** 120n - tokenSim.balanceOfPublic(accounts[0].address); + it('mint u128', async () => { + const amount = 2n ** 128n - tokenSim.balanceOfPublic(accounts[0].address); await expect(asset.methods.mint_public(accounts[1].address, amount).simulate()).rejects.toThrowError( - 'Assertion failed: Overflow', + U128_OVERFLOW_ERROR, ); }); }); @@ -312,25 +313,25 @@ describe('e2e_token_contract', () => { ).rejects.toThrowError('Assertion failed: caller is not minter'); }); - it('mint >u120 tokens to overflow', async () => { - const amount = 2n ** 120n; // SafeU120::max() + 1; + it('mint >u128 tokens to overflow', async () => { + const amount = 2n ** 128n; // U128::max() + 1; await expect(asset.methods.mint_private(amount, secretHash).simulate()).rejects.toThrowError( - 'Assertion failed: Value too large for SafeU120', + BITSIZE_TOO_BIG_ERROR, ); }); - it('mint u120', async () => { - const amount = 2n ** 120n - tokenSim.balanceOfPrivate(accounts[0].address); - expect(amount).toBeLessThan(2n ** 120n); + it('mint u128', async () => { + const amount = 2n ** 128n - tokenSim.balanceOfPrivate(accounts[0].address); + expect(amount).toBeLessThan(2n ** 128n); await expect(asset.methods.mint_private(amount, secretHash).simulate()).rejects.toThrowError( - 'Assertion failed: Overflow', + U128_OVERFLOW_ERROR, ); }); - it('mint u120', async () => { - const amount = 2n ** 120n - tokenSim.totalSupply; + it('mint u128', async () => { + const amount = 2n ** 128n - tokenSim.totalSupply; await expect(asset.methods.mint_private(amount, secretHash).simulate()).rejects.toThrowError( - 'Assertion failed: Overflow', + U128_OVERFLOW_ERROR, ); }); }); @@ -398,7 +399,7 @@ describe('e2e_token_contract', () => { const nonce = 0; await expect( asset.methods.transfer_public(accounts[0].address, accounts[1].address, amount, nonce).simulate(), - ).rejects.toThrowError('Assertion failed: Underflow'); + ).rejects.toThrowError(U128_UNDERFLOW_ERROR); }); it('transfer on behalf of self with non-zero nonce', async () => { @@ -438,7 +439,7 @@ describe('e2e_token_contract', () => { await wallets[0].setPublicAuth(messageHash, true).send().wait(); // Perform the transfer - await expect(action.simulate()).rejects.toThrowError('Assertion failed: Underflow'); + await expect(action.simulate()).rejects.toThrowError(U128_UNDERFLOW_ERROR); expect(await asset.methods.balance_of_public(accounts[0].address).view()).toEqual(balance0); expect(await asset.methods.balance_of_public(accounts[1].address).view()).toEqual(balance1); @@ -713,7 +714,7 @@ describe('e2e_token_contract', () => { expect(amount).toBeGreaterThan(0n); await expect(asset.methods.shield(accounts[0].address, amount, secretHash, 0).simulate()).rejects.toThrowError( - 'Assertion failed: Underflow', + U128_UNDERFLOW_ERROR, ); }); @@ -738,7 +739,7 @@ describe('e2e_token_contract', () => { const messageHash = computeAuthWitMessageHash(accounts[1].address, action.request()); await wallets[0].setPublicAuth(messageHash, true).send().wait(); - await expect(action.simulate()).rejects.toThrowError('Assertion failed: Underflow'); + await expect(action.simulate()).rejects.toThrowError(U128_UNDERFLOW_ERROR); }); it('on behalf of other (wrong designated caller)', async () => { @@ -921,7 +922,7 @@ describe('e2e_token_contract', () => { const amount = balance0 + 1n; const nonce = 0; await expect(asset.methods.burn_public(accounts[0].address, amount, nonce).simulate()).rejects.toThrowError( - 'Assertion failed: Underflow', + U128_UNDERFLOW_ERROR, ); }); @@ -955,7 +956,7 @@ describe('e2e_token_contract', () => { const messageHash = computeAuthWitMessageHash(accounts[1].address, action.request()); await wallets[0].setPublicAuth(messageHash, true).send().wait(); - await expect(action.simulate()).rejects.toThrowError('Assertion failed: Underflow'); + await expect(action.simulate()).rejects.toThrowError(U128_UNDERFLOW_ERROR); }); it('burn on behalf of other, wrong designated caller', async () => { diff --git a/yarn-project/end-to-end/src/fixtures/fixtures.ts b/yarn-project/end-to-end/src/fixtures/fixtures.ts index 3e4ffc7c8ae..c33f3929718 100644 --- a/yarn-project/end-to-end/src/fixtures/fixtures.ts +++ b/yarn-project/end-to-end/src/fixtures/fixtures.ts @@ -1,3 +1,9 @@ export const MNEMONIC = 'test test test test test test test test test test test junk'; export const privateKey = Buffer.from('ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', 'hex'); export const privateKey2 = Buffer.from('59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d', 'hex'); + +/// Common errors +export const U128_UNDERFLOW_ERROR = "Assertion failed: attempt to subtract with underflow 'hi == high'"; +export const U128_OVERFLOW_ERROR = "Assertion failed: attempt to add with overflow 'hi == high'"; +export const BITSIZE_TOO_BIG_ERROR = + "Assertion failed: call to assert_max_bit_size 'self.__assert_max_bit_size(bit_size)'"; diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index f418ad9c50f..d4ffb9b529a 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -13,6 +13,8 @@ import { import { TestContract } from '@aztec/noir-contracts.js/Test'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; +import { U128_UNDERFLOW_ERROR } from '../fixtures/fixtures.js'; + const { PXE_URL = 'http://localhost:8080', ETHEREUM_HOST = 'http://localhost:8545' } = process.env; describe('guides/dapp/testing', () => { @@ -244,7 +246,7 @@ describe('guides/dapp/testing', () => { it('asserts a simulation for a public function call fails', async () => { // docs:start:local-pub-fails const call = token.methods.transfer_public(owner.getAddress(), recipient.getAddress(), 1000n, 0); - await expect(call.simulate()).rejects.toThrowError(/Underflow/); + await expect(call.simulate()).rejects.toThrowError(U128_UNDERFLOW_ERROR); // docs:end:local-pub-fails }, 30_000); diff --git a/yarn-project/end-to-end/src/simulators/lending_simulator.ts b/yarn-project/end-to-end/src/simulators/lending_simulator.ts index 2a0da49ed1d..df9e9da0ec9 100644 --- a/yarn-project/end-to-end/src/simulators/lending_simulator.ts +++ b/yarn-project/end-to-end/src/simulators/lending_simulator.ts @@ -166,7 +166,10 @@ export class LendingSimulator { expect(this.borrowed).toEqual(this.stableCoin.totalSupply - this.mintedOutside); const asset = await this.lendingContract.methods.get_asset(0).view(); - expect(asset['interest_accumulator']).toEqual(this.accumulator); + + const interestAccumulator = asset['interest_accumulator']; + const interestAccumulatorBigint = BigInt(interestAccumulator.lo + interestAccumulator.hi * 2n ** 64n); + expect(interestAccumulatorBigint).toEqual(this.accumulator); expect(asset['last_updated_ts']).toEqual(BigInt(this.time)); for (const key of [this.account.address, this.account.key()]) { diff --git a/yarn-project/simulator/src/public/index.test.ts b/yarn-project/simulator/src/public/index.test.ts index 9070b0e1938..4128faa9d27 100644 --- a/yarn-project/simulator/src/public/index.test.ts +++ b/yarn-project/simulator/src/public/index.test.ts @@ -207,7 +207,7 @@ describe('ACIR public execution simulator', () => { mockStore(senderBalance, recipientBalance); await expect(executor.simulate(execution, GlobalVariables.empty())).rejects.toThrowError( - 'Assertion failed: Underflow', + 'Assertion failed: attempt to subtract with underflow', ); }); });