Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add maybe_async_trait #334

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## 0.10.0 (2024-06-11) - `utils/maybe-async` crate only
- Added `maybe-async-trait` procedural macro.
- [BREAKING] Refactored `maybe-async` macro into simpler `maybe-async` and `maybe-await` macros.

## 0.9.1 (2024-06-24) - `utils/core` crate only
Expand Down
3 changes: 1 addition & 2 deletions air/src/air/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,7 @@ impl<B: StarkField> AirContext<B> {

// we use the identity: ceil(a/b) = (a + b - 1)/b
let num_constraint_col =
(highest_constraint_degree - transition_divisior_degree + trace_length - 1)
/ trace_length;
(highest_constraint_degree - transition_divisior_degree).div_ceil(trace_length);

cmp::max(num_constraint_col, 1)
}
Expand Down
2 changes: 2 additions & 0 deletions air/src/proof/ood_frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ impl Deserializable for OodFrame {
// OOD FRAME TRACE STATES
// ================================================================================================

/// Represents the state of a frame trace.
irakliyk marked this conversation as resolved.
Show resolved Hide resolved
///
/// Stores the trace evaluations at `z` and `gz`, where `z` is a random Field element in
/// `current_row` and `next_row`, respectively. If the Air contains a Lagrange kernel auxiliary
/// column, then that column interpolated polynomial will be evaluated at `z`, `gz`, `g^2 z`, ...
Expand Down
1 change: 0 additions & 1 deletion crypto/src/hash/mds/mds_f64_12x12.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ use math::{
/// Hamish Ivey-Law and Jacqueline Nabaglo of Polygon Zero and is based on Nabaglo's implementation
/// in [Plonky2](https://github.com/mir-protocol/plonky2).
/// The circulant matrix is identified by its first row: [7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8].

// MDS matrix in frequency domain.
// More precisely, this is the output of the three 4-point (real) FFTs of the first column of
// the MDS matrix i.e. just before the multiplication with the appropriate twiddle factors
Expand Down
1 change: 0 additions & 1 deletion crypto/src/hash/mds/mds_f64_8x8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ use math::{
/// Hamish Ivey-Law and Jacqueline Nabaglo of Polygon Zero is based on Nabaglo's implementation
/// in [Plonky2](https://github.com/mir-protocol/plonky2).
/// The circulant matrix is identified by its first row: [23, 8, 13, 10, 7, 6, 21, 8].

irakliyk marked this conversation as resolved.
Show resolved Hide resolved
// MDS matrix in frequency domain.
// More precisely, this is the output of the two 4-point (real) FFTs of the first column of
// the MDS matrix i.e. just before the multiplication with the appropriate twiddle factors
Expand Down
2 changes: 2 additions & 0 deletions crypto/src/merkle/concurrent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub const MIN_CONCURRENT_LEAVES: usize = 1024;
// PUBLIC FUNCTIONS
// ================================================================================================

/// Builds merkle nodes.
irakliyk marked this conversation as resolved.
Show resolved Hide resolved
///
/// Builds all internal nodes of the Merkle using all available threads and stores the
/// results in a single vector such that root of the tree is at position 1, nodes immediately
/// under the root is at positions 2 and 3 etc.
Expand Down
2 changes: 2 additions & 0 deletions examples/src/utils/rescue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub const RATE_WIDTH: usize = 4;
/// Two elements (32-bytes) are returned as digest.
const DIGEST_SIZE: usize = 2;

/// Number of rounds of the hash protocol.
irakliyk marked this conversation as resolved.
Show resolved Hide resolved
///
/// The number of rounds is set to 7 to provide 128-bit security level with 40% security margin;
/// computed using algorithm 7 from <https://eprint.iacr.org/2020/1143.pdf>
/// security margin here differs from Rescue Prime specification which suggests 50% security
Expand Down
1 change: 1 addition & 0 deletions math/src/field/f64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

//! An implementation of a 64-bit STARK-friendly prime field with modulus $2^{64} - 2^{32} + 1$
//! using Montgomery representation.
//!
//! Our implementation follows <https://eprint.iacr.org/2022/274.pdf> and is constant-time.
//!
//! This field supports very fast modular arithmetic and has a number of other attractive
Expand Down
52 changes: 52 additions & 0 deletions utils/maybe_async/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,58 @@ async fn world() -> String {
}
```

## maybe_async_trait

The `maybe_async_trait` macro can be applied to traits, and it will conditionally add the `async` keyword to trait methods annotated with `#[maybe_async]`, depending on the async feature being enabled. It also applies `#[async_trait::async_trait(?Send)]` to the trait or impl block when the async feature is on.

For example:

```rust
// Adding `maybe_async_trait` to a trait definition
#[maybe_async_trait]
trait ExampleTrait {
#[maybe_async]
fn hello_world(&self);

fn get_hello(&self) -> String;
}

// Adding `maybe_async_trait` to an implementation of the trait
#[maybe_async_trait]
impl ExampleTrait for MyStruct {
#[maybe_async]
fn hello_world(&self) {
// ...
}

fn get_hello(&self) -> String {
// ...
}
}
```

When `async` is set, it gets transformed into:

```rust
#[async_trait::async_trait(?Send)]
trait ExampleTrait {
async fn hello_world(&self);

fn get_hello(&self) -> String;
}

#[async_trait::async_trait(?Send)]
impl ExampleTrait for MyStruct {
async fn hello_world(&self) {
// ...
}

fn get_hello(&self) -> String {
// ...
}
}
```

## License

This project is [MIT licensed](../../LICENSE).
127 changes: 126 additions & 1 deletion utils/maybe_async/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Expr, ItemFn, TraitItemFn};
use syn::{parse_macro_input, Expr, ImplItem, ItemFn, ItemImpl, ItemTrait, TraitItem, TraitItemFn};

/// Parses a function (regular or trait) and conditionally adds the `async` keyword depending on
/// the `async` feature flag being enabled.
Expand Down Expand Up @@ -67,6 +67,131 @@ pub fn maybe_async(_attr: TokenStream, input: TokenStream) -> TokenStream {
}
}

/// Conditionally add `async` keyword to functions.
///
/// Parses a trait or an `impl` block and conditionally adds the `async` keyword to methods that
/// are annotated with `#[maybe_async]`, depending on the `async` feature flag being enabled.
/// Additionally, if applied to a trait definition or impl block, it will add
/// `#[async_trait::async_trait(?Send)]` to the it.
///
/// For example, given the following trait definition:
/// ```ignore
/// #[maybe_async_trait]
/// trait ExampleTrait {
/// #[maybe_async]
/// fn hello_world(&self);
///
/// fn get_hello(&self) -> String;
/// }
/// ```
///
/// And the following implementation:
/// ```ignore
/// #[maybe_async_trait]
/// impl ExampleTrait for MyStruct {
/// #[maybe_async]
/// fn hello_world(&self) {
/// // ...
/// }
///
/// fn get_hello(&self) -> String {
/// // ...
/// }
/// }
/// ```
///
/// When the `async` feature is enabled, this will be transformed into:
/// ```ignore
/// #[async_trait::async_trait(?Send)]
/// trait ExampleTrait {
/// async fn hello_world(&self);
///
/// fn get_hello(&self) -> String;
/// }
///
/// #[async_trait::async_trait(?Send)]
/// impl ExampleTrait for MyStruct {
/// async fn hello_world(&self) {
/// // ...
/// }
///
/// fn get_hello(&self) -> String {
/// // ...
/// }
/// }
/// ```
///
/// When the `async` feature is disabled, the code remains unchanged, and neither the `async`
/// keyword nor the `#[async_trait::async_trait(?Send)]` attribute is applied.
#[proc_macro_attribute]
pub fn maybe_async_trait(_attr: TokenStream, input: TokenStream) -> TokenStream {
// Try parsing the input as a trait definition
if let Ok(trait_item) = syn::parse::<ItemTrait>(input.clone()) {
let output = if cfg!(feature = "async") {
let mut async_trait = trait_item;
irakliyk marked this conversation as resolved.
Show resolved Hide resolved

for item in &mut async_trait.items {
if let TraitItem::Fn(method) = item {
// Remove the #[maybe_async] and make method async
method.attrs.retain(|attr| {
if attr.path().is_ident("maybe_async") {
method.sig.asyncness = Some(syn::token::Async::default());
false
} else {
true
}
});
}
}

quote! {
#[async_trait::async_trait(?Send)]
#async_trait
}
} else {
quote! {
#trait_item
}
};

return output.into();
}
// Check if it is an Impl block
else if let Ok(mut impl_item) = syn::parse::<ItemImpl>(input.clone()) {
let output = if cfg!(feature = "async") {
for item in &mut impl_item.items {
if let ImplItem::Fn(method) = item {
// Remove #[maybe_async] and make method async
method.attrs.retain(|attr| {
if attr.path().is_ident("maybe_async") {
method.sig.asyncness = Some(syn::token::Async::default());
false // Remove the attribute
} else {
true // Keep other attributes
}
});
}
}
quote! {
#[async_trait::async_trait(?Send)]
#impl_item
}
} else {
quote! {
#[cfg(not(feature = "async"))]
#impl_item
}
};

return output.into();
}

// If input is neither a trait nor an impl block, emit a compile-time error
quote! {
compile_error!("`maybe_async_trait` can only be applied to trait definitions and trait impl blocks");
}.into()
}

/// Parses an expression and conditionally adds the `.await` keyword at the end of it depending on
/// the `async` feature flag being enabled.
///
Expand Down