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

Testnet 2 #121

Merged
merged 11 commits into from
Jun 17, 2024
13 changes: 3 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 2 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["stylus-sdk", "stylus-proc", "mini-alloc"]
resolver = "2"

[workspace.package]
version = "0.4.3"
version = "0.5.0"
edition = "2021"
authors = ["Offchain Labs"]
license = "MIT OR Apache-2.0"
Expand All @@ -21,9 +21,6 @@ keccak-const = "0.2.0"
lazy_static = "1.4.0"
sha3 = "0.10.8"

# data structures
fnv = "1.0.7"

# proc macros
syn = { version = "1.0", features = ["full"] }
paste = "1.0.14"
Expand All @@ -35,4 +32,4 @@ convert_case = "0.6.0"

# members
stylus-sdk = { path = "stylus-sdk" }
stylus-proc = { path = "stylus-proc", version = "0.4.1" }
stylus-proc = { path = "stylus-proc", version = "0.5.0" }
3 changes: 1 addition & 2 deletions stylus-proc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ quote.workspace = true

[features]
export-abi = []
storage-cache = []
reentrant = []

[package.metadata.docs.rs]
features = ["export-abi", "storage-cache"]
features = ["export-abi"]

[lib]
proc-macro = true
4 changes: 2 additions & 2 deletions stylus-proc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ pub fn sol_storage(input: TokenStream) -> TokenStream {
/// # Reentrant calls
///
/// Contracts that opt into reentrancy via the `reentrant` feature flag require extra care.
/// When the `storage-cache` feature is enabled, cross-contract calls must [`flush`] or [`clear`]
/// the [`StorageCache`] to safeguard state. This happens automatically via the type system.
/// When enabled, cross-contract calls must [`flush`] or [`clear`] the [`StorageCache`] to safeguard state.
/// This happens automatically via the type system.
///
/// ```ignore
/// sol_interface! {
Expand Down
15 changes: 3 additions & 12 deletions stylus-proc/src/methods/entrypoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ pub fn entrypoint(attr: TokenStream, input: TokenStream) -> TokenStream {

if cfg!(feature = "export-abi") {
output.extend(quote! {
pub fn main() {
stylus_sdk::abi::export::print_abi::<#name>();
pub fn print_abi(license: &str, pragma: &str) {
stylus_sdk::abi::export::print_abi::<#name>(license, pragma);
}
});
}
Expand All @@ -72,15 +72,6 @@ pub fn entrypoint(attr: TokenStream, input: TokenStream) -> TokenStream {
}
}

// flush the cache before program exit
cfg_if! {
if #[cfg(feature = "storage-cache")] {
let flush_cache = quote! { stylus_sdk::storage::StorageCache::flush(); };
} else {
let flush_cache = quote! {};
}
}

output.extend(quote! {
#[no_mangle]
pub unsafe fn mark_used() {
Expand All @@ -97,7 +88,7 @@ pub fn entrypoint(attr: TokenStream, input: TokenStream) -> TokenStream {
Ok(data) => (data, 0),
Err(data) => (data, 1),
};
#flush_cache
unsafe { stylus_sdk::storage::StorageCache::flush() };
stylus_sdk::contract::output(&data);
status
}
Expand Down
27 changes: 22 additions & 5 deletions stylus-proc/src/methods/external.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ use crate::types::{self, Purity};
use convert_case::{Case, Casing};
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::quote;
use quote::{quote, quote_spanned};
use std::{mem, str::FromStr};
use syn::{
parenthesized,
parse::{Parse, ParseStream},
parse_macro_input,
punctuated::Punctuated,
spanned::Spanned,
FnArg, ImplItem, Index, ItemImpl, Lit, LitStr, Pat, PatType, Result, ReturnType, Token, Type,
};

Expand Down Expand Up @@ -128,35 +129,51 @@ pub fn external(_attr: TokenStream, input: TokenStream) -> TokenStream {
};

// get the solidity args
let expand_args = (0..args.len()).map(Index::from).map(|i| quote! { args.#i });
let mut expand_args = vec![];
for (index, (_, ty)) in args.iter().enumerate() {
let index = Index {
index: index as u32,
span: ty.span(),
};
expand_args.push(quote! { args.#index });
}

// calculate selector
let constant = Ident::new(&format!("SELECTOR_{name}"), name.span());
let arg_types: &Vec<_> = &args.iter().map(|a| &a.1).collect();

let selector = match override_id {
Some(id) => quote! { #id },
None => quote! { u32::from_be_bytes(function_selector!(#sol_name #(, #arg_types)*)) },
None => quote! { u32::from_be_bytes(function_selector!(#sol_name #(, #arg_types )*)) },
};
selectors.extend(quote! {
#[allow(non_upper_case_globals)]
const #constant: u32 = #selector;
});

let in_span = method.sig.inputs.span();
let decode_inputs = quote_spanned! { in_span => <(#( #arg_types, )*) as AbiType>::SolType };

let ret_span = match &method.sig.output {
x @ ReturnType::Default => x.span(),
ReturnType::Type(_, ty) => ty.span(), // right of arrow
};
let encode_result = quote_spanned! { ret_span => EncodableReturnType::encode(result) };

// match against the selector
match_selectors.extend(quote! {
#[allow(non_upper_case_globals)]
#constant => {
#deny_value
let args = match <<( #( #arg_types, )* ) as AbiType>::SolType as SolType>::decode(input, true) {
let args = match <#decode_inputs as SolType>::decode(input, true) {
Ok(args) => args,
Err(err) => {
internal::failed_to_decode_arguments(err);
return Some(Err(vec![]));
}
};
let result = Self::#name(#storage #(#expand_args, )* );
Some(EncodableReturnType::encode(result))
Some(#encode_result)
}
});

Expand Down
8 changes: 2 additions & 6 deletions stylus-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ lazy_static.workspace = true
# export-abi
regex = { workspace = true, optional = true }

# storage-cache
fnv = { workspace = true, optional = true }

# local deps
stylus-proc.workspace = true

Expand All @@ -36,10 +33,9 @@ sha3.workspace = true
features = ["default", "docs", "debug", "export-abi"]

[features]
default = ["storage-cache"]
export-abi = ["debug", "regex", "stylus-proc/export-abi"]
default = []
export-abi = ["debug", "regex", "stylus-proc/export-abi", "alloy-primitives/tiny-keccak"]
debug = []
docs = []
hostio = []
storage-cache = ["fnv", "stylus-proc/storage-cache"]
reentrant = ["stylus-proc/reentrant"]
5 changes: 4 additions & 1 deletion stylus-sdk/src/abi/export/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,15 @@ impl<T: GenerateAbi> fmt::Display for AbiPrinter<T> {
}

/// Prints the full contract ABI to standard out
pub fn print_abi<T: GenerateAbi>() {
pub fn print_abi<T: GenerateAbi>(license: &str, pragma: &str) {
println!("/**");
println!(" * This file was automatically generated by Stylus and represents a Rust program.");
println!(" * For more information, please see [The Stylus SDK](https://github.com/OffchainLabs/stylus-sdk-rs).");
println!(" */");
println!();
println!("// SPDX-License-Identifier: {license}");
println!("{pragma}");
println!();
print!("{}", AbiPrinter::<T>(PhantomData));
}

Expand Down
9 changes: 3 additions & 6 deletions stylus-sdk/src/call/context.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2022-2023, Offchain Labs, Inc.
// Copyright 2022-2024, Offchain Labs, Inc.
// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/stylus/licenses/COPYRIGHT.md

use crate::storage::TopLevelStorage;
Expand Down Expand Up @@ -52,9 +52,6 @@ where
/// }
/// ```
///
/// Projects that opt out of the [`StorageCache`] by disabling the `storage-cache` feature
/// may ignore this method.
///
/// [`StorageCache`]: crate::storage::StorageCache
/// [`flush`]: crate::storage::StorageCache::flush
/// [`clear`]: crate::storage::StorageCache::clear
Expand Down Expand Up @@ -133,7 +130,7 @@ where
impl<T> NonPayableCallContext for &mut T where T: TopLevelStorage {}

cfg_if! {
if #[cfg(all(feature = "storage-cache", feature = "reentrant"))] {
if #[cfg(feature = "reentrant")] {
// The following impls safeguard state during reentrancy scenarios

impl<S: TopLevelStorage> StaticCallContext for Call<&S, false> {}
Expand Down Expand Up @@ -165,7 +162,7 @@ cfg_if! {
}

cfg_if! {
if #[cfg(any(all(feature = "storage-cache", feature = "reentrant"), feature = "docs"))] {
if #[cfg(any(feature = "reentrant", feature = "docs"))] {
impl Default for Call<(), false> {
fn default() -> Self {
Self::new()
Expand Down
14 changes: 7 additions & 7 deletions stylus-sdk/src/call/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2022-2023, Offchain Labs, Inc.
// Copyright 2022-2024, Offchain Labs, Inc.
// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/stylus/licenses/COPYRIGHT.md

//! Call other contracts.
Expand All @@ -21,7 +21,7 @@ pub use self::{

pub(crate) use raw::CachePolicy;

#[cfg(all(feature = "storage-cache", feature = "reentrant"))]
#[cfg(feature = "reentrant")]
use crate::storage::Storage;

mod context;
Expand All @@ -32,12 +32,12 @@ mod transfer;

macro_rules! unsafe_reentrant {
($block:block) => {
#[cfg(all(feature = "storage-cache", feature = "reentrant"))]
#[cfg(feature = "reentrant")]
unsafe {
$block
}

#[cfg(not(all(feature = "storage-cache", feature = "reentrant")))]
#[cfg(not(feature = "reentrant"))]
$block
};
}
Expand All @@ -48,7 +48,7 @@ pub fn static_call(
to: Address,
data: &[u8],
) -> Result<Vec<u8>, Error> {
#[cfg(all(feature = "storage-cache", feature = "reentrant"))]
#[cfg(feature = "reentrant")]
Storage::flush(); // flush storage to persist changes, but don't invalidate the cache

unsafe_reentrant! {{
Expand All @@ -71,7 +71,7 @@ pub unsafe fn delegate_call(
to: Address,
data: &[u8],
) -> Result<Vec<u8>, Error> {
#[cfg(all(feature = "storage-cache", feature = "reentrant"))]
#[cfg(feature = "reentrant")]
Storage::clear(); // clear the storage to persist changes, invalidating the cache

RawCall::new_delegate()
Expand All @@ -82,7 +82,7 @@ pub unsafe fn delegate_call(

/// Calls the contract at the given address.
pub fn call(context: impl MutatingCallContext, to: Address, data: &[u8]) -> Result<Vec<u8>, Error> {
#[cfg(all(feature = "storage-cache", feature = "reentrant"))]
#[cfg(feature = "reentrant")]
Storage::clear(); // clear the storage to persist changes, invalidating the cache

unsafe_reentrant! {{
Expand Down
18 changes: 6 additions & 12 deletions stylus-sdk/src/call/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ use crate::{
use alloy_primitives::{Address, B256, U256};
use cfg_if::cfg_if;

#[cfg(all(feature = "storage-cache", feature = "reentrant"))]
#[cfg(feature = "reentrant")]
use crate::storage::StorageCache;

macro_rules! unsafe_reentrant {
($(#[$meta:meta])* pub fn $name:ident $($rest:tt)*) => {
cfg_if! {
if #[cfg(all(feature = "storage-cache", feature = "reentrant"))] {
if #[cfg(feature = "reentrant")] {
$(#[$meta])*
pub unsafe fn $name $($rest)*
} else {
Expand Down Expand Up @@ -161,20 +161,14 @@ impl RawCall {
}

/// Write all cached values to persistent storage before the call.
#[cfg(any(
all(feature = "storage-cache", feature = "reentrant"),
feature = "docs"
))]
#[cfg(any(feature = "reentrant", feature = "docs"))]
pub fn flush_storage_cache(mut self) -> Self {
self.cache_policy = self.cache_policy.max(CachePolicy::Flush);
self
}

/// Flush and clear the storage cache before the call.
#[cfg(any(
all(feature = "storage-cache", feature = "reentrant"),
feature = "docs"
))]
#[cfg(any(feature = "reentrant", feature = "docs"))]
pub fn clear_storage_cache(mut self) -> Self {
self.cache_policy = CachePolicy::Clear;
self
Expand All @@ -185,7 +179,7 @@ impl RawCall {
///
/// # Safety
///
/// This function becomes `unsafe` when the `reentrant` and `storage-cache` features are enabled.
/// This function becomes `unsafe` when the `reentrant` feature is enabled.
/// That's because raw calls might alias storage if used in the middle of a storage ref's lifetime.
///
/// For extra flexibility, this method does not clear the global storage cache by default.
Expand All @@ -198,7 +192,7 @@ impl RawCall {
let gas = self.gas.unwrap_or(u64::MAX); // will be clamped by 63/64 rule
let value = B256::from(self.callvalue);
let status = unsafe {
#[cfg(all(feature = "storage-cache", feature = "reentrant"))]
#[cfg(feature = "reentrant")]
match self.cache_policy {
CachePolicy::Clear => StorageCache::clear(),
CachePolicy::Flush => StorageCache::flush(),
Expand Down
Loading
Loading