From 3f41ee77e48be5c5fb09fe009017c57316e321be Mon Sep 17 00:00:00 2001 From: Kaycke Date: Sat, 23 May 2026 20:04:03 -0300 Subject: [PATCH] test: add Stellar contract property coverage --- .github/workflows/ci.yml | 16 + README.md | 12 + stellar/Cargo.lock | 442 +++++++++++++++++- stellar/stealth-announcer/Cargo.toml | 3 +- stellar/stealth-announcer/tests/properties.rs | 117 +++++ stellar/stealth-registry/Cargo.toml | 3 +- stellar/stealth-registry/tests/properties.rs | 122 +++++ stellar/stealth-sender/Cargo.toml | 3 +- stellar/stealth-sender/tests/properties.rs | 212 +++++++++ stellar/wraith-names/Cargo.toml | 3 +- stellar/wraith-names/tests/properties.rs | 157 +++++++ 11 files changed, 1068 insertions(+), 22 deletions(-) create mode 100644 stellar/stealth-announcer/tests/properties.rs create mode 100644 stellar/stealth-registry/tests/properties.rs create mode 100644 stellar/stealth-sender/tests/properties.rs create mode 100644 stellar/wraith-names/tests/properties.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 446927c..8726b9b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,8 @@ on: branches: [main] pull_request: branches: [main] + schedule: + - cron: '23 4 * * *' jobs: changes: @@ -62,6 +64,20 @@ jobs: - run: cargo fmt --all --check - run: cargo test --workspace + stellar-nightly: + if: github.event_name == 'schedule' + runs-on: ubuntu-latest + defaults: + run: + working-directory: stellar + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - run: cargo fmt --all --check + - run: WRAITH_PROPTEST_CASES=16384 cargo test --workspace --test properties + solana: needs: changes if: needs.changes.outputs.solana == 'true' diff --git a/README.md b/README.md index 922c8d1..b681a77 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,18 @@ cd stellar cargo test --workspace ``` +#### Property Tests + +The Stellar Soroban crates include `proptest` integration tests in each crate's `tests/properties.rs`. They cover event emission, register/lookup round-trips, invalid input rejection, batch-send invariants, and name lifecycle behavior. + +```bash +cd stellar +cargo test --workspace --test properties +WRAITH_PROPTEST_CASES=16384 cargo test --workspace --test properties +``` + +By default each property runs at least 1,024 generated cases. The scheduled `stellar-nightly` CI job raises that to 16,384 cases through `WRAITH_PROPTEST_CASES`. Add new properties beside the contract they cover so failures point directly at the affected crate. + ### Solana ```bash diff --git a/stellar/Cargo.lock b/stellar/Cargo.lock index 3f2a0f3..176ea29 100644 --- a/stellar/Cargo.lock +++ b/stellar/Cargo.lock @@ -23,6 +23,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + [[package]] name = "arbitrary" version = "1.3.2" @@ -147,7 +153,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", ] [[package]] @@ -180,6 +186,27 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + [[package]] name = "block-buffer" version = "0.10.4" @@ -274,7 +301,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -498,7 +525,7 @@ checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ "curve25519-dalek", "ed25519", - "rand_core", + "rand_core 0.6.4", "serde", "sha2", "subtle", @@ -523,7 +550,7 @@ dependencies = [ "ff", "generic-array", "group", - "rand_core", + "rand_core 0.6.4", "sec1", "subtle", "zeroize", @@ -535,6 +562,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "escape-bytes" version = "0.1.1" @@ -547,13 +584,19 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b" +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + [[package]] name = "ff" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -575,6 +618,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "generic-array" version = "0.14.9" @@ -599,6 +648,31 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + [[package]] name = "group" version = "0.13.0" @@ -606,7 +680,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -625,12 +699,27 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + [[package]] name = "hashbrown" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hex" version = "0.4.3" @@ -679,6 +768,12 @@ dependencies = [ "cc", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -760,6 +855,12 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" version = "0.2.184" @@ -772,6 +873,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "log" version = "0.4.29" @@ -906,6 +1013,31 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "num-traits", + "rand 0.9.4", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[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.45" @@ -915,6 +1047,18 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rand" version = "0.8.5" @@ -922,8 +1066,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", ] [[package]] @@ -933,7 +1087,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", ] [[package]] @@ -942,7 +1106,25 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.5", ] [[package]] @@ -965,6 +1147,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + [[package]] name = "rfc6979" version = "0.4.0" @@ -984,12 +1172,37 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rustversion" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + [[package]] name = "schemars" version = "0.9.0" @@ -1141,7 +1354,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -1178,7 +1391,7 @@ dependencies = [ "soroban-wasmi", "static_assertions", "stellar-xdr", - "wasmparser", + "wasmparser 0.116.1", ] [[package]] @@ -1206,7 +1419,7 @@ dependencies = [ "ed25519-dalek", "elliptic-curve", "generic-array", - "getrandom", + "getrandom 0.2.17", "hex-literal", "hmac", "k256", @@ -1214,8 +1427,8 @@ dependencies = [ "num-integer", "num-traits", "p256", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "sec1", "sha2", "sha3", @@ -1224,7 +1437,7 @@ dependencies = [ "soroban-wasmi", "static_assertions", "stellar-strkey", - "wasmparser", + "wasmparser 0.116.1", ] [[package]] @@ -1267,7 +1480,7 @@ dependencies = [ "ctor", "derive_arbitrary", "ed25519-dalek", - "rand", + "rand 0.8.5", "rustc_version", "serde", "serde_json", @@ -1307,7 +1520,7 @@ dependencies = [ "base64 0.13.1", "stellar-xdr", "thiserror", - "wasmparser", + "wasmparser 0.116.1", ] [[package]] @@ -1365,6 +1578,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" name = "stealth-announcer" version = "0.1.0" dependencies = [ + "proptest", "soroban-sdk", ] @@ -1372,6 +1586,7 @@ dependencies = [ name = "stealth-registry" version = "0.1.0" dependencies = [ + "proptest", "soroban-sdk", ] @@ -1379,6 +1594,7 @@ dependencies = [ name = "stealth-sender" version = "0.1.0" dependencies = [ + "proptest", "soroban-sdk", ] @@ -1443,6 +1659,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -1500,24 +1729,63 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[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.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + [[package]] name = "wasm-bindgen" version = "0.2.118" @@ -1563,6 +1831,28 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser 0.244.0", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.14.0", + "wasm-encoder", + "wasmparser 0.244.0", +] + [[package]] name = "wasmi_arena" version = "0.4.1" @@ -1591,6 +1881,18 @@ dependencies = [ "semver", ] +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap 2.14.0", + "semver", +] + [[package]] name = "wasmparser-nostd" version = "0.100.2" @@ -1659,10 +1961,114 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap 2.14.0", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap 2.14.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser 0.244.0", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.14.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.244.0", +] + [[package]] name = "wraith-names" version = "0.1.0" dependencies = [ + "proptest", "soroban-sdk", ] diff --git a/stellar/stealth-announcer/Cargo.toml b/stellar/stealth-announcer/Cargo.toml index d2c851d..f5990d2 100644 --- a/stellar/stealth-announcer/Cargo.toml +++ b/stellar/stealth-announcer/Cargo.toml @@ -4,10 +4,11 @@ version = "0.1.0" edition = "2021" [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "rlib"] [dependencies] soroban-sdk = { workspace = true } [dev-dependencies] +proptest = "1.6.0" soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/stellar/stealth-announcer/tests/properties.rs b/stellar/stealth-announcer/tests/properties.rs new file mode 100644 index 0000000..5fc9d6e --- /dev/null +++ b/stellar/stealth-announcer/tests/properties.rs @@ -0,0 +1,117 @@ +use proptest::prelude::*; +use soroban_sdk::testutils::{Address as _, EnvTestConfig, Events}; +use soroban_sdk::{symbol_short, vec, Address, Bytes, BytesN, Env, IntoVal, TryFromVal, Val}; +use stealth_announcer::{StealthAnnouncerContract, StealthAnnouncerContractClient}; + +fn cases() -> u32 { + std::env::var("WRAITH_PROPTEST_CASES") + .ok() + .and_then(|value| value.parse().ok()) + .unwrap_or(1024) +} + +fn env() -> Env { + Env::new_with_config(EnvTestConfig { + capture_snapshot_at_drop: false, + }) +} + +fn bytes(env: &Env, data: &[u8]) -> Bytes { + Bytes::from_slice(env, data) +} + +fn bytes32(env: &Env, data: &[u8]) -> BytesN<32> { + let mut fixed = [0u8; 32]; + fixed.copy_from_slice(data); + BytesN::from_array(env, &fixed) +} + +proptest! { + #![proptest_config(ProptestConfig { cases: cases(), .. ProptestConfig::default() })] + #[test] + fn announces_once_for_valid_payloads(scheme_id in any::(), epk in any::<[u8; 32]>(), metadata in prop::collection::vec(any::(), 0..128)) { + let env = env(); + let contract_id = env.register(StealthAnnouncerContract, ()); + let client = StealthAnnouncerContractClient::new(&env, &contract_id); + let stealth_address = Address::generate(&env); + + client.announce(&scheme_id, &stealth_address, &bytes32(&env, &epk), &bytes(&env, &metadata)); + + prop_assert_eq!(env.events().all().len(), 1); + } + + #[test] + fn topics_round_trip_verbatim(scheme_id in any::(), epk in any::<[u8; 32]>(), metadata in prop::collection::vec(any::(), 0..128)) { + let env = env(); + let contract_id = env.register(StealthAnnouncerContract, ()); + let client = StealthAnnouncerContractClient::new(&env, &contract_id); + let stealth_address = Address::generate(&env); + + client.announce(&scheme_id, &stealth_address, &bytes32(&env, &epk), &bytes(&env, &metadata)); + let event = env.events().all().last().unwrap(); + + let expected_topics: soroban_sdk::Vec = vec![ + &env, + symbol_short!("announce").into_val(&env), + scheme_id.into_val(&env), + stealth_address.into_val(&env), + ]; + prop_assert_eq!(event.1, expected_topics); + } + + #[test] + fn payload_round_trips_verbatim(scheme_id in any::(), epk in any::<[u8; 32]>(), metadata in prop::collection::vec(any::(), 0..128)) { + let env = env(); + let contract_id = env.register(StealthAnnouncerContract, ()); + let client = StealthAnnouncerContractClient::new(&env, &contract_id); + let stealth_address = Address::generate(&env); + let epk = bytes32(&env, &epk); + let metadata = bytes(&env, &metadata); + + client.announce(&scheme_id, &stealth_address, &epk, &metadata); + let event = env.events().all().last().unwrap(); + + let actual_value: (Address, BytesN<32>, Bytes) = + <(Address, BytesN<32>, Bytes)>::try_from_val(&env, &event.2).unwrap(); + prop_assert_eq!(actual_value, (contract_id, epk, metadata)); + } + + #[test] + fn repeated_announcements_publish_latest_call(scheme_id in any::(), next_scheme_id in any::(), epk in any::<[u8; 32]>()) { + let env = env(); + let contract_id = env.register(StealthAnnouncerContract, ()); + let client = StealthAnnouncerContractClient::new(&env, &contract_id); + let stealth_address = Address::generate(&env); + let epk = bytes32(&env, &epk); + let metadata = bytes(&env, &[7]); + + client.announce(&scheme_id, &stealth_address, &epk, &metadata); + client.announce(&next_scheme_id, &stealth_address, &epk, &metadata); + + let event = env.events().all().last().unwrap(); + let expected_topics: soroban_sdk::Vec = vec![ + &env, + symbol_short!("announce").into_val(&env), + next_scheme_id.into_val(&env), + stealth_address.into_val(&env), + ]; + prop_assert_eq!(event.1, expected_topics); + } + + #[test] + fn zero_length_metadata_is_valid(scheme_id in any::(), epk in any::<[u8; 32]>()) { + let env = env(); + let contract_id = env.register(StealthAnnouncerContract, ()); + let client = StealthAnnouncerContractClient::new(&env, &contract_id); + let stealth_address = Address::generate(&env); + + client.announce(&scheme_id, &stealth_address, &bytes32(&env, &epk), &Bytes::new(&env)); + + prop_assert_eq!(env.events().all().len(), 1); + } +} + +#[test] +fn default_property_case_count_is_at_least_1024() { + assert!(cases() >= 1024); +} diff --git a/stellar/stealth-registry/Cargo.toml b/stellar/stealth-registry/Cargo.toml index 014a09f..6d0eace 100644 --- a/stellar/stealth-registry/Cargo.toml +++ b/stellar/stealth-registry/Cargo.toml @@ -4,10 +4,11 @@ version = "0.1.0" edition = "2021" [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "rlib"] [dependencies] soroban-sdk = { workspace = true } [dev-dependencies] +proptest = "1.6.0" soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/stellar/stealth-registry/tests/properties.rs b/stellar/stealth-registry/tests/properties.rs new file mode 100644 index 0000000..5e41e49 --- /dev/null +++ b/stellar/stealth-registry/tests/properties.rs @@ -0,0 +1,122 @@ +use proptest::prelude::*; +use soroban_sdk::testutils::{Address as _, EnvTestConfig, Events}; +use soroban_sdk::{symbol_short, vec, Address, Bytes, Env, IntoVal, TryFromVal, Val}; +use stealth_registry::{RegistryError, StealthRegistryContract, StealthRegistryContractClient}; + +fn cases() -> u32 { + std::env::var("WRAITH_PROPTEST_CASES") + .ok() + .and_then(|value| value.parse().ok()) + .unwrap_or(1024) +} + +fn bytes(env: &Env, data: &[u8]) -> Bytes { + Bytes::from_slice(env, data) +} + +fn env() -> Env { + Env::new_with_config(EnvTestConfig { + capture_snapshot_at_drop: false, + }) +} + +fn meta_address(env: &Env, data: [u8; 64]) -> Bytes { + bytes(env, &data) +} + +proptest! { + #![proptest_config(ProptestConfig { cases: cases(), .. ProptestConfig::default() })] + #[test] + fn register_then_lookup_round_trips(scheme_id in any::(), meta in any::<[u8; 64]>()) { + let env = env(); + env.mock_all_auths(); + let contract_id = env.register(StealthRegistryContract, ()); + let client = StealthRegistryContractClient::new(&env, &contract_id); + let registrant = Address::generate(&env); + let meta = meta_address(&env, meta); + + client.register_keys(®istrant, &scheme_id, &meta); + + prop_assert_eq!(client.stealth_meta_address_of(®istrant, &scheme_id), meta); + } + + #[test] + fn updating_same_key_replaces_previous_value(scheme_id in any::(), first in any::<[u8; 64]>(), second in any::<[u8; 64]>()) { + let env = env(); + env.mock_all_auths(); + let contract_id = env.register(StealthRegistryContract, ()); + let client = StealthRegistryContractClient::new(&env, &contract_id); + let registrant = Address::generate(&env); + let first = meta_address(&env, first); + let second = meta_address(&env, second); + + client.register_keys(®istrant, &scheme_id, &first); + client.register_keys(®istrant, &scheme_id, &second); + + prop_assert_eq!(client.stealth_meta_address_of(®istrant, &scheme_id), second); + } + + #[test] + fn scheme_ids_are_independent(first_scheme in any::(), second_scheme in any::(), first in any::<[u8; 64]>(), second in any::<[u8; 64]>()) { + prop_assume!(first_scheme != second_scheme); + + let env = env(); + env.mock_all_auths(); + let contract_id = env.register(StealthRegistryContract, ()); + let client = StealthRegistryContractClient::new(&env, &contract_id); + let registrant = Address::generate(&env); + let first = meta_address(&env, first); + let second = meta_address(&env, second); + + client.register_keys(®istrant, &first_scheme, &first); + client.register_keys(®istrant, &second_scheme, &second); + + prop_assert_eq!(client.stealth_meta_address_of(®istrant, &first_scheme), first); + prop_assert_eq!(client.stealth_meta_address_of(®istrant, &second_scheme), second); + } + + #[test] + fn rejects_any_non_64_byte_meta_address(scheme_id in any::(), data in prop::collection::vec(any::(), 0..96)) { + prop_assume!(data.len() != 64); + + let env = env(); + env.mock_all_auths(); + let contract_id = env.register(StealthRegistryContract, ()); + let client = StealthRegistryContractClient::new(&env, &contract_id); + let registrant = Address::generate(&env); + + let result = client.try_register_keys(®istrant, &scheme_id, &bytes(&env, &data)); + + prop_assert_eq!(result, Err(Ok(RegistryError::InvalidMetaAddressLength))); + } + + #[test] + fn successful_register_emits_one_verbatim_event(scheme_id in any::(), meta in any::<[u8; 64]>()) { + let env = env(); + env.mock_all_auths(); + let contract_id = env.register(StealthRegistryContract, ()); + let client = StealthRegistryContractClient::new(&env, &contract_id); + let registrant = Address::generate(&env); + let meta = meta_address(&env, meta); + + client.register_keys(®istrant, &scheme_id, &meta); + + let events = env.events().all(); + prop_assert_eq!(events.len(), 1); + let event = events.last().unwrap(); + let expected_topics: soroban_sdk::Vec = vec![ + &env, + symbol_short!("register").into_val(&env), + registrant.into_val(&env), + scheme_id.into_val(&env), + ]; + prop_assert_eq!(event.1, expected_topics); + let actual_value = Bytes::try_from_val(&env, &event.2).unwrap(); + prop_assert_eq!(actual_value, meta); + } +} + +#[test] +fn default_property_case_count_is_at_least_1024() { + assert!(cases() >= 1024); +} diff --git a/stellar/stealth-sender/Cargo.toml b/stellar/stealth-sender/Cargo.toml index d7accd0..7480a49 100644 --- a/stellar/stealth-sender/Cargo.toml +++ b/stellar/stealth-sender/Cargo.toml @@ -4,10 +4,11 @@ version = "0.1.0" edition = "2021" [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "rlib"] [dependencies] soroban-sdk = { workspace = true } [dev-dependencies] +proptest = "1.6.0" soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/stellar/stealth-sender/tests/properties.rs b/stellar/stealth-sender/tests/properties.rs new file mode 100644 index 0000000..d04ffc3 --- /dev/null +++ b/stellar/stealth-sender/tests/properties.rs @@ -0,0 +1,212 @@ +use proptest::prelude::*; +use soroban_sdk::testutils::{Address as _, EnvTestConfig}; +use soroban_sdk::{contract, contractimpl, token, Address, Bytes, BytesN, Env, Vec}; +use stealth_sender::{SenderError, StealthSenderContract, StealthSenderContractClient}; + +#[contract] +pub struct AnnouncerMock; + +#[contractimpl] +impl AnnouncerMock { + pub fn announce( + env: Env, + scheme_id: u32, + stealth_address: Address, + ephemeral_pub_key: BytesN<32>, + metadata: Bytes, + ) { + let key = soroban_sdk::symbol_short!("count"); + let count: u32 = env.storage().instance().get(&key).unwrap_or(0); + env.storage().instance().set(&key, &(count + 1)); + env.events().publish( + ( + soroban_sdk::symbol_short!("announce"), + scheme_id, + stealth_address, + ), + (env.current_contract_address(), ephemeral_pub_key, metadata), + ); + } + + pub fn count(env: Env) -> u32 { + env.storage() + .instance() + .get(&soroban_sdk::symbol_short!("count")) + .unwrap_or(0) + } +} + +fn cases() -> u32 { + std::env::var("WRAITH_PROPTEST_CASES") + .ok() + .and_then(|value| value.parse().ok()) + .unwrap_or(1024) +} + +fn bytes(env: &Env, data: &[u8]) -> Bytes { + Bytes::from_slice(env, data) +} + +fn env() -> Env { + Env::new_with_config(EnvTestConfig { + capture_snapshot_at_drop: false, + }) +} + +fn bytes32(env: &Env, data: &[u8; 32]) -> BytesN<32> { + BytesN::from_array(env, data) +} + +struct Fixture { + env: Env, + contract_id: Address, + announcer: Address, + sender: Address, + token: Address, +} + +fn fixture(initial_balance: i128) -> Fixture { + let env = env(); + env.mock_all_auths(); + + let announcer = env.register(AnnouncerMock, ()); + let contract_id = env.register(StealthSenderContract, ()); + + let admin = Address::generate(&env); + let sender = Address::generate(&env); + let sac = env.register_stellar_asset_contract_v2(admin.clone()); + let token = sac.address(); + let asset_client = token::StellarAssetClient::new(&env, &token); + asset_client.mint(&sender, &initial_balance); + + Fixture { + env, + contract_id, + announcer, + sender, + token, + } +} + +proptest! { + #![proptest_config(ProptestConfig { cases: cases(), .. ProptestConfig::default() })] + #[test] + fn init_can_only_run_once(_seed in any::<[u8; 32]>()) { + let f = fixture(1_000_000); + let client = StealthSenderContractClient::new(&f.env, &f.contract_id); + client.init(&f.announcer); + + let result = client.try_init(&f.announcer); + + prop_assert_eq!(result, Err(Ok(SenderError::AlreadyInitialized))); + } + + #[test] + fn send_requires_initialization(amount in 1i128..1_000_000, epk in any::<[u8; 32]>(), metadata in prop::collection::vec(any::(), 0..64)) { + let f = fixture(1_000_000); + let client = StealthSenderContractClient::new(&f.env, &f.contract_id); + let stealth_address = Address::generate(&f.env); + + let result = client.try_send( + &f.sender, + &f.token, + &amount, + &1u32, + &stealth_address, + &bytes32(&f.env, &epk), + &bytes(&f.env, &metadata), + ); + + prop_assert_eq!(result, Err(Ok(SenderError::NotInitialized))); + } + + #[test] + fn send_transfers_exact_amount_and_announces(amount in 1i128..1_000_000, scheme_id in any::(), epk in any::<[u8; 32]>(), metadata in prop::collection::vec(any::(), 0..64)) { + let f = fixture(1_000_000); + let client = StealthSenderContractClient::new(&f.env, &f.contract_id); + let token_client = token::TokenClient::new(&f.env, &f.token); + let announcer_client = AnnouncerMockClient::new(&f.env, &f.announcer); + let stealth_address = Address::generate(&f.env); + client.init(&f.announcer); + + client.send( + &f.sender, + &f.token, + &amount, + &scheme_id, + &stealth_address, + &bytes32(&f.env, &epk), + &bytes(&f.env, &metadata), + ); + + prop_assert_eq!(token_client.balance(&stealth_address), amount); + prop_assert_eq!(token_client.balance(&f.sender), 1_000_000 - amount); + prop_assert_eq!(announcer_client.count(), 1); + } + + #[test] + fn batch_send_rejects_mismatched_lengths(amount in 1i128..1_000_000, epk in any::<[u8; 32]>()) { + let f = fixture(1_000_000); + let client = StealthSenderContractClient::new(&f.env, &f.contract_id); + let announcer_client = AnnouncerMockClient::new(&f.env, &f.announcer); + let stealth_address = Address::generate(&f.env); + client.init(&f.announcer); + + let mut addresses = Vec::new(&f.env); + addresses.push_back(stealth_address); + let keys = Vec::new(&f.env); + let mut metadatas = Vec::new(&f.env); + metadatas.push_back(bytes(&f.env, &[1])); + let mut amounts = Vec::new(&f.env); + amounts.push_back(amount); + + let result = client.try_batch_send( + &f.sender, + &f.token, + &1u32, + &addresses, + &keys, + &metadatas, + &amounts, + ); + + prop_assert_eq!(result, Err(Ok(SenderError::LengthMismatch))); + prop_assert_eq!(announcer_client.count(), 0); + let _ = epk; + } + + #[test] + fn batch_send_transfers_every_item_and_announces(count in 1u32..8, amount in 1i128..100_000, scheme_id in any::(), epk in any::<[u8; 32]>()) { + let total = amount * i128::from(count); + let f = fixture(total + 1_000); + let client = StealthSenderContractClient::new(&f.env, &f.contract_id); + let token_client = token::TokenClient::new(&f.env, &f.token); + let announcer_client = AnnouncerMockClient::new(&f.env, &f.announcer); + client.init(&f.announcer); + + let mut addresses = Vec::new(&f.env); + let mut keys = Vec::new(&f.env); + let mut metadatas = Vec::new(&f.env); + let mut amounts = Vec::new(&f.env); + + for i in 0..count { + addresses.push_back(Address::generate(&f.env)); + keys.push_back(bytes32(&f.env, &epk)); + metadatas.push_back(bytes(&f.env, &[i as u8])); + amounts.push_back(amount); + } + + client.batch_send(&f.sender, &f.token, &scheme_id, &addresses, &keys, &metadatas, &amounts); + + for i in 0..count { + prop_assert_eq!(token_client.balance(&addresses.get(i).unwrap()), amount); + } + prop_assert_eq!(token_client.balance(&f.sender), 1_000); + prop_assert_eq!(announcer_client.count(), count); + } +} + +#[test] +fn default_property_case_count_is_at_least_1024() { + assert!(cases() >= 1024); +} diff --git a/stellar/wraith-names/Cargo.toml b/stellar/wraith-names/Cargo.toml index 5acc329..14b55b8 100644 --- a/stellar/wraith-names/Cargo.toml +++ b/stellar/wraith-names/Cargo.toml @@ -4,10 +4,11 @@ version = "0.1.0" edition = "2021" [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "rlib"] [dependencies] soroban-sdk = { workspace = true } [dev-dependencies] +proptest = "1.6.0" soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/stellar/wraith-names/tests/properties.rs b/stellar/wraith-names/tests/properties.rs new file mode 100644 index 0000000..0622b98 --- /dev/null +++ b/stellar/wraith-names/tests/properties.rs @@ -0,0 +1,157 @@ +use proptest::prelude::*; +use soroban_sdk::testutils::{Address as _, EnvTestConfig, Events}; +use soroban_sdk::{Address, Bytes, Env, String}; +use wraith_names::{NamesError, WraithNamesContract, WraithNamesContractClient}; + +fn cases() -> u32 { + std::env::var("WRAITH_PROPTEST_CASES") + .ok() + .and_then(|value| value.parse().ok()) + .unwrap_or(1024) +} + +fn bytes(env: &Env, data: &[u8]) -> Bytes { + Bytes::from_slice(env, data) +} + +fn env() -> Env { + Env::new_with_config(EnvTestConfig { + capture_snapshot_at_drop: false, + }) +} + +fn name(env: &Env, value: &str) -> String { + String::from_str(env, value) +} + +fn valid_name_strategy() -> impl Strategy { + "[a-z0-9]{3,32}" +} + +fn invalid_name_strategy() -> impl Strategy { + prop_oneof![ + "[a-z0-9]{0,2}", + "[a-z0-9]{33,40}", + "[A-Z][a-z0-9]{2,10}", + "[a-z0-9]{1,8}[-_][a-z0-9]{1,8}", + ] +} + +proptest! { + #![proptest_config(ProptestConfig { cases: cases(), .. ProptestConfig::default() })] + #[test] + fn register_then_resolve_round_trips_valid_names(name_value in valid_name_strategy(), meta in any::<[u8; 64]>()) { + let env = env(); + env.mock_all_auths(); + let contract_id = env.register(WraithNamesContract, ()); + let client = WraithNamesContractClient::new(&env, &contract_id); + let owner = Address::generate(&env); + let name = name(&env, &name_value); + let meta = bytes(&env, &meta); + + client.register(&owner, &name, &meta); + + prop_assert_eq!(client.resolve(&name), meta); + } + + #[test] + fn name_of_round_trips_the_registered_name(name_value in valid_name_strategy(), meta in any::<[u8; 64]>()) { + let env = env(); + env.mock_all_auths(); + let contract_id = env.register(WraithNamesContract, ()); + let client = WraithNamesContractClient::new(&env, &contract_id); + let owner = Address::generate(&env); + let name = name(&env, &name_value); + let meta = bytes(&env, &meta); + + client.register(&owner, &name, &meta); + + prop_assert_eq!(client.name_of(&meta), name); + } + + #[test] + fn update_replaces_meta_address_for_owner(name_value in valid_name_strategy(), first in any::<[u8; 64]>(), second in any::<[u8; 64]>()) { + let env = env(); + env.mock_all_auths(); + let contract_id = env.register(WraithNamesContract, ()); + let client = WraithNamesContractClient::new(&env, &contract_id); + let owner = Address::generate(&env); + let name = name(&env, &name_value); + let first = bytes(&env, &first); + let second = bytes(&env, &second); + + client.register(&owner, &name, &first); + client.update(&owner, &name, &second); + + prop_assert_eq!(client.resolve(&name), second); + prop_assert_eq!(client.try_name_of(&first), Err(Ok(NamesError::NameNotFound))); + } + + #[test] + fn invalid_names_are_rejected(name_value in invalid_name_strategy(), meta in any::<[u8; 64]>()) { + let env = env(); + env.mock_all_auths(); + let contract_id = env.register(WraithNamesContract, ()); + let client = WraithNamesContractClient::new(&env, &contract_id); + let owner = Address::generate(&env); + let name = name(&env, &name_value); + let meta = bytes(&env, &meta); + + let result = client.try_register(&owner, &name, &meta); + + prop_assert!(result.is_err()); + } + + #[test] + fn non_64_byte_meta_addresses_are_rejected(name_value in valid_name_strategy(), data in prop::collection::vec(any::(), 0..96)) { + prop_assume!(data.len() != 64); + + let env = env(); + env.mock_all_auths(); + let contract_id = env.register(WraithNamesContract, ()); + let client = WraithNamesContractClient::new(&env, &contract_id); + let owner = Address::generate(&env); + let name = name(&env, &name_value); + + let result = client.try_register(&owner, &name, &bytes(&env, &data)); + + prop_assert_eq!(result, Err(Ok(NamesError::InvalidMetaAddress))); + } + + #[test] + fn release_removes_forward_and_reverse_lookup(name_value in valid_name_strategy(), meta in any::<[u8; 64]>()) { + let env = env(); + env.mock_all_auths(); + let contract_id = env.register(WraithNamesContract, ()); + let client = WraithNamesContractClient::new(&env, &contract_id); + let owner = Address::generate(&env); + let name = name(&env, &name_value); + let meta = bytes(&env, &meta); + + client.register(&owner, &name, &meta); + client.release(&owner, &name); + + prop_assert_eq!(client.try_resolve(&name), Err(Ok(NamesError::NameNotFound))); + prop_assert_eq!(client.try_name_of(&meta), Err(Ok(NamesError::NameNotFound))); + } + + #[test] + fn successful_register_emits_one_event(name_value in valid_name_strategy(), meta in any::<[u8; 64]>()) { + let env = env(); + env.mock_all_auths(); + let contract_id = env.register(WraithNamesContract, ()); + let client = WraithNamesContractClient::new(&env, &contract_id); + let owner = Address::generate(&env); + let name = name(&env, &name_value); + let meta = bytes(&env, &meta); + + client.register(&owner, &name, &meta); + + prop_assert_eq!(env.events().all().len(), 1); + } +} + +#[test] +fn default_property_case_count_is_at_least_1024() { + assert!(cases() >= 1024); +}