diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 838da72e8..090b068c1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -70,6 +70,18 @@ jobs: - run: cargo hack --feature-powerset --exclude-features docs build --target ${{ matrix.sys.target }} - run: cargo hack --feature-powerset --ignore-unknown-features --features testutils --exclude-features docs test --target ${{ matrix.sys.target }} + build-fuzz: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: stellar/actions/rust-cache@main + - run: rustup install nightly + - uses: stellar/binaries@v15 + with: + name: cargo-fuzz + version: 0.11.2 + - run: make build-fuzz + docs: runs-on: ubuntu-latest steps: diff --git a/Cargo.lock b/Cargo.lock index 369e11718..da1b190bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,6 +92,27 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "block-buffer" version = "0.9.0" @@ -356,7 +377,7 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek", "ed25519", - "rand", + "rand 0.7.3", "serde", "sha2 0.9.9", "zeroize", @@ -387,12 +408,42 @@ dependencies = [ "zeroize", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "ethnum" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0198b9d0078e0f30dedc7acbb21c974e838fc8fae3ee170128658a98cb2c1c04" +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "ff" version = "0.13.0" @@ -467,6 +518,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -531,12 +588,32 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "intx" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f38a50a899dc47a6d0ed5508e7f601a2e34c3a85303514b5d137f3c10a0c75" +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + [[package]] name = "itertools" version = "0.10.5" @@ -584,6 +661,12 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.146" @@ -596,6 +679,12 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + [[package]] name = "log" version = "0.4.19" @@ -656,6 +745,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -741,6 +831,42 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bit-set", + "bitflags", + "byteorder", + "lazy_static", + "num-traits", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "proptest-arbitrary-interop" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1981e49bd2432249da8b0e11e5557099a8e74690d6b94e721f7dc0bb7f3555f" +dependencies = [ + "arbitrary", + "proptest", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "1.0.28" @@ -758,11 +884,22 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ "getrandom 0.1.16", "libc", - "rand_chacha", + "rand_chacha 0.2.2", "rand_core 0.5.1", "rand_hc", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -773,6 +910,16 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -800,6 +947,30 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "rfc6979" version = "0.4.0" @@ -816,6 +987,32 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustix" +version = "0.37.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + [[package]] name = "ryu" version = "1.0.13" @@ -990,8 +1187,8 @@ dependencies = [ "num-derive", "num-integer", "num-traits", - "rand", - "rand_chacha", + "rand 0.7.3", + "rand_chacha 0.2.2", "sha2 0.9.9", "sha3", "soroban-env-common", @@ -1043,10 +1240,13 @@ dependencies = [ name = "soroban-sdk" version = "0.8.4" dependencies = [ + "arbitrary", "bytes-lit", "ed25519-dalek", "hex", - "rand", + "proptest", + "proptest-arbitrary-interop", + "rand 0.7.3", "soroban-env-guest", "soroban-env-host", "soroban-ledger-snapshot", @@ -1207,6 +1407,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +dependencies = [ + "autocfg", + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys", +] + [[package]] name = "test_add_i128" version = "0.8.4" @@ -1277,6 +1491,13 @@ dependencies = [ "soroban-sdk", ] +[[package]] +name = "test_fuzz" +version = "0.8.4" +dependencies = [ + "soroban-sdk", +] + [[package]] name = "test_import_contract" version = "0.8.4" @@ -1358,6 +1579,12 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-ident" version = "1.0.9" @@ -1370,6 +1597,15 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -1490,6 +1726,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 292502c08..42c0fd307 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ members = [ "tests/errors", "tests/alloc", "tests/auth", + "tests/fuzz", ] [workspace.package] diff --git a/Makefile b/Makefile index e2d344b03..f9621ef5f 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,9 @@ check: build fmt cargo hack --feature-powerset --exclude-features docs check cargo hack check --release --target wasm32-unknown-unknown +build-fuzz: + cd tests/fuzz/fuzz && cargo +nightly fuzz check + readme: cd soroban-sdk \ && cargo +nightly rustdoc -- -Zunstable-options -wjson \ diff --git a/deny.toml b/deny.toml index 6ed140d8e..b095aa443 100644 --- a/deny.toml +++ b/deny.toml @@ -249,6 +249,14 @@ deny = [ # Certain crates/versions that will be skipped when doing duplicate detection. skip = [ #{ name = "ansi_term", version = "=0.11.0" }, + + # ed25519-dalek and proptest have conflicting rand deps, + # where ed25519-dalek is on an older revision. + # proptest is only used as a dev-dependency, + # so this conflict should not cause extra bloat to contracts. + # Below are the newer version numbers required by proptest. + { name = "rand", version = "0.8" }, + { name = "rand_chacha", version = "0.3" }, ] # Similarly to `skip` allows you to skip certain crates during duplicate # detection. Unlike skip, it also includes the entire tree of transitive diff --git a/soroban-sdk-macros/Cargo.toml b/soroban-sdk-macros/Cargo.toml index 5143fcef4..25348ea2b 100644 --- a/soroban-sdk-macros/Cargo.toml +++ b/soroban-sdk-macros/Cargo.toml @@ -25,3 +25,6 @@ proc-macro2 = "1.0" itertools = "0.10.3" darling = "0.20.0" sha2 = "0.9.9" + +[features] +testutils = [] diff --git a/soroban-sdk-macros/src/arbitrary.rs b/soroban-sdk-macros/src/arbitrary.rs new file mode 100644 index 000000000..b9c5322f9 --- /dev/null +++ b/soroban-sdk-macros/src/arbitrary.rs @@ -0,0 +1,344 @@ +use proc_macro2::TokenStream as TokenStream2; +use quote::{format_ident, quote}; +use syn::{DataEnum, DataStruct, Ident, Path, Visibility}; + +pub fn derive_arbitrary_struct( + path: &Path, + vis: &Visibility, + ident: &Ident, + data: &DataStruct, +) -> TokenStream2 { + derive_arbitrary_struct_common(path, vis, ident, data, FieldType::Named) +} + +pub fn derive_arbitrary_struct_tuple( + path: &Path, + vis: &Visibility, + ident: &Ident, + data: &DataStruct, +) -> TokenStream2 { + derive_arbitrary_struct_common(path, vis, ident, data, FieldType::Unnamed) +} + +enum FieldType { + Named, + Unnamed, +} + +fn derive_arbitrary_struct_common( + path: &Path, + vis: &Visibility, + ident: &Ident, + data: &DataStruct, + field_type: FieldType, +) -> TokenStream2 { + let arbitrary_type_ident = format_ident!("Arbitrary{}", ident); + + let arbitrary_type_fields: Vec = data + .fields + .iter() + .map(|field| { + let field_type = &field.ty; + match &field.ident { + Some(ident) => { + quote! { + #ident: <#field_type as #path::arbitrary::SorobanArbitrary>::Prototype + } + } + None => { + quote! { + <#field_type as #path::arbitrary::SorobanArbitrary>::Prototype + } + } + } + }) + .collect(); + + let field_conversions: Vec = data + .fields + .iter() + .enumerate() + .map(|(i, field)| match &field.ident { + Some(ident) => { + quote! { + #ident: #path::IntoVal::into_val(&v.#ident, env) + } + } + None => { + let i = syn::Index::from(i); + quote! { + #path::IntoVal::into_val(&v.#i, env) + } + } + }) + .collect(); + + let arbitrary_type_decl = match field_type { + FieldType::Named => quote! { + struct #arbitrary_type_ident { + #(#arbitrary_type_fields,)* + } + }, + FieldType::Unnamed => quote! { + struct #arbitrary_type_ident ( + #(#arbitrary_type_fields,)* + ); + }, + }; + + let arbitrary_ctor = match field_type { + FieldType::Named => quote! { + #ident { + #(#field_conversions,)* + } + }, + FieldType::Unnamed => quote! { + #ident ( + #(#field_conversions,)* + ) + }, + }; + + quote_arbitrary( + path, + vis, + ident, + arbitrary_type_ident, + arbitrary_type_decl, + arbitrary_ctor, + ) +} + +pub fn derive_arbitrary_enum( + path: &Path, + vis: &Visibility, + ident: &Ident, + data: &DataEnum, +) -> TokenStream2 { + let arbitrary_type_ident = format_ident!("Arbitrary{}", ident); + + let arbitrary_type_variants: Vec = data + .variants + .iter() + .map(|variant| { + let mut field_types = None; + let variant_ident = &variant.ident; + let fields: Vec = variant + .fields + .iter() + .map(|field| { + let field_type = &field.ty; + match &field.ident { + Some(ident) => { + field_types = Some(FieldType::Named); + quote! { + #ident: <#field_type as #path::arbitrary::SorobanArbitrary>::Prototype + } + } + None => { + field_types = Some(FieldType::Unnamed); + quote! { + <#field_type as #path::arbitrary::SorobanArbitrary>::Prototype + } + } + } + }) + .collect(); + match field_types { + None => { + quote! { + #variant_ident + } + }, + Some(FieldType::Named) => { + quote! { + #variant_ident { #(#fields,)* } + } + } + Some(FieldType::Unnamed) => { + quote! { + #variant_ident ( #(#fields,)* ) + } + } + } + }) + .collect(); + + let variant_conversions: Vec = data + .variants + .iter() + .map(|variant| { + let mut field_types = None; + let variant_ident = &variant.ident; + let fields: Vec = variant + .fields + .iter() + .enumerate() + .map(|(i, field)| { + match &field.ident { + Some(ident) => { + quote! { + #ident + } + } + None => { + let ident = format_ident!("field_{}", i); + quote! { + #ident + } + } + } + }) + .collect(); + let field_conversions: Vec = variant + .fields + .iter() + .enumerate() + .map(|(i, field)| { + match &field.ident { + Some(ident) => { + field_types = Some(FieldType::Named); + quote! { + #ident: #path::IntoVal::into_val(#ident, env) + } + } + None => { + field_types = Some(FieldType::Unnamed); + let ident = format_ident!("field_{}", i); + quote! { + #path::IntoVal::into_val(#ident, env) + } + } + } + }) + .collect(); + match field_types { + None => { + quote! { + #arbitrary_type_ident::#variant_ident => #ident::#variant_ident + } + }, + Some(FieldType::Named) => { + quote! { + #arbitrary_type_ident::#variant_ident { #(#fields,)* } => #ident::#variant_ident { #(#field_conversions,)* } + } + } + Some(FieldType::Unnamed) => { + quote! { + #arbitrary_type_ident::#variant_ident ( #(#fields,)* ) => #ident::#variant_ident ( #(#field_conversions,)* ) + } + } + } + }) + .collect(); + + let arbitrary_type_decl = quote! { + enum #arbitrary_type_ident { + #(#arbitrary_type_variants,)* + } + }; + let arbitrary_ctor = quote! { + match v { + #(#variant_conversions,)* + } + }; + + quote_arbitrary( + path, + vis, + ident, + arbitrary_type_ident, + arbitrary_type_decl, + arbitrary_ctor, + ) +} + +pub fn derive_arbitrary_enum_int( + path: &Path, + vis: &Visibility, + ident: &Ident, + data: &DataEnum, +) -> TokenStream2 { + let arbitrary_type_ident = format_ident!("Arbitrary{}", ident); + + let arbitrary_type_variants: Vec = data + .variants + .iter() + .map(|variant| { + let variant_ident = &variant.ident; + quote! { + #variant_ident + } + }) + .collect(); + + let variant_conversions: Vec = data + .variants + .iter() + .map(|variant| { + let variant_ident = &variant.ident; + quote! { + #arbitrary_type_ident::#variant_ident => #ident::#variant_ident + } + }) + .collect(); + + let arbitrary_type_decl = quote! { + enum #arbitrary_type_ident { + #(#arbitrary_type_variants,)* + } + }; + let arbitrary_ctor = quote! { + match v { + #(#variant_conversions,)* + } + }; + + quote_arbitrary( + path, + vis, + ident, + arbitrary_type_ident, + arbitrary_type_decl, + arbitrary_ctor, + ) +} + +fn quote_arbitrary( + path: &Path, + vis: &Visibility, + ident: &Ident, + arbitrary_type_ident: Ident, + arbitrary_type_decl: TokenStream2, + arbitrary_ctor: TokenStream2, +) -> TokenStream2 { + if !cfg!(any(test, feature = "testutils")) { + return quote!(); + } + quote! { + // This allows us to create a scope to import std and arbitrary, while + // also keeping everything from the current scope. This is better than a + // module because: modules inside functions have surprisingly + // inconsistent scoping rules and visibility management is harder. + #[cfg(any(test, feature = "testutils"))] + const _: () = { + // derive(Arbitrary) expects these two to be in scope + use #path::arbitrary::std; + use #path::arbitrary::arbitrary; + + #[derive(#path::arbitrary::arbitrary::Arbitrary, Debug)] + #vis #arbitrary_type_decl + + impl #path::arbitrary::SorobanArbitrary for #ident { + type Prototype = #arbitrary_type_ident; + } + + impl #path::TryFromVal<#path::Env, #arbitrary_type_ident> for #ident { + type Error = #path::ConversionError; + fn try_from_val(env: &#path::Env, v: &#arbitrary_type_ident) -> std::result::Result { + Ok(#arbitrary_ctor) + } + } + }; + } +} diff --git a/soroban-sdk-macros/src/derive_enum.rs b/soroban-sdk-macros/src/derive_enum.rs index 58f066477..b36f2bc41 100644 --- a/soroban-sdk-macros/src/derive_enum.rs +++ b/soroban-sdk-macros/src/derive_enum.rs @@ -1,7 +1,7 @@ use itertools::MultiUnzip; use proc_macro2::{Literal, TokenStream as TokenStream2}; use quote::{format_ident, quote}; -use syn::{spanned::Spanned, Attribute, DataEnum, Error, Fields, Ident, Path}; +use syn::{spanned::Spanned, Attribute, DataEnum, Error, Fields, Ident, Path, Visibility}; use stellar_xdr::{ Error as XdrError, ScSpecEntry, ScSpecTypeDef, ScSpecUdtUnionCaseTupleV0, ScSpecUdtUnionCaseV0, @@ -12,6 +12,7 @@ use crate::{doc::docs_from_attrs, map_type::map_type}; pub fn derive_type_enum( path: &Path, + vis: &Visibility, enum_ident: &Ident, attrs: &[Attribute], data: &DataEnum, @@ -160,6 +161,8 @@ pub fn derive_type_enum( None }; + let arbitrary_tokens = crate::arbitrary::derive_arbitrary_enum(path, vis, enum_ident, data); + // Output. quote! { #spec_gen @@ -262,6 +265,8 @@ pub fn derive_type_enum( (&val).try_into() } } + + #arbitrary_tokens } } diff --git a/soroban-sdk-macros/src/derive_enum_int.rs b/soroban-sdk-macros/src/derive_enum_int.rs index ca68fe688..ef495fd35 100644 --- a/soroban-sdk-macros/src/derive_enum_int.rs +++ b/soroban-sdk-macros/src/derive_enum_int.rs @@ -2,7 +2,7 @@ use itertools::MultiUnzip; use proc_macro2::TokenStream as TokenStream2; use quote::{format_ident, quote}; use stellar_xdr::{ScSpecUdtEnumV0, StringM}; -use syn::{spanned::Spanned, Attribute, DataEnum, Error, ExprLit, Ident, Lit, Path}; +use syn::{spanned::Spanned, Attribute, DataEnum, Error, ExprLit, Ident, Lit, Path, Visibility}; use stellar_xdr::{ScSpecEntry, ScSpecUdtEnumCaseV0, WriteXdr}; @@ -12,6 +12,7 @@ use crate::doc::docs_from_attrs; pub fn derive_type_enum_int( path: &Path, + vis: &Visibility, enum_ident: &Ident, attrs: &[Attribute], data: &DataEnum, @@ -89,6 +90,8 @@ pub fn derive_type_enum_int( None }; + let arbitrary_tokens = crate::arbitrary::derive_arbitrary_enum_int(path, vis, enum_ident, data); + // Output. quote! { #spec_gen @@ -149,5 +152,7 @@ pub fn derive_type_enum_int( Ok((self as u32).into()) } } + + #arbitrary_tokens } } diff --git a/soroban-sdk-macros/src/derive_struct.rs b/soroban-sdk-macros/src/derive_struct.rs index 3bb430b4c..1da14dfde 100644 --- a/soroban-sdk-macros/src/derive_struct.rs +++ b/soroban-sdk-macros/src/derive_struct.rs @@ -1,7 +1,7 @@ use itertools::Itertools; use proc_macro2::{Literal, TokenStream as TokenStream2}; use quote::{format_ident, quote}; -use syn::{Attribute, DataStruct, Error, Ident, Path}; +use syn::{Attribute, DataStruct, Error, Ident, Path, Visibility}; use stellar_xdr::{ ScSpecEntry, ScSpecTypeDef, ScSpecUdtStructFieldV0, ScSpecUdtStructV0, StringM, WriteXdr, @@ -16,6 +16,7 @@ use crate::{doc::docs_from_attrs, map_type::map_type}; pub fn derive_type_struct( path: &Path, + vis: &Visibility, ident: &Ident, attrs: &[Attribute], data: &DataStruct, @@ -100,6 +101,8 @@ pub fn derive_type_struct( None }; + let arbitrary_tokens = crate::arbitrary::derive_arbitrary_struct(path, vis, ident, data); + // Output. quote! { #spec_gen @@ -200,5 +203,7 @@ pub fn derive_type_struct( (&val).try_into() } } + + #arbitrary_tokens } } diff --git a/soroban-sdk-macros/src/derive_struct_tuple.rs b/soroban-sdk-macros/src/derive_struct_tuple.rs index 52db345f1..9586fdc8d 100644 --- a/soroban-sdk-macros/src/derive_struct_tuple.rs +++ b/soroban-sdk-macros/src/derive_struct_tuple.rs @@ -1,7 +1,7 @@ use itertools::MultiUnzip; use proc_macro2::{Literal, TokenStream as TokenStream2}; use quote::{format_ident, quote}; -use syn::{Attribute, DataStruct, Error, Ident, Path}; +use syn::{Attribute, DataStruct, Error, Ident, Path, Visibility}; use stellar_xdr::{ ScSpecEntry, ScSpecTypeDef, ScSpecUdtStructFieldV0, ScSpecUdtStructV0, StringM, WriteXdr, @@ -11,6 +11,7 @@ use crate::{doc::docs_from_attrs, map_type::map_type}; pub fn derive_type_struct_tuple( path: &Path, + vis: &Visibility, ident: &Ident, attrs: &[Attribute], data: &DataStruct, @@ -87,6 +88,8 @@ pub fn derive_type_struct_tuple( None }; + let arbitrary_tokens = crate::arbitrary::derive_arbitrary_struct_tuple(path, vis, ident, data); + // Output. quote! { #spec_gen @@ -186,5 +189,7 @@ pub fn derive_type_struct_tuple( (&val).try_into() } } + + #arbitrary_tokens } } diff --git a/soroban-sdk-macros/src/lib.rs b/soroban-sdk-macros/src/lib.rs index e54aefc77..8b591c7d2 100644 --- a/soroban-sdk-macros/src/lib.rs +++ b/soroban-sdk-macros/src/lib.rs @@ -1,5 +1,6 @@ extern crate proc_macro; +mod arbitrary; mod derive_client; mod derive_enum; mod derive_enum_int; @@ -255,6 +256,7 @@ pub fn contracttype(metadata: TokenStream, input: TokenStream) -> TokenStream { Err(e) => return e.write_errors().into(), }; let input = parse_macro_input!(input as DeriveInput); + let vis = &input.vis; let ident = &input.ident; let attrs = &input.attrs; // If the export argument has a value, do as it instructs regarding @@ -267,11 +269,17 @@ pub fn contracttype(metadata: TokenStream, input: TokenStream) -> TokenStream { let derived = match &input.data { Data::Struct(s) => match s.fields { Fields::Named(_) => { - derive_type_struct(&args.crate_path, ident, attrs, s, gen_spec, &args.lib) - } - Fields::Unnamed(_) => { - derive_type_struct_tuple(&args.crate_path, ident, attrs, s, gen_spec, &args.lib) + derive_type_struct(&args.crate_path, vis, ident, attrs, s, gen_spec, &args.lib) } + Fields::Unnamed(_) => derive_type_struct_tuple( + &args.crate_path, + vis, + ident, + attrs, + s, + gen_spec, + &args.lib, + ), Fields::Unit => Error::new( s.fields.span(), "unit structs are not supported as contract types", @@ -286,9 +294,9 @@ pub fn contracttype(metadata: TokenStream, input: TokenStream) -> TokenStream { .filter(|v| v.discriminant.is_some()) .count(); if count_of_int_variants == 0 { - derive_type_enum(&args.crate_path, ident, attrs, e, gen_spec, &args.lib) + derive_type_enum(&args.crate_path, vis, ident, attrs, e, gen_spec, &args.lib) } else if count_of_int_variants == count_of_variants { - derive_type_enum_int(&args.crate_path, ident, attrs, e, gen_spec, &args.lib) + derive_type_enum_int(&args.crate_path, vis, ident, attrs, e, gen_spec, &args.lib) } else { Error::new(input.span(), "enums are supported as contract types only when all variants have an explicit integer literal, or when all variants are unit or single field") .to_compile_error() diff --git a/soroban-sdk/Cargo.toml b/soroban-sdk/Cargo.toml index cf782aa62..454dac12a 100644 --- a/soroban-sdk/Cargo.toml +++ b/soroban-sdk/Cargo.toml @@ -24,21 +24,26 @@ soroban-env-guest = { workspace = true } soroban-env-host = { workspace = true, features = [] } soroban-ledger-snapshot = { workspace = true } stellar-strkey = { workspace = true } +arbitrary = { version = "1.1.3", features = ["derive"], optional = true } ed25519-dalek = { version = "1.0.1", optional = true } # match the version of rand used in dalek rand = "0.7.3" [dev-dependencies] +soroban-sdk-macros = { workspace = true, features = ["testutils"] } soroban-env-host = { workspace = true, features = ["testutils"] } stellar-xdr = { workspace = true, features = ["next", "std"] } soroban-spec = { workspace = true } ed25519-dalek = "1.0.1" rand = "0.7.3" hex = "0.4.3" +arbitrary = { version = "1.1.3", features = ["derive"] } +proptest = "1.2.0" +proptest-arbitrary-interop = "0.1.0" [features] alloc = [] -testutils = ["soroban-env-host/testutils", "dep:ed25519-dalek"] +testutils = ["soroban-sdk-macros/testutils", "soroban-env-host/testutils", "dep:ed25519-dalek", "dep:arbitrary"] docs = [] [package.metadata.docs.rs] diff --git a/soroban-sdk/src/arbitrary.rs b/soroban-sdk/src/arbitrary.rs new file mode 100644 index 000000000..efdf4f943 --- /dev/null +++ b/soroban-sdk/src/arbitrary.rs @@ -0,0 +1,1233 @@ +//! Support for fuzzing Soroban contracts with [`cargo-fuzz`]. +//! +//! This module provides a pattern for generating Soroban contract types for the +//! purpose of fuzzing Soroban contracts. It is focused on implementing the +//! [`Arbitrary`] trait that `cargo-fuzz` relies on to feed fuzzers with +//! generated Rust values. +//! +//! [`cargo-fuzz`]: https://github.com/rust-fuzz/cargo-fuzz/ +//! [`Arbitrary`]: ::arbitrary::Arbitrary +//! +//! This module +//! +//! - defines the [`SorobanArbitrary`] trait, +//! - defines the [`fuzz_catch_panic`] helper, +//! - reexports the [`arbitrary`] crate and the [`Arbitrary`] type. +//! +//! This module is only available when the "testutils" Cargo feature is defined. +//! +//! +//! ## About `cargo-fuzz` and `Arbitrary` +//! +//! In its basic operation `cargo-fuzz` fuzz generates raw bytes and feeds them +//! to a program-dependent fuzzer designed to exercise a program, in our case a +//! Soroban contract. +//! +//! `cargo-fuzz` programs declare their entry points with a macro: +//! +//! ``` +//! # macro_rules! fuzz_target { +//! # (|$data:ident: $dty: ty| $body:block) => { }; +//! # } +//! fuzz_target!(|input: &[u8]| { +//! // feed bytes to the program +//! }); +//! ``` +//! +//! More sophisticated fuzzers accept not bytes but Rust types: +//! +//! ``` +//! # use arbitrary::Arbitrary; +//! # macro_rules! fuzz_target { +//! # (|$data:ident: $dty: ty| $body:block) => { }; +//! # } +//! #[derive(Arbitrary, Debug)] +//! struct FuzzDeposit { +//! deposit_amount: i128, +//! } +//! +//! fuzz_target!(|input: FuzzDeposit| { +//! // fuzz the program based on the input +//! }); +//! ``` +//! +//! Types accepted as input to `fuzz_target` must implement the `Arbitrary` trait, +//! which transforms bytes to Rust types. +//! +//! +//! ## The `SorobanArbitrary` trait +//! +//! Soroban types are managed by the host environment, and so must be created +//! from an [`Env`] value; `Arbitrary` values though must be created from +//! nothing but bytes. The [`SorobanArbitrary`] trait, implemented for all +//! Soroban contract types, exists to bridge this gap: it defines a _prototype_ +//! pattern whereby the `fuzz_target` macro creates prototype values that the +//! fuzz program can convert to contract values with the standard soroban +//! conversion traits, [`FromVal`] or [`IntoVal`]. +//! +//! [`Env`]: crate::Env +//! [`FromVal`]: crate::FromVal +//! [`IntoVal`]: crate::IntoVal +//! +//! The types of prototypes are identified by the associated type, +//! [`SorobanArbitrary::Prototype`]: +//! +//! ``` +//! # use soroban_sdk::arbitrary::Arbitrary; +//! # use soroban_sdk::{TryFromVal, IntoVal, Val, Env}; +//! pub trait SorobanArbitrary: +//! TryFromVal + IntoVal + TryFromVal +//! { +//! type Prototype: for <'a> Arbitrary<'a>; +//! } +//! ``` +//! +//! Types that implement `SorobanArbitrary` include: +//! +//! - `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `I256`, `U256`, `()`, and `bool`, +//! - [`Error`], +//! - [`Bytes`], [`BytesN`], [`Vec`], [`Map`], +//! - [`Address`], [`Symbol`], +//! - [`Val`], +//! +//! [`I256`]: crate::I256 +//! [`U256`]: crate::U256 +//! [`Error`]: crate::Error +//! [`Bytes`]: crate::Bytes +//! [`BytesN`]: crate::BytesN +//! [`Vec`]: crate::Vec +//! [`Map`]: crate::Map +//! [`Address`]: crate::Address +//! [`Symbol`]: crate::Symbol +//! [`Val`]: crate::Val +//! +//! All user-defined contract types, those with the [`contracttype`] attribute, +//! automatically derive `SorobanArbitrary`. Note that `SorobanArbitrary` is +//! only derived when the "testutils" Cargo feature is active. This implies +//! that, in general, to make a Soroban contract fuzzable, the contract crate +//! must define a "testutils" Cargo feature, that feature should turn on the +//! "soroban-sdk/testutils" feature, and the fuzz test, which is its own crate, +//! must turn that feature on. +//! +//! [`contracttype`]: crate::contracttype +//! +//! +//! ## Example: take a Soroban `Vec` of `Address` as fuzzer input +//! +//! ``` +//! # macro_rules! fuzz_target { +//! # (|$data:ident: $dty: ty| $body:block) => { }; +//! # } +//! use soroban_sdk::{Address, Env, Vec}; +//! use soroban_sdk::arbitrary::SorobanArbitrary; +//! +//! fuzz_target!(|input: as SorobanArbitrary>::Prototype| { +//! let env = Env::default(); +//! let addresses: Vec
= input.into_val(&env); +//! // fuzz the program based on the input +//! }); +//! ``` +//! +//! +//! ## Example: take a custom contract type as fuzzer input +//! +//! ``` +//! # macro_rules! fuzz_target { +//! # (|$data:ident: $dty: ty| $body:block) => { }; +//! # } +//! use soroban_sdk::{Address, Env, Vec}; +//! use soroban_sdk::contracttype; +//! use soroban_sdk::arbitrary::{Arbitrary, SorobanArbitrary}; +//! use std::vec::Vec as RustVec; +//! +//! #[derive(Arbitrary, Debug)] +//! struct TestInput { +//! deposit_amount: i128, +//! claim_address:
::Prototype, +//! time_bound: ::Prototype, +//! } +//! +//! #[contracttype] +//! pub struct TimeBound { +//! pub kind: TimeBoundKind, +//! pub timestamp: u64, +//! } +//! +//! #[contracttype] +//! pub enum TimeBoundKind { +//! Before, +//! After, +//! } +//! +//! fuzz_target!(|input: TestInput| { +//! let env = Env::default(); +//! let claim_address: Address = input.claim_address.into_val(&env); +//! let time_bound: TimeBound = input.time_bound.into_val(&env); +//! // fuzz the program based on the input +//! }); +//! ``` + +#![cfg(any(test, feature = "testutils"))] +#![cfg_attr(feature = "docs", doc(cfg(feature = "testutils")))] + +/// A reexport of the `arbitrary` crate. +/// +/// Used by the `contracttype` macro to derive `Arbitrary`. +pub use arbitrary; + +// Used often enough in fuzz tests to want direct access to it. +pub use arbitrary::Arbitrary; + +// Used by `contracttype` +#[doc(hidden)] +pub use std; + +pub use api::*; +pub use fuzz_test_helpers::*; + +/// The traits that must be implemented on Soroban types to support fuzzing. +/// +/// These allow for ergonomic conversion from a randomly-generated "prototype" +/// that implements `Arbitrary` into `Env`-"hosted" values that are paired with an +/// `Env`. +/// +/// These traits are intended to be easy to automatically derive. +mod api { + use crate::Env; + use crate::Val; + use crate::{IntoVal, TryFromVal}; + use arbitrary::Arbitrary; + + /// An `Env`-hosted contract value that can be randomly generated. + /// + /// Types that implement `SorabanArbitrary` have an associated "prototype" + /// type that implements [`Arbitrary`]. + /// + /// This exists partly that the prototype can be named like + /// + /// ```ignore + /// fuzz_target!(|input: ::Arbitrary| { + /// ... + /// }); + /// ``` + // This also makes derivation of `SorobanArbitrary` for custom types easier + // since we depend on all fields also implementing `SorobanArbitrary`. + // + // The IntoVal + TryFromVal bounds are to satisfy + // the bounds of Vec and Map, so that collections of prototypes can be + // converted to contract types. + pub trait SorobanArbitrary: + TryFromVal + IntoVal + TryFromVal + { + /// A type that implements [`Arbitrary`] and can be converted to this + /// [`SorobanArbitrary`] type. + // NB: The `Arbitrary` bound here is not necessary for the correct use of + // `SorobanArbitrary`, but it makes the purpose clear. + type Prototype: for<'a> Arbitrary<'a>; + } +} + +/// Implementations of `soroban_sdk::arbitrary::api` for Rust scalar types. +/// +/// These types +/// +/// - do not have a distinct `Arbitrary` prototype, +/// i.e. they use themselves as the `SorobanArbitrary::Prototype` type, +/// - implement `Arbitrary` in the `arbitrary` crate, +/// - trivially implement `TryFromVal`, +/// +/// Examples: +/// +/// - `u32` +mod scalars { + use crate::arbitrary::api::*; + + impl SorobanArbitrary for () { + type Prototype = (); + } + + impl SorobanArbitrary for bool { + type Prototype = bool; + } + + impl SorobanArbitrary for u32 { + type Prototype = u32; + } + + impl SorobanArbitrary for i32 { + type Prototype = i32; + } + + impl SorobanArbitrary for u64 { + type Prototype = u64; + } + + impl SorobanArbitrary for i64 { + type Prototype = i64; + } + + impl SorobanArbitrary for u128 { + type Prototype = u128; + } + + impl SorobanArbitrary for i128 { + type Prototype = i128; + } +} + +/// Implementations of `soroban_sdk::arbitrary::api` for Soroban types that do not +/// need access to the Soroban host environment. +/// +/// These types +/// +/// - do not have a distinct `Arbitrary` prototype, +/// i.e. they use themselves as the `SorobanArbitrary::Prototype` type, +/// - implement `Arbitrary` in the `soroban-env-common` crate, +/// - trivially implement `TryFromVal`, +/// +/// Examples: +/// +/// - `Error` +mod simple { + use crate::arbitrary::api::*; + pub use crate::Error; + + impl SorobanArbitrary for Error { + type Prototype = Error; + } +} + +/// Implementations of `soroban_sdk::arbitrary::api` for Soroban types that do +/// need access to the Soroban host environment. +/// +/// These types +/// +/// - have a distinct `Arbitrary` prototype that derives `Arbitrary`, +/// - require an `Env` to be converted to their actual contract type. +/// +/// Examples: +/// +/// - `Vec` +mod objects { + use arbitrary::{Arbitrary, Result as ArbitraryResult, Unstructured}; + + use crate::arbitrary::api::*; + use crate::ConversionError; + use crate::{Env, IntoVal, TryFromVal}; + + use crate::xdr::{Int256Parts, ScVal, UInt256Parts}; + use crate::{Address, Bytes, BytesN, Map, String, Symbol, Val, Vec, I256, U256}; + use soroban_env_host::TryIntoVal; + + use std::string::String as RustString; + use std::vec::Vec as RustVec; + + ////////////////////////////////// + + #[derive(Arbitrary, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] + pub struct ArbitraryU256 { + parts: (u64, u64, u64, u64), + } + + impl SorobanArbitrary for U256 { + type Prototype = ArbitraryU256; + } + + impl TryFromVal for U256 { + type Error = ConversionError; + fn try_from_val(env: &Env, v: &ArbitraryU256) -> Result { + let v = ScVal::U256(UInt256Parts { + hi_hi: v.parts.0, + hi_lo: v.parts.1, + lo_hi: v.parts.2, + lo_lo: v.parts.3, + }); + let v = Val::try_from_val(env, &v)?; + v.try_into_val(env) + } + } + + ////////////////////////////////// + + #[derive(Arbitrary, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] + pub struct ArbitraryI256 { + parts: (i64, u64, u64, u64), + } + + impl SorobanArbitrary for I256 { + type Prototype = ArbitraryI256; + } + + impl TryFromVal for I256 { + type Error = ConversionError; + fn try_from_val(env: &Env, v: &ArbitraryI256) -> Result { + let v = ScVal::I256(Int256Parts { + hi_hi: v.parts.0, + hi_lo: v.parts.1, + lo_hi: v.parts.2, + lo_lo: v.parts.3, + }); + let v = Val::try_from_val(env, &v)?; + v.try_into_val(env) + } + } + + ////////////////////////////////// + + #[derive(Arbitrary, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] + pub struct ArbitraryBytes { + vec: RustVec, + } + + impl SorobanArbitrary for Bytes { + type Prototype = ArbitraryBytes; + } + + impl TryFromVal for Bytes { + type Error = ConversionError; + fn try_from_val(env: &Env, v: &ArbitraryBytes) -> Result { + Self::try_from_val(env, &v.vec.as_slice()) + } + } + + ////////////////////////////////// + + #[derive(Arbitrary, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] + pub struct ArbitraryString { + inner: RustString, + } + + impl SorobanArbitrary for String { + type Prototype = ArbitraryString; + } + + impl TryFromVal for String { + type Error = ConversionError; + fn try_from_val(env: &Env, v: &ArbitraryString) -> Result { + Self::try_from_val(env, &v.inner.as_str()) + } + } + + ////////////////////////////////// + + #[derive(Arbitrary, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] + pub struct ArbitraryBytesN { + array: [u8; N], + } + + impl SorobanArbitrary for BytesN { + type Prototype = ArbitraryBytesN; + } + + impl TryFromVal> for BytesN { + type Error = ConversionError; + fn try_from_val(env: &Env, v: &ArbitraryBytesN) -> Result { + Self::try_from_val(env, &v.array) + } + } + + ////////////////////////////////// + + #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] + pub struct ArbitrarySymbol { + s: RustString, + } + + impl<'a> Arbitrary<'a> for ArbitrarySymbol { + fn arbitrary(u: &mut Unstructured<'a>) -> ArbitraryResult { + let valid_chars = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + let valid_chars = valid_chars.as_bytes(); + let mut chars = vec![]; + let len = u.int_in_range(0..=32)?; + for _ in 0..len { + let ch = u.choose(valid_chars)?; + chars.push(*ch); + } + Ok(ArbitrarySymbol { + s: RustString::from_utf8(chars).expect("utf8"), + }) + } + } + + impl SorobanArbitrary for Symbol { + type Prototype = ArbitrarySymbol; + } + + impl TryFromVal for Symbol { + type Error = ConversionError; + fn try_from_val(env: &Env, v: &ArbitrarySymbol) -> Result { + Self::try_from_val(env, &v.s.as_str()) + } + } + + ////////////////////////////////// + + #[derive(Arbitrary, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] + pub struct ArbitraryVec { + vec: RustVec, + } + + impl SorobanArbitrary for Vec + where + T: SorobanArbitrary, + { + type Prototype = ArbitraryVec; + } + + impl TryFromVal> for Vec + where + T: SorobanArbitrary, + { + type Error = ConversionError; + fn try_from_val(env: &Env, v: &ArbitraryVec) -> Result { + let mut buf: Vec = Vec::new(env); + for item in v.vec.iter() { + buf.push_back(item.into_val(env)); + } + Ok(buf) + } + } + + ////////////////////////////////// + + #[derive(Arbitrary, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] + pub struct ArbitraryMap { + map: RustVec<(K, V)>, + } + + impl SorobanArbitrary for Map + where + K: SorobanArbitrary, + V: SorobanArbitrary, + { + type Prototype = ArbitraryMap; + } + + impl TryFromVal> for Map + where + K: SorobanArbitrary, + V: SorobanArbitrary, + { + type Error = ConversionError; + fn try_from_val( + env: &Env, + v: &ArbitraryMap, + ) -> Result { + let mut map: Map = Map::new(env); + for (k, v) in v.map.iter() { + map.set(k.into_val(env), v.into_val(env)); + } + Ok(map) + } + } + + ////////////////////////////////// + + #[derive(Arbitrary, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] + pub struct ArbitraryAddress { + inner: [u8; 32], + } + + impl SorobanArbitrary for Address { + type Prototype = ArbitraryAddress; + } + + impl TryFromVal for Address { + type Error = ConversionError; + fn try_from_val(env: &Env, v: &ArbitraryAddress) -> Result { + use crate::env::xdr::{Hash, ScAddress}; + + let sc_addr = ScVal::Address(ScAddress::Contract(Hash(v.inner))); + Ok(sc_addr.into_val(env)) + } + } +} + +/// Implementations of `soroban_sdk::arbitrary::api` for `Val`. +mod composite { + use arbitrary::Arbitrary; + + use crate::arbitrary::api::*; + use crate::ConversionError; + use crate::{Env, IntoVal, TryFromVal}; + + use super::objects::*; + use super::simple::*; + use crate::{Address, Bytes, Map, String, Symbol, Val, Vec, I256, U256}; + + #[derive(Arbitrary, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] + pub enum ArbitraryVal { + Void, + Bool(bool), + Error(Error), + U32(u32), + I32(i32), + U64(u64), + I64(i64), + U128(u128), + I128(i128), + U256(ArbitraryU256), + I256(ArbitraryI256), + Bytes(ArbitraryBytes), + String(ArbitraryString), + Symbol(ArbitrarySymbol), + Vec(ArbitraryValVec), + Map(ArbitraryValMap), + Address(
::Prototype), + } + + impl SorobanArbitrary for Val { + type Prototype = ArbitraryVal; + } + + impl TryFromVal for Val { + type Error = ConversionError; + fn try_from_val(env: &Env, v: &ArbitraryVal) -> Result { + Ok(match v { + ArbitraryVal::Void => Val::VOID.into(), + ArbitraryVal::Bool(v) => v.into_val(env), + ArbitraryVal::Error(v) => v.into_val(env), + ArbitraryVal::U32(v) => v.into_val(env), + ArbitraryVal::I32(v) => v.into_val(env), + ArbitraryVal::U64(v) => v.into_val(env), + ArbitraryVal::I64(v) => v.into_val(env), + ArbitraryVal::U256(v) => { + let v: U256 = v.into_val(env); + v.into_val(env) + } + ArbitraryVal::I256(v) => { + let v: I256 = v.into_val(env); + v.into_val(env) + } + ArbitraryVal::U128(v) => v.into_val(env), + ArbitraryVal::I128(v) => v.into_val(env), + ArbitraryVal::Bytes(v) => { + let v: Bytes = v.into_val(env); + v.into_val(env) + } + ArbitraryVal::String(v) => { + let v: String = v.into_val(env); + v.into_val(env) + } + ArbitraryVal::Symbol(v) => { + let v: Symbol = v.into_val(env); + v.into_val(env) + } + ArbitraryVal::Vec(v) => v.into_val(env), + ArbitraryVal::Map(v) => v.into_val(env), + ArbitraryVal::Address(v) => { + let v: Address = v.into_val(env); + v.into_val(env) + } + }) + } + } + + #[derive(Arbitrary, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] + pub enum ArbitraryValVec { + Void( as SorobanArbitrary>::Prototype), + Bool( as SorobanArbitrary>::Prototype), + Error( as SorobanArbitrary>::Prototype), + U32( as SorobanArbitrary>::Prototype), + I32( as SorobanArbitrary>::Prototype), + U64( as SorobanArbitrary>::Prototype), + I64( as SorobanArbitrary>::Prototype), + U128( as SorobanArbitrary>::Prototype), + I128( as SorobanArbitrary>::Prototype), + U256( as SorobanArbitrary>::Prototype), + I256( as SorobanArbitrary>::Prototype), + Bytes( as SorobanArbitrary>::Prototype), + String( as SorobanArbitrary>::Prototype), + Symbol( as SorobanArbitrary>::Prototype), + Vec(> as SorobanArbitrary>::Prototype), + Map( as SorobanArbitrary>::Prototype), + Address( as SorobanArbitrary>::Prototype), + Val( as SorobanArbitrary>::Prototype), + } + + impl TryFromVal for Val { + type Error = ConversionError; + fn try_from_val(env: &Env, v: &ArbitraryValVec) -> Result { + Ok(match v { + ArbitraryValVec::Void(v) => { + let v: Vec<()> = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::Bool(v) => { + let v: Vec = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::Error(v) => { + let v: Vec = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::U32(v) => { + let v: Vec = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::I32(v) => { + let v: Vec = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::U64(v) => { + let v: Vec = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::I64(v) => { + let v: Vec = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::U128(v) => { + let v: Vec = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::I128(v) => { + let v: Vec = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::U256(v) => { + let v: Vec = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::I256(v) => { + let v: Vec = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::Bytes(v) => { + let v: Vec = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::String(v) => { + let v: Vec = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::Symbol(v) => { + let v: Vec = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::Vec(v) => { + let v: Vec> = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::Map(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::Address(v) => { + let v: Vec
= v.into_val(env); + v.into_val(env) + } + ArbitraryValVec::Val(v) => { + let v: Vec = v.into_val(env); + v.into_val(env) + } + }) + } + } + + #[derive(Arbitrary, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] + pub enum ArbitraryValMap { + VoidToVoid( as SorobanArbitrary>::Prototype), + BoolToBool( as SorobanArbitrary>::Prototype), + ErrorToError( as SorobanArbitrary>::Prototype), + U32ToU32( as SorobanArbitrary>::Prototype), + I32ToI32( as SorobanArbitrary>::Prototype), + U64ToU64( as SorobanArbitrary>::Prototype), + I64ToI64( as SorobanArbitrary>::Prototype), + U128ToU128( as SorobanArbitrary>::Prototype), + I128ToI128( as SorobanArbitrary>::Prototype), + U256ToU256( as SorobanArbitrary>::Prototype), + I256ToI256( as SorobanArbitrary>::Prototype), + BytesToBytes( as SorobanArbitrary>::Prototype), + StringToString( as SorobanArbitrary>::Prototype), + SymbolToSymbol( as SorobanArbitrary>::Prototype), + VecToVec(, Vec> as SorobanArbitrary>::Prototype), + MapToMap(, Map> as SorobanArbitrary>::Prototype), + AddressToAddress( as SorobanArbitrary>::Prototype), + ValToVal( as SorobanArbitrary>::Prototype), + } + + impl TryFromVal for Val { + type Error = ConversionError; + fn try_from_val(env: &Env, v: &ArbitraryValMap) -> Result { + Ok(match v { + ArbitraryValMap::VoidToVoid(v) => { + let v: Map<(), ()> = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::BoolToBool(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::ErrorToError(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::U32ToU32(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::I32ToI32(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::U64ToU64(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::I64ToI64(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::U128ToU128(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::I128ToI128(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::U256ToU256(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::I256ToI256(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::BytesToBytes(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::StringToString(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::SymbolToSymbol(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::VecToVec(v) => { + let v: Map, Vec> = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::MapToMap(v) => { + let v: Map, Map> = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::AddressToAddress(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + ArbitraryValMap::ValToVal(v) => { + let v: Map = v.into_val(env); + v.into_val(env) + } + }) + } + } +} + +/// Additional tools for writing fuzz tests. +mod fuzz_test_helpers { + use soroban_env_host::call_with_suppressed_panic_hook; + + /// Catch panics within a fuzz test. + /// + /// The `cargo-fuzz` test harness turns panics into test failures, + /// immediately aborting the process. + /// + /// This function catches panics while temporarily disabling the + /// `cargo-fuzz` panic handler. + /// + /// # Example + /// + /// ``` + /// # macro_rules! fuzz_target { + /// # (|$data:ident: $dty: ty| $body:block) => { }; + /// # } + /// # struct ExampleContract; + /// # impl ExampleContract { + /// # fn new(e: &soroban_sdk::Env, b: &soroban_sdk::BytesN<32>) { } + /// # fn deposit(&self, a: soroban_sdk::Address, n: i128) { } + /// # } + /// use soroban_sdk::{Address, Env}; + /// use soroban_sdk::arbitrary::{Arbitrary, SorobanArbitrary}; + /// + /// #[derive(Arbitrary, Debug)] + /// struct FuzzDeposit { + /// deposit_amount: i128, + /// deposit_address:
::Prototype, + /// } + /// + /// fuzz_target!(|input: FuzzDeposit| { + /// let env = Env::default(); + /// + /// let contract = ExampleContract::new(env, &env.register_contract(None, ExampleContract {})); + /// + /// let addresses: Address = input.deposit_address.into_val(&env); + /// let r = fuzz_catch_panic(|| { + /// contract.deposit(deposit_address, input.deposit_amount); + /// }); + /// }); + /// ``` + pub fn fuzz_catch_panic(f: F) -> std::thread::Result + where + F: FnOnce() -> R, + { + call_with_suppressed_panic_hook(std::panic::AssertUnwindSafe(f)) + } +} + +#[cfg(test)] +mod tests { + use crate::arbitrary::*; + use crate::{Bytes, BytesN, Map, String, Symbol, Val, Vec, I256, U256}; + use crate::{Env, IntoVal}; + use arbitrary::{Arbitrary, Unstructured}; + use rand::RngCore; + + fn run_test() + where + T: SorobanArbitrary, + T::Prototype: for<'a> Arbitrary<'a>, + { + let env = Env::default(); + let mut rng = rand::thread_rng(); + let mut rng_data = [0u8; 64]; + + for _ in 0..100 { + rng.fill_bytes(&mut rng_data); + let mut unstructured = Unstructured::new(&rng_data); + let input = T::Prototype::arbitrary(&mut unstructured).expect("SorobanArbitrary"); + let _val: T = input.into_val(&env); + } + } + + #[test] + fn test_u32() { + run_test::() + } + + #[test] + fn test_i32() { + run_test::() + } + + #[test] + fn test_u64() { + run_test::() + } + + #[test] + fn test_i64() { + run_test::() + } + + #[test] + fn test_u128() { + run_test::() + } + + #[test] + fn test_i128() { + run_test::() + } + + #[test] + fn test_u256() { + run_test::() + } + + #[test] + fn test_i256() { + run_test::() + } + + #[test] + fn test_bytes() { + run_test::() + } + + #[test] + fn test_string() { + run_test::() + } + + #[test] + fn test_symbol() { + run_test::() + } + + #[test] + fn test_bytes_n() { + run_test::>() + } + + #[test] + fn test_vec_u32() { + run_test::>() + } + + #[test] + fn test_vec_i32() { + run_test::>() + } + + #[test] + fn test_vec_bytes() { + run_test::>() + } + + #[test] + fn test_vec_bytes_n() { + run_test::>>() + } + + #[test] + fn test_vec_vec_bytes() { + run_test::>>() + } + + #[test] + fn test_map_u32() { + run_test::>>() + } + + #[test] + fn test_map_i32() { + run_test::>>() + } + + #[test] + fn test_map_bytes() { + run_test::>() + } + + #[test] + fn test_map_bytes_n() { + run_test::, Bytes>>() + } + + #[test] + fn test_map_vec() { + run_test::, Vec>>() + } + + #[test] + fn test_raw_val() { + run_test::() + } + + mod user_defined_types { + use crate as soroban_sdk; + use crate::arbitrary::tests::run_test; + use crate::{Address, Bytes, BytesN, Error, Map, Symbol, Vec, I256, U256}; + use soroban_sdk::contracttype; + + #[contracttype] + #[derive(Clone, Debug, Eq, PartialEq)] + struct PrivStruct { + count_u: u32, + count_i: i32, + bytes_n: BytesN<32>, + vec: Vec, + map: Map>, + u256: U256, + i156: I256, + error: Error, + address: Address, + symbol: Symbol, + } + + #[test] + fn test_user_defined_priv_struct() { + run_test::(); + } + + #[contracttype] + #[derive(Clone, Debug, Eq, PartialEq)] + struct PrivStructPubFields { + pub count_u: u32, + pub count_i: i32, + pub bytes_n: BytesN<32>, + pub vec: Vec, + pub map: Map>, + } + + #[test] + fn test_user_defined_priv_struct_pub_fields() { + run_test::(); + } + + #[contracttype] + #[derive(Clone, Debug, Eq, PartialEq)] + pub struct PubStruct { + count_u: u32, + count_i: i32, + bytes_n: BytesN<32>, + vec: Vec, + map: Map>, + } + + #[test] + fn test_user_defined_pub_struct() { + run_test::(); + } + + #[contracttype] + #[derive(Clone, Debug, Eq, PartialEq)] + pub struct PubStructPubFields { + pub count_u: u32, + pub count_i: i32, + pub bytes_n: BytesN<32>, + pub vec: Vec, + pub map: Map>, + } + + #[test] + fn test_user_defined_pubstruct_pub_fields() { + run_test::(); + } + + #[contracttype] + #[derive(Clone, Debug, Eq, PartialEq)] + struct PrivTupleStruct(u32, i32, BytesN<32>, Vec, Map>); + + #[test] + fn test_user_defined_priv_tuple_struct() { + run_test::(); + } + + #[contracttype] + #[derive(Clone, Debug, Eq, PartialEq)] + struct PrivTupleStructPubFields( + pub u32, + pub i32, + pub BytesN<32>, + pub Vec, + pub Map>, + ); + + #[test] + fn test_user_defined_priv_tuple_struct_pub_fields() { + run_test::(); + } + + #[contracttype] + #[derive(Clone, Debug, Eq, PartialEq)] + pub struct PubTupleStruct(u32, i32, BytesN<32>, Vec, Map>); + + #[test] + fn test_user_defined_pub_tuple_struct() { + run_test::(); + } + + #[contracttype] + #[derive(Clone, Debug, Eq, PartialEq)] + pub struct PubTupleStructPubFields( + pub u32, + pub i32, + pub BytesN<32>, + pub Vec, + pub Map>, + ); + + #[test] + fn test_user_defined_pub_tuple_struct_pub_fields() { + run_test::(); + } + + #[contracttype] + #[derive(Clone, Debug, Eq, PartialEq)] + pub(crate) struct PubCrateStruct(u32); + + #[test] + fn test_user_defined_pub_crate_struct() { + run_test::(); + } + + #[contracttype] + #[derive(Clone, Debug, Eq, PartialEq)] + enum PrivEnum { + A(u32), + Aa(u32, u32), + C, + D, + } + + #[test] + fn test_user_defined_priv_enum() { + run_test::(); + } + + #[contracttype] + #[derive(Clone, Debug, Eq, PartialEq)] + pub enum PubEnum { + A(u32), + C, + D, + } + + #[test] + fn test_user_defined_pub_enum() { + run_test::(); + } + + #[contracttype] + #[derive(Clone, Debug, Eq, PartialEq)] + pub(crate) enum PubCrateEnum { + A(u32), + C, + D, + } + + #[test] + fn test_user_defined_pub_crate_enum() { + run_test::(); + } + + #[contracttype] + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + enum PrivEnumInt { + A = 1, + C = 2, + D = 3, + } + + #[test] + fn test_user_defined_priv_enum_int() { + run_test::(); + } + + #[contracttype] + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub enum PubEnumInt { + A = 1, + C = 2, + D = 3, + } + + #[test] + fn test_user_defined_pub_enum_int() { + run_test::(); + } + + #[test] + fn test_declared_inside_a_fn() { + #[contracttype] + struct Foo { + a: u32, + } + + #[contracttype] + enum Bar { + Baz, + Qux, + } + + run_test::(); + run_test::(); + } + } +} diff --git a/soroban-sdk/src/arbitrary_extra.rs b/soroban-sdk/src/arbitrary_extra.rs new file mode 100644 index 000000000..9e05794dc --- /dev/null +++ b/soroban-sdk/src/arbitrary_extra.rs @@ -0,0 +1,71 @@ +//! Extra trait impls required by the bounds to `SorobanArbitrary`. +//! +//! These are in their own module so that they are defined even when "testutils" +//! is not configured, making type inference consistent between configurations. + +use crate::ConversionError; +use crate::Error; +use crate::{Env, TryFromVal}; + +impl TryFromVal for u32 { + type Error = ConversionError; + fn try_from_val(_env: &Env, v: &u32) -> Result { + Ok(*v) + } +} + +impl TryFromVal for i32 { + type Error = ConversionError; + fn try_from_val(_env: &Env, v: &i32) -> Result { + Ok(*v) + } +} + +impl TryFromVal for u64 { + type Error = ConversionError; + fn try_from_val(_env: &Env, v: &u64) -> Result { + Ok(*v) + } +} + +impl TryFromVal for i64 { + type Error = ConversionError; + fn try_from_val(_env: &Env, v: &i64) -> Result { + Ok(*v) + } +} + +impl TryFromVal for u128 { + type Error = ConversionError; + fn try_from_val(_env: &Env, v: &u128) -> Result { + Ok(*v) + } +} + +impl TryFromVal for i128 { + type Error = ConversionError; + fn try_from_val(_env: &Env, v: &i128) -> Result { + Ok(*v) + } +} + +impl TryFromVal for bool { + type Error = ConversionError; + fn try_from_val(_env: &Env, v: &bool) -> Result { + Ok(*v) + } +} + +impl TryFromVal for () { + type Error = ConversionError; + fn try_from_val(_env: &Env, v: &()) -> Result { + Ok(*v) + } +} + +impl TryFromVal for Error { + type Error = ConversionError; + fn try_from_val(_env: &Env, v: &Error) -> Result { + Ok(*v) + } +} diff --git a/soroban-sdk/src/lib.rs b/soroban-sdk/src/lib.rs index 88e1b70cc..294ca6968 100644 --- a/soroban-sdk/src/lib.rs +++ b/soroban-sdk/src/lib.rs @@ -686,4 +686,7 @@ pub mod xdr; pub mod testutils; +pub mod arbitrary; +mod arbitrary_extra; + mod tests; diff --git a/soroban-sdk/src/tests.rs b/soroban-sdk/src/tests.rs index 3c33ea635..2bcad8d75 100644 --- a/soroban-sdk/src/tests.rs +++ b/soroban-sdk/src/tests.rs @@ -18,5 +18,7 @@ mod contractimport; mod contractimport_with_error; mod contractimport_with_sha256; mod env; +mod proptest_scval_cmp; +mod proptest_val_cmp; mod token_client; mod token_spec; diff --git a/soroban-sdk/src/tests/proptest_scval_cmp.rs b/soroban-sdk/src/tests/proptest_scval_cmp.rs new file mode 100644 index 000000000..fb18ab391 --- /dev/null +++ b/soroban-sdk/src/tests/proptest_scval_cmp.rs @@ -0,0 +1,121 @@ +//! Check that Val and ScVal can be converted between each other, +//! and that their comparison functions are equivalent. + +use crate::xdr::ScVal; +use crate::Env; +use crate::TryFromVal; +use crate::Val; +use core::cmp::Ordering; +use proptest::prelude::*; +use proptest_arbitrary_interop::arb; +use soroban_env_host::Compare; + +proptest! { + #![proptest_config(ProptestConfig::with_cases(10000))] + + #[test] + fn test( + scval_1 in arb::(), + scval_2 in arb::(), + ) { + let env = &Env::default(); + + // Compare Ord & PartialOrd + let scval_cmp = Ord::cmp(&scval_1, &scval_2); + let scval_cmp_partial = PartialOrd::partial_cmp(&scval_1, &scval_2); + + prop_assert_eq!(Some(scval_cmp), scval_cmp_partial); + + let rawval_1 = Val::try_from_val(env, &scval_1); + let rawval_1 = match rawval_1 { + Ok(rawval_1) => rawval_1, + Err(_) => { + // Many ScVal's are invalid: + // + // - LedgerKeyNonce + // - Vec(None), Map(None) + // - Symbol with invalid chars + // - Map with duplicate keys + // - Containers with the above + return Ok(()); + } + }; + + let rawval_2 = Val::try_from_val(env, &scval_2); + let rawval_2 = match rawval_2 { + Ok(rawval_2) => rawval_2, + Err(_) => { + return Ok(()); + } + }; + + let rawval_cmp = env.compare(&rawval_1, &rawval_2).expect("cmp"); + + if scval_cmp != rawval_cmp { + panic!( + "scval and rawval don't compare the same:\n\ + {scval_1:#?}\n\ + {scval_2:#?}\n\ + {scval_cmp:#?}\n\ + {rawval_1:#?}\n\ + {rawval_2:#?}\n\ + {rawval_cmp:#?}" + ); + } + + // Compare Eq + let scval_partial_eq = PartialEq::eq(&scval_1, &scval_2); + let rawval_cmp_is_eq = scval_cmp == Ordering::Equal; + + prop_assert_eq!(scval_partial_eq, rawval_cmp_is_eq); + + // Compare for Budget + let budget = env.budget().0; + let scval_budget_cmp = budget.compare(&scval_1, &scval_2).expect("cmp"); + + if scval_budget_cmp != scval_cmp { + panic!( + "scval (budget) and scval don't compare the same:\n\ + {scval_1:#?}\n\ + {scval_2:#?}\n\ + {scval_budget_cmp:#?}\n\ + {scval_cmp:#?}" + ); + } + + // Roundtrip checks + { + let scval_after_1 = ScVal::try_from_val(env, &rawval_1); + let scval_after_1 = match scval_after_1 { + Ok(scval_after_1) => scval_after_1, + Err(e) => { + panic!( + "couldn't convert rawval to scval:\n\ + {rawval_1:?},\n\ + {scval_1:?},\n\ + {e:#?}" + ); + } + }; + + let scval_cmp_before_after_1 = Ord::cmp(&scval_1, &scval_after_1); + prop_assert_eq!(scval_cmp_before_after_1, Ordering::Equal); + + let scval_after_2 = ScVal::try_from_val(env, &rawval_2); + let scval_after_2 = match scval_after_2 { + Ok(scval_after_2) => scval_after_2, + Err(e) => { + panic!( + "couldn't convert rawval to scval:\n\ + {rawval_2:?},\n\ + {scval_2:?},\n\ + {e:#?}" + ); + } + }; + + let scval_cmp_before_after_2 = Ord::cmp(&scval_2, &scval_after_2); + prop_assert_eq!(scval_cmp_before_after_2, Ordering::Equal); + } + } +} diff --git a/soroban-sdk/src/tests/proptest_val_cmp.rs b/soroban-sdk/src/tests/proptest_val_cmp.rs new file mode 100644 index 000000000..df5ffc1bd --- /dev/null +++ b/soroban-sdk/src/tests/proptest_val_cmp.rs @@ -0,0 +1,135 @@ +//! Check that Val and ScVal can be converted between each other, +//! and that their comparison functions are equivalent. + +use crate::arbitrary::SorobanArbitrary; +use crate::xdr::ScVal; +use crate::Env; +use crate::Val; +use crate::{FromVal, TryFromVal}; +use core::cmp::Ordering; +use proptest::prelude::*; +use proptest_arbitrary_interop::arb; +use soroban_env_host::Compare; + +proptest! { + #![proptest_config(ProptestConfig::with_cases(10000))] + + #[test] + fn test( + rawval_proto_1 in arb::<::Prototype>(), + rawval_proto_2 in arb::<::Prototype>(), + ) { + let env = &Env::default(); + let budget = env.budget().0; + + let rawval_1 = Val::from_val(env, &rawval_proto_1); + let rawval_2 = Val::from_val(env, &rawval_proto_2); + + let (scval_1, scval_2) = { + let scval_1 = ScVal::try_from_val(env, &rawval_1); + let scval_2 = ScVal::try_from_val(env, &rawval_2); + + let scval_1 = match scval_1 { + Ok(scval_1) => scval_1, + Err(e) => { + panic!( + "couldn't convert rawval to scval:\n\ + {rawval_1:?},\n\ + {e:#?}" + ); + } + }; + + let scval_2 = match scval_2 { + Ok(scval_2) => scval_2, + Err(e) => { + panic!( + "couldn't convert rawval to scval:\n\ + {rawval_2:?},\n\ + {e:#?}" + ); + } + }; + + (scval_1, scval_2) + }; + + // Check the comparison functions + { + let rawval_cmp = env.compare(&rawval_1, &rawval_2); + let rawval_cmp = rawval_cmp.expect("cmp"); + let scval_cmp = Ord::cmp(&scval_1, &scval_2); + + let rawval_cmp_is_eq = rawval_cmp == Ordering::Equal; + + if rawval_cmp != scval_cmp { + panic!( + "rawval and scval don't compare the same:\n\ + {rawval_1:#?}\n\ + {rawval_2:#?}\n\ + {rawval_cmp:#?}\n\ + {scval_1:#?}\n\ + {scval_2:#?}\n\ + {scval_cmp:#?}" + ); + } + + let scval_cmp_partial = PartialOrd::partial_cmp(&scval_1, &scval_2); + + prop_assert_eq!(Some(scval_cmp), scval_cmp_partial); + + let scval_partial_eq = PartialEq::eq(&scval_1, &scval_2); + prop_assert_eq!(rawval_cmp_is_eq, scval_partial_eq); + + // Compare for Budget + let scval_budget_cmp = budget.compare(&scval_1, &scval_2); + let scval_budget_cmp = scval_budget_cmp.expect("cmp"); + if rawval_cmp != scval_budget_cmp { + panic!( + "rawval and scval (budget) don't compare the same:\n\ + {rawval_1:#?}\n\ + {rawval_2:#?}\n\ + {rawval_cmp:#?}\n\ + {scval_1:#?}\n\ + {scval_2:#?}\n\ + {scval_budget_cmp:#?}" + ); + } + } + + // Roundtrip checks + { + let rawval_after_1 = Val::try_from_val(env, &scval_1); + let rawval_after_1 = match rawval_after_1 { + Ok(rawval_after_1) => rawval_after_1, + Err(e) => { + panic!( + "couldn't convert scval to rawval:\n\ + {scval_1:?},\n\ + {e:#?}" + ); + } + }; + + let rawval_cmp_before_after_1 = env.compare(&rawval_1, &rawval_after_1).expect("compare"); + + prop_assert_eq!(rawval_cmp_before_after_1, Ordering::Equal); + + let rawval_after_2 = Val::try_from_val(env, &scval_2); + let rawval_after_2 = match rawval_after_2 { + Ok(rawval_after_2) => rawval_after_2, + Err(e) => { + panic!( + "couldn't convert scval to rawval:\n\ + {scval_2:?},\n\ + {e:#?}" + ); + } + }; + + let rawval_cmp_before_after_2 = env.compare(&rawval_2, &rawval_after_2).expect("compare"); + + prop_assert_eq!(rawval_cmp_before_after_2, Ordering::Equal); + } + } +} diff --git a/soroban-sdk/src/testutils.rs b/soroban-sdk/src/testutils.rs index 15d4bd3cd..13f84c983 100644 --- a/soroban-sdk/src/testutils.rs +++ b/soroban-sdk/src/testutils.rs @@ -68,7 +68,7 @@ pub mod budget { /// # #[cfg(not(feature = "testutils"))] /// # fn main() { } /// ``` - pub struct Budget(crate::env::internal::budget::Budget); + pub struct Budget(pub(crate) crate::env::internal::budget::Budget); impl Display for Budget { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { diff --git a/tests/fuzz/Cargo.toml b/tests/fuzz/Cargo.toml new file mode 100644 index 000000000..8184f09c7 --- /dev/null +++ b/tests/fuzz/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "test_fuzz" +version.workspace = true +authors = ["Stellar Development Foundation "] +license = "Apache-2.0" +edition = "2021" +publish = false +rust-version = "1.70" + +[lib] +# Adding rlib is required so that the test_fuzz crate can be imported as a +# library into the fuzzing crate. However, having multiple crate types is a +# problem, it'll cause LTO optimizations to be disabled for the cdylib build. +# TODO: Figure out what to do about this. +crate-type = ["cdylib", "rlib"] +doctest = false + +[features] +testutils = ["soroban-sdk/testutils"] + +[dependencies] +soroban-sdk = {path = "../../soroban-sdk"} + +[dev-dependencies] +soroban-sdk = {path = "../../soroban-sdk", features = ["testutils"]} diff --git a/tests/fuzz/fuzz/.gitignore b/tests/fuzz/fuzz/.gitignore new file mode 100644 index 000000000..1a45eee77 --- /dev/null +++ b/tests/fuzz/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/tests/fuzz/fuzz/Cargo.lock b/tests/fuzz/fuzz/Cargo.lock new file mode 100644 index 000000000..ac8000c97 --- /dev/null +++ b/tests/fuzz/fuzz/Cargo.lock @@ -0,0 +1,1460 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "arbitrary" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base32" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes-lit" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0adabf37211a5276e46335feabcbb1530c95eb3fdf85f324c7db942770aa025d" +dependencies = [ + "num-bigint", + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "winapi", +] + +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpufeatures" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" +dependencies = [ + "libc", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f998aef136a4e7833b0e4f0fc0939a59c40140b28e0ffbf524ad84fb2cc568c8" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "darling" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.18", +] + +[[package]] +name = "darling_macro" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56acb310e15652100da43d130af8d97b509e95af61aab1c5a7939ef24337ee17" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "ecdsa" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature 2.1.0", + "spki", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature 1.6.4", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "ethnum" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0198b9d0078e0f30dedc7acbb21c974e838fc8fae3ee170128658a98cb2c1c04" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", + "serde", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[package]] +name = "intx" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f38a50a899dc47a6d0ed5508e7f601a2e34c3a85303514b5d137f3c10a0c75" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.7", + "signature 2.1.0", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.146" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beb09950ae85a0a94b27676cccf37da5ff13f27076aa1adbc6545dd0d0e1bd4e" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b0377b720bde721213a46cda1289b2f34abf0a436907cad91578c20de0454d" +dependencies = [ + "proc-macro2", + "syn 2.0.18", +] + +[[package]] +name = "proc-macro2" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.10", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "sec1" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "serde" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "serde_json" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513" +dependencies = [ + "base64 0.21.2", + "chrono", + "hex", + "indexmap", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "soroban-env-common" +version = "0.0.16" +source = "git+https://github.com/stellar/rs-soroban-env?rev=c43bbd47959dde2e39eeeb5b7207868a44e96c7d#c43bbd47959dde2e39eeeb5b7207868a44e96c7d" +dependencies = [ + "arbitrary", + "crate-git-revision", + "ethnum", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr", +] + +[[package]] +name = "soroban-env-guest" +version = "0.0.16" +source = "git+https://github.com/stellar/rs-soroban-env?rev=c43bbd47959dde2e39eeeb5b7207868a44e96c7d#c43bbd47959dde2e39eeeb5b7207868a44e96c7d" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "0.0.16" +source = "git+https://github.com/stellar/rs-soroban-env?rev=c43bbd47959dde2e39eeeb5b7207868a44e96c7d#c43bbd47959dde2e39eeeb5b7207868a44e96c7d" +dependencies = [ + "backtrace", + "curve25519-dalek", + "ed25519-dalek", + "getrandom 0.2.10", + "hex", + "k256", + "log", + "num-derive", + "num-integer", + "num-traits", + "rand", + "rand_chacha", + "sha2 0.9.9", + "sha3", + "soroban-env-common", + "soroban-native-sdk-macros", + "soroban-wasmi", + "static_assertions", + "stellar-strkey", +] + +[[package]] +name = "soroban-env-macros" +version = "0.0.16" +source = "git+https://github.com/stellar/rs-soroban-env?rev=c43bbd47959dde2e39eeeb5b7207868a44e96c7d#c43bbd47959dde2e39eeeb5b7207868a44e96c7d" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr", + "syn 2.0.18", + "thiserror", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "0.8.4" +dependencies = [ + "serde", + "serde_json", + "soroban-env-common", + "soroban-env-host", + "thiserror", +] + +[[package]] +name = "soroban-native-sdk-macros" +version = "0.0.16" +source = "git+https://github.com/stellar/rs-soroban-env?rev=c43bbd47959dde2e39eeeb5b7207868a44e96c7d#c43bbd47959dde2e39eeeb5b7207868a44e96c7d" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "soroban-sdk" +version = "0.8.4" +dependencies = [ + "arbitrary", + "bytes-lit", + "ed25519-dalek", + "rand", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey", +] + +[[package]] +name = "soroban-sdk-macros" +version = "0.8.4" +dependencies = [ + "darling", + "itertools", + "proc-macro2", + "quote", + "sha2 0.9.9", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", + "syn 2.0.18", +] + +[[package]] +name = "soroban-spec" +version = "0.8.4" +dependencies = [ + "base64 0.13.1", + "stellar-xdr", + "thiserror", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "0.8.4" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2 0.9.9", + "soroban-spec", + "stellar-xdr", + "syn 2.0.18", + "thiserror", +] + +[[package]] +name = "soroban-wasmi" +version = "0.30.0-soroban" +source = "git+https://github.com/stellar/wasmi?rev=1a2bc7f#1a2bc7f3801c565c2dab22021255a164c05a7f76" +dependencies = [ + "intx", + "smallvec", + "soroban-wasmi_core", + "spin", + "wasmi_arena", + "wasmparser-nostd", +] + +[[package]] +name = "soroban-wasmi_core" +version = "0.30.0-soroban" +source = "git+https://github.com/stellar/wasmi?rev=1a2bc7f#1a2bc7f3801c565c2dab22021255a164c05a7f76" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stellar-strkey" +version = "0.0.7" +source = "git+https://github.com/stellar/rs-stellar-strkey?rev=e6ba45c60c16de28c7522586b80ed0150157df73#e6ba45c60c16de28c7522586b80ed0150157df73" +dependencies = [ + "base32", + "thiserror", +] + +[[package]] +name = "stellar-xdr" +version = "0.0.16" +source = "git+https://github.com/stellar/rs-stellar-xdr?rev=50c7a57e55603bc57719b1d096091b3239ea6859#50c7a57e55603bc57719b1d096091b3239ea6859" +dependencies = [ + "arbitrary", + "base64 0.13.1", + "crate-git-revision", + "hex", + "serde", + "serde_with", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "test_fuzz" +version = "0.8.4" +dependencies = [ + "soroban-sdk", +] + +[[package]] +name = "test_fuzz-fuzz" +version = "0.0.0" +dependencies = [ + "libfuzzer-sys", + "soroban-sdk", + "test_fuzz", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "time" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasmi_arena" +version = "0.4.0" +source = "git+https://github.com/stellar/wasmi?rev=1a2bc7f#1a2bc7f3801c565c2dab22021255a164c05a7f76" + +[[package]] +name = "wasmparser" +version = "0.88.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8cf7dd82407fe68161bedcd57fde15596f32ebf6e9b3bdbf3ae1da20e38e5e" +dependencies = [ + "indexmap", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] diff --git a/tests/fuzz/fuzz/Cargo.toml b/tests/fuzz/fuzz/Cargo.toml new file mode 100644 index 000000000..ab7af11e3 --- /dev/null +++ b/tests/fuzz/fuzz/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "test_fuzz-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" +soroban-sdk = { path = "../../../soroban-sdk", features = [ "testutils" ]} +test_fuzz = { path = "..", features = [ "testutils" ] } + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "fuzz_target_1" +path = "fuzz_targets/fuzz_target_1.rs" +test = false +doc = false diff --git a/tests/fuzz/fuzz/fuzz_targets/fuzz_target_1.rs b/tests/fuzz/fuzz/fuzz_targets/fuzz_target_1.rs new file mode 100644 index 000000000..617b4463d --- /dev/null +++ b/tests/fuzz/fuzz/fuzz_targets/fuzz_target_1.rs @@ -0,0 +1,25 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; + +use soroban_sdk::{arbitrary::{arbitrary,Arbitrary,SorobanArbitrary}, Env, IntoVal, U256}; + +use test_fuzz::{Contract, ContractClient}; + +#[derive(Arbitrary, Debug)] +struct Input { + a: ::Prototype, + b: ::Prototype, +} + +fuzz_target!(|input: Input| { + let env = Env::default(); + + let a: U256 = input.a.into_val(&env); + let b: U256 = input.b.into_val(&env); + + let contract_id = env.register_contract(None, Contract); + let client = ContractClient::new(&env, &contract_id); + + let _ = client.run(&a, &b); +}); diff --git a/tests/fuzz/src/lib.rs b/tests/fuzz/src/lib.rs new file mode 100644 index 000000000..fa6c3a6df --- /dev/null +++ b/tests/fuzz/src/lib.rs @@ -0,0 +1,13 @@ +#![no_std] +use soroban_sdk::{contractimpl, U256}; + +pub struct Contract; + +#[contractimpl] +impl Contract { + pub fn run(a: U256, b: U256) { + if a < b { + panic!("unexpected") + } + } +}