diff --git a/.github/workflows/chess.yml b/.github/workflows/chess.yml new file mode 100644 index 00000000..eb1819c8 --- /dev/null +++ b/.github/workflows/chess.yml @@ -0,0 +1,28 @@ +name: Chess Example CI + +on: + push: + branches: [main, develop] + paths: + - 'examples/chess/**' + - '!examples/chess/**/*.md' + - '.github/workflows/example_reusable.yml' + - '.github/workflows/chess.yml' + pull_request: + branches: [main, develop] + paths: + - 'examples/chess/**' + - '!examples/chess/**/*.md' + - '.github/workflows/example_reusable.yml' + - '.github/workflows/chess.yml' + +concurrency: + group: chess-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + ci: + uses: ./.github/workflows/example_reusable.yml + with: + job_name: Chess Example CI + working_directory: examples/chess diff --git a/examples/checkers/Cargo.lock b/examples/checkers/Cargo.lock new file mode 100644 index 00000000..149cba15 --- /dev/null +++ b/examples/checkers/Cargo.lock @@ -0,0 +1,1934 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[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 = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[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.118", +] + +[[package]] +name = "cc" +version = "1.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_eval" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "checkers" +version = "0.1.0" +dependencies = [ + "cougr-core", + "soroban-sdk", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cougr-core" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc489e355664841b5fb4d0b19e014c3be1a9b81cd1cd9f7ad29f02fe54a4135" +dependencies = [ + "soroban-sdk", + "wee_alloc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "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 = "ctor" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dtor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "escape-bytes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" + +[[package]] +name = "ethnum" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if 1.0.4", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[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 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if 1.0.4", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if 1.0.4", + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.118", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[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", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[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_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.8.22", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7192e3a5551a7aeee90d2110b11b615798e81951fd8c8293c87ea7f88b0168f5" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "soroban-env-common" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc49a80a68fc1005847308e63b9fce39874de731940b1807b721d472de3ff01" +dependencies = [ + "arbitrary", + "crate-git-revision", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr", + "wasmparser", +] + +[[package]] +name = "soroban-env-guest" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2334ba1cfe0a170ab744d96db0b4ca86934de9ff68187ceebc09dc342def55" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43af5d53c57bc2f546e122adc0b1cca6f93942c718977379aa19ddd04f06fcec" +dependencies = [ + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "curve25519-dalek", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "generic-array", + "getrandom", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "p256", + "rand", + "rand_chacha", + "sec1", + "sha2", + "sha3", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", + "static_assertions", + "stellar-strkey 0.0.13", + "wasmparser", +] + +[[package]] +name = "soroban-env-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a989167512e3592d455b1e204d703cfe578a36672a77ed2f9e6f7e1bbfd9cc5c" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr", + "syn 2.0.118", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ca06e6c5029d1285e66219cb387a234224e26969ce8ad2bc2d5017e9395d63b" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common", + "soroban-env-host", + "thiserror", +] + +[[package]] +name = "soroban-sdk" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4502f2e018f238a4c5d3212d7d20ea6abcdc6e58babd63b642b693739db30fd1" +dependencies = [ + "arbitrary", + "bytes-lit", + "crate-git-revision", + "ctor", + "derive_arbitrary", + "ed25519-dalek", + "rand", + "rustc_version", + "serde", + "serde_json", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey 0.0.16", + "visibility", +] + +[[package]] +name = "soroban-sdk-macros" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca03e9cf61d241cb9afdd6ddf41f6c25698b3f566a875e7009ea799b89e2bf0a" +dependencies = [ + "darling 0.20.11", + "heck", + "itertools", + "macro-string", + "proc-macro2", + "quote", + "sha2", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", + "syn 2.0.118", +] + +[[package]] +name = "soroban-spec" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa02e07f507cc27406ae0834db4dcf309b78c4cc8776eb3b2d662d66e8859d25" +dependencies = [ + "base64", + "sha2", + "stellar-xdr", + "thiserror", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6835bb510763ef3fa5405e89036e3c8ea6ef5abe55fc52cfe9ac0e38be9d531c" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2", + "soroban-spec", + "stellar-xdr", + "syn 2.0.118", + "thiserror", +] + +[[package]] +name = "soroban-wasmi" +version = "0.31.1-soroban.20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" +dependencies = [ + "smallvec", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[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.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1832fb50c651ad10f734aaf5d31ca5acdfb197a6ecda64d93fcdb8885af913" +dependencies = [ + "crate-git-revision", + "data-encoding", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084afcb0d458c3d5d5baa2d294b18f881e62cc258ef539d8fdf68be7dbe45520" +dependencies = [ + "crate-git-revision", + "data-encoding", + "heapless", +] + +[[package]] +name = "stellar-xdr" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d20dafed80076b227d4b17c0c508a4bbc4d5e4c3d4c1de7cd42242df4b1eaf" +dependencies = [ + "arbitrary", + "base64", + "cfg_eval", + "crate-git-revision", + "escape-bytes", + "ethnum", + "hex", + "serde", + "serde_with", + "sha2", + "stellar-strkey 0.0.13", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[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.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "time" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.118", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmi_arena" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "wasmparser" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" +dependencies = [ + "indexmap 2.14.0", + "semver", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[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-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/examples/checkers/README.md b/examples/checkers/README.md index 0e94a8f1..43b77ff8 100644 --- a/examples/checkers/README.md +++ b/examples/checkers/README.md @@ -1,105 +1,29 @@ -# Checkers — Cougr ECS Example +# Checkers -A fully on-chain two-player Checkers game implemented as a Soroban smart -contract. This example demonstrates how the Cougr Entity Component System -(ECS) model applies to a board game with non-trivial rule enforcement — -combining grid movement, mandatory captures, king promotion, and win -detection inside a single deterministic contract. +> **Transitional example**: This example uses an older Cougr pattern and is preserved +> for compatibility reference. For the current recommended approach, see `snake`. ---- +A fully on-chain two-player Checkers game implemented as a Soroban smart contract. -## Why Checkers? +## Purpose and pattern -Tic-Tac-Toe establishes the basics of turn management and a fixed board. -Checkers is the natural next step: +This example demonstrates a turn-based board game with non-trivial rule enforcement — +diagonal grid movement, mandatory captures, multi-hop chain captures, king promotion, +and win detection — implemented as plain Rust functions over `#[contracttype]` structs. +It showcases the simplest possible Cougr-adjacent pattern: component-shaped data types +with hand-written contract storage access, predating the `ComponentTrait`/`GameApp` +conventions used by newer examples. It is a useful reference for understanding what a +Soroban board-game contract looks like with no ECS framework involvement at all. -| Feature | Tic-Tac-Toe | Checkers | -|---|---|---| -| Fixed grid | ✓ | ✓ | -| Two-player turns | ✓ | ✓ | -| Piece movement rules | — | ✓ | -| Capture mechanics | — | ✓ | -| Forced-move enforcement | — | ✓ | -| Piece promotion | — | ✓ | -| Multi-hop chain captures | — | ✓ | -| Stalemate detection | — | ✓ | - ---- - -## Board Layout - -Standard 8×8 English Draughts. Only dark squares (where `row + col` is odd) -are ever occupied. - -``` - col 0 1 2 3 4 5 6 7 -row 0 [ ] [P1] [ ] [P1] [ ] [P1] [ ] [P1] -row 1 [P1] [ ] [P1] [ ] [P1] [ ] [P1] [ ] -row 2 [ ] [P1] [ ] [P1] [ ] [P1] [ ] [P1] -row 3 [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] -row 4 [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] -row 5 [P2] [ ] [P2] [ ] [P2] [ ] [P2] [ ] -row 6 [ ] [P2] [ ] [P2] [ ] [P2] [ ] [P2] -row 7 [P2] [ ] [P2] [ ] [P2] [ ] [P2] [ ] -``` - -**Piece encoding** (stored as `i32` in a flat 64-element vector): - -| Value | Meaning | -|---|---| -| `0` | Empty | -| `1` | Player One man | -| `2` | Player One king | -| `-1` | Player Two man | -| `-2` | Player Two king | - -Player One moves from row 0 toward row 7 (+1 row per step). -Player Two moves from row 7 toward row 0 (−1 row per step). -Kings may move diagonally in all four directions. - ---- - -## ECS Architecture - -### Components - -| Component | Fields | Purpose | -|---|---|---| -| `BoardComponent` | `cells: Vec` | 8×8 flat grid of piece values | -| `TurnComponent` | `current_player: u32`, `move_number: u32` | Whose turn it is and how many moves have been played | -| `GameStatusComponent` | `status: GameStatus`, `winner: u32` | Active / Finished and optional winner (1 or 2) | -| `ChainCapture` *(internal)* | `row: u32`, `col: u32` | Tracks the landing square during a multi-hop capture sequence | - -### Systems - -| System | Responsibility | -|---|---| -| `MoveValidationSystem` | Checks diagonal legality, occupancy, and bounds for both steps and jumps | -| `CaptureSystem` | Identifies the jumped piece, removes it, and detects further chain-capture options | -| `PromotionSystem` | Promotes a man to king when it reaches the opponent's back rank | -| `TurnSystem` | Advances the turn after a non-capturing move or when no further captures exist from the landing square; holds the turn during chain captures | -| `EndConditionSystem` | Declares a winner when one side has no pieces or no legal moves | - ---- - -## Contract API +## Public contract API -```rust -/// Initialise a new game. -fn init_game(env: Env, player_one: Address, player_two: Address) - -/// Submit a move from (from_row, from_col) to (to_row, to_col). -fn submit_move(env: Env, player: Address, from_row: u32, from_col: u32, to_row: u32, to_col: u32) - -/// Return the full game state snapshot. -fn get_state(env: Env) -> GameState - -/// Return the current board cells (64 values, row-major order). -fn get_board(env: Env) -> BoardState - -/// Return the Address of the player whose turn it currently is. -fn get_current_player(env: Env) -> Address -``` +| Function | Parameters | Returns | Description | +|---|---|---|---| +| `init_game` | `player_one: Address`, `player_two: Address` | `Result<(), CheckersError>` | Seeds the standard 8×8 opening position; Player One moves first. Returns `AlreadyInitialised` if called more than once. | +| `submit_move` | `player: Address`, `from_row: u32`, `from_col: u32`, `to_row: u32`, `to_col: u32` | `Result<(), CheckersError>` | Validates and applies a step or capture, resolves chain captures, promotes kings, and advances or holds the turn. Returns one of the `CheckersError` variants on any rule violation. | +| `get_state` | — | `Result` | Full snapshot: board, turn, status, and both player addresses. | +| `get_board` | — | `Result` | Raw 64-cell board array, row-major order. | +| `get_current_player` | — | `Result` | The `Address` of the player whose turn it currently is. | ### Error codes @@ -108,7 +32,7 @@ fn get_current_player(env: Env) -> Address | `AlreadyInitialised` | 1 | `init_game` called more than once | | `NotInitialised` | 2 | Any call before `init_game` | | `NotAPlayer` | 3 | Caller is not `player_one` or `player_two` | -| `WrongTurn` | 4 | Caller is the correct player but it is not their turn | +| `WrongTurn` | 4 | Caller is a registered player but it is not their turn | | `NotYourPiece` | 5 | Source square is empty or owned by the opponent | | `DestinationOccupied` | 6 | Target square is already occupied | | `IllegalMove` | 7 | Move is not a legal diagonal step or jump | @@ -118,206 +42,105 @@ fn get_current_player(env: Env) -> Address | `ChainCapturePieceMismatch` | 11 | During a chain capture the origin square was not the chain square | | `NotDarkSquare` | 12 | Destination square is a light square (row + col is even) | ---- - -## Rules Implemented - -1. **Diagonal movement only** — men move one square diagonally forward; kings - move one square diagonally in any direction. -2. **Captures (jumps)** — a piece jumps over an adjacent opponent piece into - the empty square beyond. The captured piece is removed immediately. -3. **Forced captures** — if any capture is available for the current player, - they *must* make a capture. A non-capture step is rejected with - `MustCapture`. -4. **Chain captures (multi-hop)** — after a capture, if the landing piece can - continue capturing, the same player must do so. The turn is held and the - active square is tracked via `ChainCapture`. The player may only move the - same piece until no further captures are available. -5. **Promotion** — a man reaching the opponent's back rank (row 7 for Player - One, row 0 for Player Two) is immediately promoted to a king. Promotion - happens before chain-capture detection, so a newly crowned king may - continue capturing if further opportunities exist. -6. **Win condition** — a player wins when the opponent has no pieces remaining - on the board, or when the opponent has no legal move (step or capture) - available on their turn. - ---- - -## Getting Started - -### Prerequisites - -- Rust 1.70 or newer -- `wasm32v1-none` target: `rustup target add wasm32v1-none` -- Stellar CLI: `cargo install --locked stellar-cli --features opt` +## Architecture overview -### Run the tests +There is no `GameApp` tick loop. `submit_move` runs a fixed validation/update pipeline +synchronously on each call: -```bash -cd examples/checkers -cargo test ``` - -### Check formatting and lints - -```bash -cargo fmt --check -cargo clippy --all-targets --all-features -- -D warnings +submit_move + └─ identify caller, check turn order, bounds, and dark-square rule + └─ MoveValidationSystem (systems.rs: legal_steps / legal_captures) + → confirms the requested move is a legal step or capture for this piece + └─ forced-capture check (systems.rs: any_capture_available) + → rejects a non-capture move if any capture exists for the player + └─ apply move + remove captured piece + └─ PromotionSystem (systems.rs: maybe_promote) + → crowns a man reaching the opponent's back rank + └─ chain-capture detection (systems.rs: legal_captures on the landing square) + → holds the turn and records the active square if another capture is available + └─ EndConditionSystem (systems.rs: check_winner) + → declares a winner on piece-count exhaustion or no legal moves + └─ TurnSystem + → advances current_player, or holds it during a chain capture ``` -### Build the Soroban WASM contract +`components.rs` holds the data shapes (`BoardComponent`, `TurnComponent`, +`GameStatusComponent`, `GameStatus`, the public `GameState`/`BoardState` view types, the +internal `ChainCapture` tracker, and the small `SmallVec4` fixed-capacity collection). +`systems.rs` holds the pure, storage-free game-logic functions (move/capture legality, +promotion, win detection, board initialisation). `lib.rs` is the only module that touches +`env.storage()`; it loads components, calls into `systems.rs`, and writes the results +back. -```bash -stellar contract build -``` +## Storage model -The compiled WASM artefact is written to: - -``` -target/wasm32v1-none/release/checkers.wasm -``` - -### Deploy to Testnet (optional) - -```bash -# Generate or reuse an identity -stellar keys generate --global alice --network - -# Deploy -stellar contract deploy \ - --wasm target/wasm32v1-none/release/checkers.wasm \ - --source alice \ - --network \ - --alias checkers_contract -``` - -### Invoke on Testnet - -```bash -# Initialise a game -stellar contract invoke \ - --id checkers_contract \ - --source alice \ - --network \ - -- init_game \ - --player_one \ - --player_two - -# Submit a move -stellar contract invoke \ - --id checkers_contract \ - --source alice \ - --network \ - -- submit_move \ - --player \ - --from_row 2 --from_col 1 \ - --to_row 3 --to_col 0 - -# Read the current board -stellar contract invoke \ - --id checkers_contract \ - --network \ - -- get_board -``` - ---- - -## Project Layout - -``` -examples/checkers/ -├── Cargo.toml # Package manifest and dependency pinning -├── README.md # This file -└── src/ - ├── lib.rs # Contract implementation (components + systems + API) - └── test.rs # Integration test suite -``` - ---- - -## Test Coverage - -| Scenario | Test | -|---|---| -| Standard starting position | `test_init_sets_standard_start_position` | -| Double-init rejection | `test_double_init_fails` | -| State before init | `test_get_state_before_init_fails` | -| Legal diagonal step | `test_legal_diagonal_step_advances_piece` | -| Turn advancement | `test_turn_advances_after_step` | -| Light-square rejection | `test_move_to_light_square_rejected` | -| Wrong-piece rejection | `test_move_wrong_piece_rejected` | -| Empty-square move rejection | `test_move_empty_square_rejected` | -| Horizontal move rejection | `test_horizontal_move_rejected` | -| Backward move (man) rejection | `test_backward_move_man_rejected` | -| Out-of-bounds rejection | `test_out_of_bounds_rejected` | -| Wrong-turn rejection | `test_wrong_turn_player_rejected` | -| Unknown address rejection | `test_unknown_address_rejected` | -| Capture execution + removal | `test_capture_removes_opponent_piece` | -| Forced-capture enforcement | `test_forced_capture_prevents_step` | -| King promotion at back rank | `test_man_promoted_to_king_at_back_rank` | -| Win detection (no pieces) | `test_win_when_opponent_has_no_pieces` | -| Move after game over | `test_move_after_game_over_rejected` | -| King backward movement | `test_king_can_move_backward` | -| Board size invariant | `test_get_board_returns_64_cells` | -| Current-player query | `test_get_current_player_switches_each_turn` | - ---- - -## Design Notes - -### Storage Keys - -Soroban persistent storage is keyed by `Symbol`. This contract uses five -top-level keys: +All state lives in **persistent storage**, under five top-level `Symbol` keys: | Key | Contents | |---|---| -| `BOARD` | `BoardComponent` (64 cells) | -| `TURN` | `TurnComponent` | -| `STATUS` | `GameStatusComponent` | +| `BOARD` | `BoardComponent` — 64-cell flat grid | +| `TURN` | `TurnComponent` — current player and move number | +| `STATUS` | `GameStatusComponent` — active/finished and winner | | `P1` | `Address` of Player One | | `P2` | `Address` of Player Two | -| `CHAIN` | `ChainCapture` (present only during a multi-hop sequence) | - -The `CHAIN` key is absent when no multi-hop is in progress. Its presence is -the signal to `TurnSystem` that the current turn is not yet complete. - -### `no_std` Compatibility - -The contract is compiled with `#![no_std]` as required by Soroban. All -collections use `soroban_sdk::Vec` rather than `std::vec::Vec`. Internal -helper logic that needs small fixed-size arrays uses a local `SmallVec4` -type backed by a stack-allocated `[T; 4]` array. - -### Capture Validation Strategy - -Capture legality is checked by `MoveValidationSystem` against the list -returned by `legal_captures`. This list is also used to: - -- determine whether a forced capture exists anywhere on the board - (`any_capture_available`), -- detect chain-capture continuation opportunities after a jump lands. +| `CHAIN` | `ChainCapture` — present only while a multi-hop capture sequence is in progress | + +Persistent storage (rather than instance storage) is used because each key is read and +rewritten independently inside `submit_move` — the board, turn, and status are not bundled +into one aggregate struct in this example, so per-key persistent entries map directly onto +that access pattern. The `CHAIN` key is removed as soon as a turn fully ends; its absence +is what tells `submit_move` that no multi-hop sequence is active, so it doubles as both +data and a lightweight state flag. + +## Main gameplay flow + +1. Deployer calls `init_game(player_one, player_two)`. The board is seeded with the + standard 12-piece-per-side opening position; Player One moves first. +2. Player One calls `submit_move` with a `(from_row, from_col)` → `(to_row, to_col)` pair. + The contract checks turn order, bounds, the dark-square rule, piece ownership, and + move geometry (diagonal step or jump). +3. If any capture is available for the mover anywhere on the board, a non-capture move is + rejected with `MustCapture` — captures are mandatory, not optional. +4. On a legal capture, the jumped piece is removed. If the landing square has a further + capture available, the turn is **held**: the same player must continue jumping with the + same piece (tracked via the `CHAIN` key) until no further capture exists. +5. A man reaching the opponent's back rank is promoted to a king immediately after the + move that lands it there, before chain-capture continuation is checked. +6. After each move, `EndConditionSystem` checks whether either side has no pieces left or + no legal move available; if so, `STATUS` flips to `Finished` with a recorded winner. +7. Players alternate `submit_move` calls until a winner is recorded; `get_state`, + `get_board`, and `get_current_player` provide read-only views at any point. + +## Cougr APIs used + +- `cougr_core::component::ComponentTrait` — gives each component (`BoardComponent`, + `TurnComponent`, `GameStatusComponent`) a `component_type()` symbol and byte-level + `serialize`/`deserialize`, chosen here because the game has a single fixed set of + components per instance rather than a dynamic entity population that would benefit from + `SimpleWorld`/`SimpleQueryBuilder` scanning. + +Per the API usage guidance in `EXAMPLE_STANDARD.md` §8, `GameApp`/`ScheduleStage`, +`SimpleWorld`, `auth`, and `privacy` APIs do not apply here: there is exactly one +fixed-shape game state per contract instance, one synchronous validation pipeline per +call, no session-key or multi-device flow, and no hidden information or proof submission. +This example is kept as a transitional reference precisely because it predates Cougr's +`GameApp` tick conventions — see `snake` for the current recommended approach. + +## Build and test commands -This avoids duplicating directional logic and keeps the validation surface -small and testable. - ---- - -## Relation to Other Examples - -| Example | What it adds over this one | -|---|---| -| `tic_tac_toe` | Simpler board, no movement or capture | -| `checkers` (this) | Grid movement, captures, forced rules, promotion, chains | -| `chess` | More piece types, complex movement geometry, check/checkmate | -| `battleship` | Hidden state, commit-reveal, coordinate bombing | - -Checkers occupies the middle of the complexity ladder: rich enough to -demonstrate real rule enforcement without the combinatorial complexity of -chess. - ---- +```bash +cargo test +stellar contract build +``` -## License +## Known limitations -MIT OR Apache-2.0 \ No newline at end of file +- Does not use `GameApp`, `ScheduleStage`, or `SimpleWorld` — game logic is invoked + directly from contract entrypoints rather than through a tick-based scheduler, since a + two-player, one-decision-per-call board game does not require staged scheduling or + entity queries. +- No timeout/forfeit mechanism for an unresponsive player. +- No draw detection (e.g., repetition or no-progress rules); a game can only end by piece + exhaustion or a player having no legal move. +- No spectator or replay API beyond the three read-only getters. diff --git a/examples/checkers/src/components.rs b/examples/checkers/src/components.rs new file mode 100644 index 00000000..5cb784d5 --- /dev/null +++ b/examples/checkers/src/components.rs @@ -0,0 +1,221 @@ +#![allow(dead_code)] + +use cougr_core::component::ComponentTrait; +use soroban_sdk::{contracterror, contracttype, symbol_short, Address, Bytes, Env, Symbol, Vec}; + +pub(crate) const BOARD_SIZE: u32 = 64; + +// Error type +#[contracterror] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[repr(u32)] +pub enum CheckersError { + /// Game has already been initialised. + AlreadyInitialised = 1, + /// Game has not been initialised yet. + NotInitialised = 2, + /// The caller is not a registered player. + NotAPlayer = 3, + /// It is not the caller's turn. + WrongTurn = 4, + /// The source cell is empty or owned by the opponent. + NotYourPiece = 5, + /// The destination cell is occupied. + DestinationOccupied = 6, + /// The move is not a legal diagonal step or jump. + IllegalMove = 7, + /// A capture is available and must be taken (forced-capture rule). + MustCapture = 8, + /// The game is already over. + GameOver = 9, + /// Row or column index is out of the 0–7 range. + OutOfBounds = 10, + /// During a chain capture the piece must continue from the landing square. + ChainCapturePieceMismatch = 11, + /// The destination must be a dark square (row + col odd). + NotDarkSquare = 12, +} + +// Component types +#[contracttype] +#[derive(Clone, Debug, PartialEq)] +pub struct BoardComponent { + pub cells: Vec, +} + +/// **TurnComponent** — whose turn it is and how many moves have been played. +#[contracttype] +#[derive(Clone, Debug, PartialEq)] +pub struct TurnComponent { + /// `1` = Player One, `2` = Player Two. + pub current_player: u32, + pub move_number: u32, +} + +/// **GameStatusComponent** — overall game lifecycle. +#[contracttype] +#[derive(Clone, Debug, PartialEq)] +pub struct GameStatusComponent { + pub status: GameStatus, + /// `0` = no winner yet, `1` = Player One won, `2` = Player Two won. + pub winner: u32, +} + +/// Lifecycle state of the game. +#[contracttype] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum GameStatus { + Active, + Finished, +} +// Public view types returned by the contract API +/// Full game state snapshot returned by `get_state`. +#[contracttype] +#[derive(Clone, Debug, PartialEq)] +pub struct GameState { + pub board: BoardComponent, + pub turn: TurnComponent, + pub status: GameStatusComponent, + pub player_one: Address, + pub player_two: Address, +} + +/// Board snapshot returned by `get_board`. +#[contracttype] +#[derive(Clone, Debug, PartialEq)] +pub struct BoardState { + pub cells: Vec, +} +// ── ComponentTrait implementations ─────────────────────────────────────────── + +impl ComponentTrait for BoardComponent { + fn component_type() -> Symbol { + symbol_short!("boardcomp") + } + + fn serialize(&self, env: &Env) -> Bytes { + let mut bytes = Bytes::new(env); + for i in 0..BOARD_SIZE { + let cell = self.cells.get(i).unwrap_or(0); + bytes.append(&Bytes::from_array(env, &cell.to_be_bytes())); + } + bytes + } + + fn deserialize(env: &Env, data: &Bytes) -> Option { + if data.len() != BOARD_SIZE * 4 { + return None; + } + let mut cells = Vec::new(env); + for i in 0..BOARD_SIZE { + let offset = i * 4; + let cell = i32::from_be_bytes([ + data.get(offset)?, + data.get(offset + 1)?, + data.get(offset + 2)?, + data.get(offset + 3)?, + ]); + cells.push_back(cell); + } + Some(Self { cells }) + } +} + +impl ComponentTrait for TurnComponent { + fn component_type() -> Symbol { + symbol_short!("turncomp") + } + + fn serialize(&self, env: &Env) -> Bytes { + let mut bytes = Bytes::new(env); + bytes.append(&Bytes::from_array(env, &self.current_player.to_be_bytes())); + bytes.append(&Bytes::from_array(env, &self.move_number.to_be_bytes())); + bytes + } + + fn deserialize(_env: &Env, data: &Bytes) -> Option { + if data.len() != 8 { + return None; + } + let current_player = + u32::from_be_bytes([data.get(0)?, data.get(1)?, data.get(2)?, data.get(3)?]); + let move_number = + u32::from_be_bytes([data.get(4)?, data.get(5)?, data.get(6)?, data.get(7)?]); + Some(Self { + current_player, + move_number, + }) + } +} + +impl ComponentTrait for GameStatusComponent { + fn component_type() -> Symbol { + symbol_short!("gamestatu") + } + + fn serialize(&self, env: &Env) -> Bytes { + let status_tag = match self.status { + GameStatus::Active => 0u32, + GameStatus::Finished => 1u32, + }; + let mut bytes = Bytes::new(env); + bytes.append(&Bytes::from_array(env, &status_tag.to_be_bytes())); + bytes.append(&Bytes::from_array(env, &self.winner.to_be_bytes())); + bytes + } + + fn deserialize(_env: &Env, data: &Bytes) -> Option { + if data.len() != 8 { + return None; + } + let status_tag = + u32::from_be_bytes([data.get(0)?, data.get(1)?, data.get(2)?, data.get(3)?]); + let winner = u32::from_be_bytes([data.get(4)?, data.get(5)?, data.get(6)?, data.get(7)?]); + let status = match status_tag { + 0 => GameStatus::Active, + 1 => GameStatus::Finished, + _ => return None, + }; + Some(Self { status, winner }) + } +} + +// Internal: chain-capture tracking stored in persistent state + +#[contracttype] +#[derive(Clone)] +pub(crate) struct ChainCapture { + pub row: u32, + pub col: u32, +} + +// Internal: SmallVec4 — tiny fixed-capacity stack-allocated collection +#[derive(Copy, Clone)] +pub(crate) struct SmallVec4 { + data: [T; 4], + len: usize, +} + +impl SmallVec4 { + pub(crate) fn new() -> Self { + Self { + data: [T::default(); 4], + len: 0, + } + } + + pub(crate) fn push(&mut self, v: T) { + if self.len < 4 { + self.data[self.len] = v; + self.len += 1; + } + } + + pub(crate) fn is_empty(&self) -> bool { + self.len == 0 + } + + pub(crate) fn as_slice(&self) -> &[T] { + &self.data[..self.len] + } +} diff --git a/examples/checkers/src/lib.rs b/examples/checkers/src/lib.rs index 0dca6cff..db1ba16a 100644 --- a/examples/checkers/src/lib.rs +++ b/examples/checkers/src/lib.rs @@ -1,339 +1,20 @@ #![no_std] -use soroban_sdk::{ - contract, contracterror, contractimpl, contracttype, symbol_short, Address, Env, Vec, -}; - -// Error type -#[contracterror] -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -#[repr(u32)] -pub enum CheckersError { - /// Game has already been initialised. - AlreadyInitialised = 1, - /// Game has not been initialised yet. - NotInitialised = 2, - /// The caller is not a registered player. - NotAPlayer = 3, - /// It is not the caller's turn. - WrongTurn = 4, - /// The source cell is empty or owned by the opponent. - NotYourPiece = 5, - /// The destination cell is occupied. - DestinationOccupied = 6, - /// The move is not a legal diagonal step or jump. - IllegalMove = 7, - /// A capture is available and must be taken (forced-capture rule). - MustCapture = 8, - /// The game is already over. - GameOver = 9, - /// Row or column index is out of the 0–7 range. - OutOfBounds = 10, - /// During a chain capture the piece must continue from the landing square. - ChainCapturePieceMismatch = 11, - /// The destination must be a dark square (row + col odd). - NotDarkSquare = 12, -} - -// Component types -#[contracttype] -#[derive(Clone, Debug, PartialEq)] -pub struct BoardComponent { - pub cells: Vec, -} - -/// **TurnComponent** — whose turn it is and how many moves have been played. -#[contracttype] -#[derive(Clone, Debug, PartialEq)] -pub struct TurnComponent { - /// `1` = Player One, `2` = Player Two. - pub current_player: u32, - pub move_number: u32, -} - -/// **GameStatusComponent** — overall game lifecycle. -#[contracttype] -#[derive(Clone, Debug, PartialEq)] -pub struct GameStatusComponent { - pub status: GameStatus, - /// `0` = no winner yet, `1` = Player One won, `2` = Player Two won. - pub winner: u32, -} - -/// Lifecycle state of the game. -#[contracttype] -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum GameStatus { - Active, - Finished, -} -// Public view types returned by the contract API -/// Full game state snapshot returned by `get_state`. -#[contracttype] -#[derive(Clone, Debug, PartialEq)] -pub struct GameState { - pub board: BoardComponent, - pub turn: TurnComponent, - pub status: GameStatusComponent, - pub player_one: Address, - pub player_two: Address, -} - -/// Board snapshot returned by `get_board`. -#[contracttype] -#[derive(Clone, Debug, PartialEq)] -pub struct BoardState { - pub cells: Vec, -} -// Internal: chain-capture tracking stored in persistent state - -#[contracttype] -#[derive(Clone)] -struct ChainCapture { - pub row: u32, - pub col: u32, -} - -// Internal: SmallVec4 — tiny fixed-capacity stack-allocated collection -#[derive(Copy, Clone)] -struct SmallVec4 { - data: [T; 4], - len: usize, -} - -impl SmallVec4 { - fn new() -> Self { - Self { - data: [T::default(); 4], - len: 0, - } - } - - fn push(&mut self, v: T) { - if self.len < 4 { - self.data[self.len] = v; - self.len += 1; - } - } - - fn is_empty(&self) -> bool { - self.len == 0 - } - - fn as_slice(&self) -> &[T] { - &self.data[..self.len] - } -} - -// Internal: board cell access -#[inline] -fn board_get(cells: &Vec, row: u32, col: u32) -> i32 { - cells.get(row * 8 + col).unwrap_or(0) -} - -#[inline] -fn board_set(cells: &mut Vec, row: u32, col: u32, val: i32) { - cells.set(row * 8 + col, val); -} -// Internal: piece helpers -/// `true` when `piece` belongs to `player` (1 or 2). -#[inline] -fn owned_by(piece: i32, player: u32) -> bool { - match player { - 1 => piece == 1 || piece == 2, - 2 => piece == -1 || piece == -2, - _ => false, - } -} - -/// `true` when `piece` is a king (moves in all four diagonal directions). -#[inline] -fn is_king(piece: i32) -> bool { - piece == 2 || piece == -2 -} - -#[inline] -fn forward_delta(player: u32) -> i32 { - if player == 1 { - 1 - } else { - -1 - } -} -// System: MoveValidationSystem -fn legal_steps(cells: &Vec, row: u32, col: u32, player: u32) -> SmallVec4<(u32, u32)> { - let piece = board_get(cells, row, col); - let fd = forward_delta(player); - - let mut deltas: [(i32, i32); 4] = [(0, 0); 4]; - deltas[0] = (fd, 1); - deltas[1] = (fd, -1); - let count = if is_king(piece) { - deltas[2] = (-fd, 1); - deltas[3] = (-fd, -1); - 4 - } else { - 2 - }; - - let mut out = SmallVec4::<(u32, u32)>::new(); - for &(dr, dc) in deltas.iter().take(count) { - let nr = row as i32 + dr; - let nc = col as i32 + dc; - if (0..8).contains(&nr) && (0..8).contains(&nc) { - let (nr, nc) = (nr as u32, nc as u32); - if board_get(cells, nr, nc) == 0 { - out.push((nr, nc)); - } - } - } - out -} - -/// Returns all legal capture destinations from `(row, col)` for `player`. -fn legal_captures( - cells: &Vec, - row: u32, - col: u32, - player: u32, -) -> SmallVec4<(u32, u32, u32, u32)> { - let piece = board_get(cells, row, col); - let fd = forward_delta(player); - - let mut deltas: [(i32, i32); 4] = [(0, 0); 4]; - deltas[0] = (fd, 1); - deltas[1] = (fd, -1); - let count = if is_king(piece) { - deltas[2] = (-fd, 1); - deltas[3] = (-fd, -1); - 4 - } else { - 2 - }; - - let mut out = SmallVec4::<(u32, u32, u32, u32)>::new(); - for &(dr, dc) in deltas.iter().take(count) { - let mr = row as i32 + dr; - let mc = col as i32 + dc; - let lr = row as i32 + 2 * dr; - let lc = col as i32 + 2 * dc; - - // Bounds-check both squares. - if !(0..8).contains(&mr) || !(0..8).contains(&mc) { - continue; - } - if !(0..8).contains(&lr) || !(0..8).contains(&lc) { - continue; - } - - let (mr, mc) = (mr as u32, mc as u32); - let (lr, lc) = (lr as u32, lc as u32); - - let mid = board_get(cells, mr, mc); - let land = board_get(cells, lr, lc); - - // Middle must be an opponent piece; landing must be empty. - if owned_by(mid, 3 - player) && land == 0 { - out.push((lr, lc, mr, mc)); - } - } - out -} - -/// `true` if `player` has at least one capture available anywhere on the board. -fn any_capture_available(cells: &Vec, player: u32) -> bool { - for row in 0u32..8 { - for col in 0u32..8 { - if owned_by(board_get(cells, row, col), player) - && !legal_captures(cells, row, col, player).is_empty() - { - return true; - } - } - } - false -} - -/// `true` if `player` has at least one legal move (step or capture) anywhere. -fn any_legal_move(cells: &Vec, player: u32) -> bool { - for row in 0u32..8 { - for col in 0u32..8 { - if owned_by(board_get(cells, row, col), player) { - if !legal_captures(cells, row, col, player).is_empty() { - return true; - } - if !legal_steps(cells, row, col, player).is_empty() { - return true; - } - } - } - } - false -} - -// System: PromotionSystem -fn maybe_promote(cells: &mut Vec, row: u32, col: u32, player: u32) { - let promotion_row = if player == 1 { 7u32 } else { 0u32 }; - if row == promotion_row { - let piece = board_get(cells, row, col); - if !is_king(piece) { - let king_val = if player == 1 { 2i32 } else { -2i32 }; - board_set(cells, row, col, king_val); - } - } -} -// System: EndConditionSystem -/// Returns the winning player number (1 or 2), or 0 if the game continues. -fn check_winner(cells: &Vec) -> u32 { - let mut p1_pieces = 0u32; - let mut p2_pieces = 0u32; - for row in 0u32..8 { - for col in 0u32..8 { - let v = board_get(cells, row, col); - if v == 1 || v == 2 { - p1_pieces += 1; - } else if v == -1 || v == -2 { - p2_pieces += 1; - } - } - } - - // A player with no pieces loses immediately. - if p2_pieces == 0 { - return 1; - } - if p1_pieces == 0 { - return 2; - } - - if !any_legal_move(cells, 1) { - return 2; - } - if !any_legal_move(cells, 2) { - return 1; - } +mod components; +mod systems; +#[cfg(test)] +mod test; - 0 // Game still active. -} +use components::{ + BoardComponent, BoardState, ChainCapture, CheckersError, GameState, GameStatus, + GameStatusComponent, TurnComponent, +}; +use systems::{ + any_capture_available, board_get, board_set, check_winner, initial_board, legal_captures, + legal_steps, maybe_promote, owned_by, +}; -// Board initialisation helper -fn initial_board(env: &Env) -> Vec { - let mut cells: Vec = Vec::new(env); - for row in 0u32..8 { - for col in 0u32..8 { - let dark = (row + col) % 2 == 1; - let val: i32 = if dark && row <= 2 { - 1 - } else if dark && row >= 5 { - -1 - } else { - 0 - }; - cells.push_back(val); - } - } - cells -} +use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env}; // Contract #[contract] @@ -651,7 +332,3 @@ impl CheckersContract { Ok(addr) } } - -// Tests -#[cfg(test)] -mod test; diff --git a/examples/checkers/src/systems.rs b/examples/checkers/src/systems.rs new file mode 100644 index 00000000..347dd5b5 --- /dev/null +++ b/examples/checkers/src/systems.rs @@ -0,0 +1,218 @@ +use soroban_sdk::{Env, Vec}; + +use crate::components::SmallVec4; + +// Internal: board cell access +#[inline] +pub(crate) fn board_get(cells: &Vec, row: u32, col: u32) -> i32 { + cells.get(row * 8 + col).unwrap_or(0) +} + +#[inline] +pub(crate) fn board_set(cells: &mut Vec, row: u32, col: u32, val: i32) { + cells.set(row * 8 + col, val); +} +// Internal: piece helpers +/// `true` when `piece` belongs to `player` (1 or 2). +#[inline] +pub(crate) fn owned_by(piece: i32, player: u32) -> bool { + match player { + 1 => piece == 1 || piece == 2, + 2 => piece == -1 || piece == -2, + _ => false, + } +} + +/// `true` when `piece` is a king (moves in all four diagonal directions). +#[inline] +pub(crate) fn is_king(piece: i32) -> bool { + piece == 2 || piece == -2 +} + +#[inline] +pub(crate) fn forward_delta(player: u32) -> i32 { + if player == 1 { + 1 + } else { + -1 + } +} +// System: MoveValidationSystem +pub(crate) fn legal_steps( + cells: &Vec, + row: u32, + col: u32, + player: u32, +) -> SmallVec4<(u32, u32)> { + let piece = board_get(cells, row, col); + let fd = forward_delta(player); + + let mut deltas: [(i32, i32); 4] = [(0, 0); 4]; + deltas[0] = (fd, 1); + deltas[1] = (fd, -1); + let count = if is_king(piece) { + deltas[2] = (-fd, 1); + deltas[3] = (-fd, -1); + 4 + } else { + 2 + }; + + let mut out = SmallVec4::<(u32, u32)>::new(); + for &(dr, dc) in deltas.iter().take(count) { + let nr = row as i32 + dr; + let nc = col as i32 + dc; + if (0..8).contains(&nr) && (0..8).contains(&nc) { + let (nr, nc) = (nr as u32, nc as u32); + if board_get(cells, nr, nc) == 0 { + out.push((nr, nc)); + } + } + } + out +} + +/// Returns all legal capture destinations from `(row, col)` for `player`. +pub(crate) fn legal_captures( + cells: &Vec, + row: u32, + col: u32, + player: u32, +) -> SmallVec4<(u32, u32, u32, u32)> { + let piece = board_get(cells, row, col); + let fd = forward_delta(player); + + let mut deltas: [(i32, i32); 4] = [(0, 0); 4]; + deltas[0] = (fd, 1); + deltas[1] = (fd, -1); + let count = if is_king(piece) { + deltas[2] = (-fd, 1); + deltas[3] = (-fd, -1); + 4 + } else { + 2 + }; + + let mut out = SmallVec4::<(u32, u32, u32, u32)>::new(); + for &(dr, dc) in deltas.iter().take(count) { + let mr = row as i32 + dr; + let mc = col as i32 + dc; + let lr = row as i32 + 2 * dr; + let lc = col as i32 + 2 * dc; + + // Bounds-check both squares. + if !(0..8).contains(&mr) || !(0..8).contains(&mc) { + continue; + } + if !(0..8).contains(&lr) || !(0..8).contains(&lc) { + continue; + } + + let (mr, mc) = (mr as u32, mc as u32); + let (lr, lc) = (lr as u32, lc as u32); + + let mid = board_get(cells, mr, mc); + let land = board_get(cells, lr, lc); + + // Middle must be an opponent piece; landing must be empty. + if owned_by(mid, 3 - player) && land == 0 { + out.push((lr, lc, mr, mc)); + } + } + out +} + +/// `true` if `player` has at least one capture available anywhere on the board. +pub(crate) fn any_capture_available(cells: &Vec, player: u32) -> bool { + for row in 0u32..8 { + for col in 0u32..8 { + if owned_by(board_get(cells, row, col), player) + && !legal_captures(cells, row, col, player).is_empty() + { + return true; + } + } + } + false +} + +/// `true` if `player` has at least one legal move (step or capture) anywhere. +pub(crate) fn any_legal_move(cells: &Vec, player: u32) -> bool { + for row in 0u32..8 { + for col in 0u32..8 { + if owned_by(board_get(cells, row, col), player) { + if !legal_captures(cells, row, col, player).is_empty() { + return true; + } + if !legal_steps(cells, row, col, player).is_empty() { + return true; + } + } + } + } + false +} + +// System: PromotionSystem +pub(crate) fn maybe_promote(cells: &mut Vec, row: u32, col: u32, player: u32) { + let promotion_row = if player == 1 { 7u32 } else { 0u32 }; + if row == promotion_row { + let piece = board_get(cells, row, col); + if !is_king(piece) { + let king_val = if player == 1 { 2i32 } else { -2i32 }; + board_set(cells, row, col, king_val); + } + } +} +// System: EndConditionSystem +/// Returns the winning player number (1 or 2), or 0 if the game continues. +pub(crate) fn check_winner(cells: &Vec) -> u32 { + let mut p1_pieces = 0u32; + let mut p2_pieces = 0u32; + for row in 0u32..8 { + for col in 0u32..8 { + let v = board_get(cells, row, col); + if v == 1 || v == 2 { + p1_pieces += 1; + } else if v == -1 || v == -2 { + p2_pieces += 1; + } + } + } + + // A player with no pieces loses immediately. + if p2_pieces == 0 { + return 1; + } + if p1_pieces == 0 { + return 2; + } + + if !any_legal_move(cells, 1) { + return 2; + } + if !any_legal_move(cells, 2) { + return 1; + } + + 0 // Game still active. +} + +// Board initialisation helper +pub(crate) fn initial_board(env: &Env) -> Vec { + let mut cells: Vec = Vec::new(env); + for row in 0u32..8 { + for col in 0u32..8 { + let dark = (row + col) % 2 == 1; + let val: i32 = if dark && row <= 2 { + 1 + } else if dark && row >= 5 { + -1 + } else { + 0 + }; + cells.push_back(val); + } + } + cells +} diff --git a/examples/chess/.github/workflows/chess.yml b/examples/chess/.github/workflows/chess.yml deleted file mode 100644 index a46c4751..00000000 --- a/examples/chess/.github/workflows/chess.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: Chess Contract CI - -on: - push: - branches: [ main ] - paths: - - 'examples/chess/**' - pull_request: - branches: [ main ] - paths: - - 'examples/chess/**' - -env: - CARGO_TERM_COLOR: always - -jobs: - test: - name: Test Chess Contract - runs-on: ubuntu-latest - defaults: - run: - working-directory: examples/chess - - steps: - - uses: actions/checkout@v4 - - - name: Install Rust - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: stable - components: rustfmt, clippy - - - name: Cache cargo registry - uses: actions/cache@v4 - with: - path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo index - uses: actions/cache@v4 - with: - path: ~/.cargo/git - key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo build - uses: actions/cache@v4 - with: - path: examples/chess/target - key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - - name: Check formatting - run: cargo fmt -- --check - - - name: Run clippy - run: cargo clippy -- -D warnings - - - name: Run tests - run: cargo test --verbose - - - name: Build contract - run: cargo build --release --target wasm32-unknown-unknown - - build: - name: Build WASM - runs-on: ubuntu-latest - defaults: - run: - working-directory: examples/chess - - steps: - - uses: actions/checkout@v4 - - - name: Install Rust - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: stable - target: wasm32-unknown-unknown - - - name: Install Stellar CLI - run: | - cargo install --locked stellar-cli --features opt - - - name: Build optimized WASM - run: stellar contract build - - - name: Check WASM size - run: | - WASM_SIZE=$(wc -c < target/wasm32-unknown-unknown/release/chess.wasm) - echo "WASM size: $WASM_SIZE bytes" - if [ $WASM_SIZE -gt 1048576 ]; then - echo "Warning: WASM size exceeds 1MB" - fi - - - name: Upload WASM artifact - uses: actions/upload-artifact@v4 - with: - name: chess-contract-wasm - path: examples/chess/target/wasm32-unknown-unknown/release/chess.wasm - retention-days: 7 diff --git a/examples/chess/Cargo.lock b/examples/chess/Cargo.lock new file mode 100644 index 00000000..20e9459f --- /dev/null +++ b/examples/chess/Cargo.lock @@ -0,0 +1,1934 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[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 = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[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.118", +] + +[[package]] +name = "cc" +version = "1.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_eval" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "chess" +version = "0.1.0" +dependencies = [ + "cougr-core", + "soroban-sdk", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cougr-core" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc489e355664841b5fb4d0b19e014c3be1a9b81cd1cd9f7ad29f02fe54a4135" +dependencies = [ + "soroban-sdk", + "wee_alloc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "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 = "ctor" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dtor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "escape-bytes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" + +[[package]] +name = "ethnum" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if 1.0.4", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[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 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if 1.0.4", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if 1.0.4", + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.118", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[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", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[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_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.8.22", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7192e3a5551a7aeee90d2110b11b615798e81951fd8c8293c87ea7f88b0168f5" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "soroban-env-common" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc49a80a68fc1005847308e63b9fce39874de731940b1807b721d472de3ff01" +dependencies = [ + "arbitrary", + "crate-git-revision", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr", + "wasmparser", +] + +[[package]] +name = "soroban-env-guest" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2334ba1cfe0a170ab744d96db0b4ca86934de9ff68187ceebc09dc342def55" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43af5d53c57bc2f546e122adc0b1cca6f93942c718977379aa19ddd04f06fcec" +dependencies = [ + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "curve25519-dalek", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "generic-array", + "getrandom", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "p256", + "rand", + "rand_chacha", + "sec1", + "sha2", + "sha3", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", + "static_assertions", + "stellar-strkey 0.0.13", + "wasmparser", +] + +[[package]] +name = "soroban-env-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a989167512e3592d455b1e204d703cfe578a36672a77ed2f9e6f7e1bbfd9cc5c" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr", + "syn 2.0.118", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ca06e6c5029d1285e66219cb387a234224e26969ce8ad2bc2d5017e9395d63b" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common", + "soroban-env-host", + "thiserror", +] + +[[package]] +name = "soroban-sdk" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4502f2e018f238a4c5d3212d7d20ea6abcdc6e58babd63b642b693739db30fd1" +dependencies = [ + "arbitrary", + "bytes-lit", + "crate-git-revision", + "ctor", + "derive_arbitrary", + "ed25519-dalek", + "rand", + "rustc_version", + "serde", + "serde_json", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey 0.0.16", + "visibility", +] + +[[package]] +name = "soroban-sdk-macros" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca03e9cf61d241cb9afdd6ddf41f6c25698b3f566a875e7009ea799b89e2bf0a" +dependencies = [ + "darling 0.20.11", + "heck", + "itertools", + "macro-string", + "proc-macro2", + "quote", + "sha2", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", + "syn 2.0.118", +] + +[[package]] +name = "soroban-spec" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa02e07f507cc27406ae0834db4dcf309b78c4cc8776eb3b2d662d66e8859d25" +dependencies = [ + "base64", + "sha2", + "stellar-xdr", + "thiserror", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6835bb510763ef3fa5405e89036e3c8ea6ef5abe55fc52cfe9ac0e38be9d531c" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2", + "soroban-spec", + "stellar-xdr", + "syn 2.0.118", + "thiserror", +] + +[[package]] +name = "soroban-wasmi" +version = "0.31.1-soroban.20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" +dependencies = [ + "smallvec", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[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.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1832fb50c651ad10f734aaf5d31ca5acdfb197a6ecda64d93fcdb8885af913" +dependencies = [ + "crate-git-revision", + "data-encoding", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084afcb0d458c3d5d5baa2d294b18f881e62cc258ef539d8fdf68be7dbe45520" +dependencies = [ + "crate-git-revision", + "data-encoding", + "heapless", +] + +[[package]] +name = "stellar-xdr" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d20dafed80076b227d4b17c0c508a4bbc4d5e4c3d4c1de7cd42242df4b1eaf" +dependencies = [ + "arbitrary", + "base64", + "cfg_eval", + "crate-git-revision", + "escape-bytes", + "ethnum", + "hex", + "serde", + "serde_with", + "sha2", + "stellar-strkey 0.0.13", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[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.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "time" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.118", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmi_arena" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "wasmparser" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" +dependencies = [ + "indexmap 2.14.0", + "semver", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[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-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/examples/chess/README.md b/examples/chess/README.md index dfe06cd6..a1d61994 100644 --- a/examples/chess/README.md +++ b/examples/chess/README.md @@ -1,366 +1,122 @@ # Verifiable Chess with ZK Move Validation -A simplified chess implementation on Stellar Soroban that demonstrates **zero-knowledge proof verification** for move legality using the **Cougr-Core** ZK framework. Move validation happens off-chain via circuits, and only compact Groth16 proofs are verified on-chain. +> **Transitional example**: This example uses an older Cougr pattern and is preserved +> for compatibility reference. For the current recommended approach to ZK-backed game +> logic specifically, see `hidden_hand`, which uses `cougr_core::circuits`' pre-built +> circuit builders instead of constructing a `CustomCircuit` by hand. -## Why ZK Proofs for Chess? +A simplified chess implementation on Stellar Soroban that demonstrates **zero-knowledge proof +verification** for move legality using the [Cougr](../../README.md) ZK framework. Move +legality is checked off-chain by a circuit; the contract only verifies a compact Groth16 +proof and applies the move. -Traditional on-chain chess requires the contract to validate every move rule: -- Pawn forward movement and capture diagonals -- Knight L-shaped moves -- Bishop diagonal paths -- Rook straight paths -- Queen combined movement -- King single-step movement -- Check and checkmate detection -- Castling, en passant, promotion rules +## Purpose and pattern -**This is expensive.** Each move requires complex on-chain computation that scales with rule complexity. +This example demonstrates how to keep move-validation logic entirely off-chain while still +enforcing it trustlessly: instead of the contract checking piece-movement rules, path +obstruction, and check/checkmate conditions on every move, the player generates a Groth16 +proof off-chain that the move is legal given the current board state, and the contract +verifies that proof in constant time. It showcases `cougr_core::privacy::experimental`'s +generic `CustomCircuit` builder for binding a proof's public inputs (`state_hash`, `from`, +`to`) to a verification key, alongside the same `ComponentTrait` pattern used by the other +ECS-lite examples for typed, byte-serializable game state. -### The ZK Approach - -| Aspect | Traditional On-Chain | ZK Proof Verification | -|--------|---------------------|----------------------| -| **Move Validation** | Contract checks all rules | Off-chain circuit proves legality | -| **On-Chain Cost** | O(rule complexity) | O(1) - constant proof verification | -| **Gas Usage** | High, varies per piece | Low, fixed per move | -| **Extensibility** | Requires contract upgrade | Update circuit off-chain | -| **Privacy** | All moves public | Can hide move details (future) | - -### Architecture Pattern - -```text -Off-Chain (Player) On-Chain (Contract) -───────────────────── ─────────────────── - -1. Generate move - (e4 → e5) - -2. Prove legality - Circuit validates: - - Piece can move this way - - Path is clear - - Move doesn't leave king in check - - Current board state matches - -3. Generate Groth16 proof - Public inputs: - - state_hash (current board) - - from (e4) - - to (e5) - 4. Verify proof - ✓ Proof valid? - ✓ State hash matches? - ✓ Player's turn? - - 5. Apply move - - Update board - - Compute new state_hash - - Switch turn -``` - -## Game Flow - -### 1. INIT -```rust -new_game(white: Address, black: Address) -``` -- Initializes standard chess board -- Computes initial state hash (Poseidon2) -- Sets white as current player - -### 2. MOVE (per turn) -```rust -submit_move(player: Address, from: u8, to: u8, proof: Bytes) -> MoveResult -``` - -**Off-chain (player):** -1. Generate move (e.g., pawn from 12 → 20) -2. Build circuit with current `state_hash`, `from`, `to` -3. Prove move is legal given board state -4. Submit proof to contract - -**On-chain (contract):** -1. **TurnSystem**: Validate player and turn order -2. **ProofVerificationSystem**: Verify Groth16 proof - - Uses `GameCircuit` trait - - Public inputs: `[state_hash, from, to]` -3. **BoardUpdateSystem**: Apply move if proof valid -4. **TurnSystem**: Switch to next player -5. **EndGameSystem**: Check for king capture (simplified checkmate) - -### 3. END -```rust -resign(player: Address) -``` -- Player can resign at any time -- Checkmate detected when king is captured - -## ECS Architecture - -### Components - -| Component | Fields | Purpose | -|-----------|--------|---------| -| `BoardState` | `state_hash: BytesN<32>`
`pieces: Map` | Current board position
Hash for proof binding
Map for display | -| `Piece` | `kind: enum {King, Queen, Rook, Bishop, Knight, Pawn}`
`color: enum {White, Black}` | Piece type and ownership | -| `TurnState` | `current: Address`
`move_count: u32`
`status: enum {Playing, Checkmate, Draw, Resigned}` | Turn tracking and game status | -| `ProofRecord` | `last_proof: Bytes`
`verified: bool` | Audit trail of last proof | - -All components implement `cougr_core::component::ComponentTrait` for type-safe serialization. - -### Systems - -| System | Responsibility | -|--------|---------------| -| **ProofVerificationSystem** | Verifies Groth16 proof using `GameCircuit` trait
Validates move against current `state_hash` | -| **BoardUpdateSystem** | Applies verified move to board
Computes new `state_hash` (SHA-256) | -| **TurnSystem** | Enforces alternating turns
Validates player identity | -| **EndGameSystem** | Detects king capture (simplified checkmate)
Accepts resignation | - -## ZK Circuit Design - -### Move Validation Circuit - -Uses `cougr_core::privacy::experimental::CustomCircuitBuilder`: - -```rust -let circuit = CustomCircuit::builder(vk) - .add_bytes32(&state_hash) // Current board state - .add_u32(&env, from as u32) // Source position - .add_u32(&env, to as u32) // Destination position - .build(); - -let valid = circuit.verify(&env, &proof)?; -``` - -**Public Inputs:** -1. `state_hash` - Binds proof to specific board state (prevents replay) -2. `from` - Source square (0-63) -3. `to` - Destination square (0-63) - -**Private Inputs (in circuit, not on-chain):** -- Full board state -- Piece being moved -- Piece type-specific movement rules -- Path obstruction checks - -### Circuit Rules (Simplified Subset) - -| Piece | Movement Rule | Circuit Constraint | -|-------|--------------|-------------------| -| **Pawn** | Forward 1 (or 2 from start) | `to = from + 8` (white) or `from - 8` (black)
No piece at destination | -| **Knight** | L-shape (2+1 or 1+2) | `abs(to_row - from_row) * abs(to_col - from_col) = 2`
Can jump over pieces | -| **Rook** | Straight lines | `to_row = from_row` OR `to_col = from_col`
Path clear | -| **Bishop** | Diagonals | `abs(to_row - from_row) = abs(to_col - from_col)`
Path clear | -| **Queen** | Rook + Bishop | Combined constraints | -| **King** | One square any direction | `max(abs(to_row - from_row), abs(to_col - from_col)) = 1` | - -**Not implemented (future extensions):** -- Castling -- En passant -- Pawn promotion -- Check/checkmate validation (currently simplified to king capture) - -## Contract API - -### Functions +## Public contract API | Function | Parameters | Returns | Description | -|----------|-----------|---------|-------------| -| `new_game` | `white: Address`
`black: Address` | - | Initialize new game | -| `submit_move` | `player: Address`
`from: u8`
`to: u8`
`proof: Bytes` | `MoveResult` | Submit move with ZK proof | -| `resign` | `player: Address` | - | Resign the game | -| `get_board` | - | `BoardState` | Get current board state | -| `get_state` | - | `GameState` | Get full game state | -| `set_vk` | `vk: VerificationKey` | - | Set verification key (admin) | - -### Board Positions +|---|---|---|---| +| `new_game` | `white: Address`, `black: Address` | — | Initializes the standard 8x8 opening position, computes the initial `state_hash`, and sets White to move first. | +| `submit_move` | `player: Address`, `from: u32`, `to: u32`, `proof: Bytes` | `MoveResult` | Requires `player.require_auth()`. Checks turn order and game status, verifies the supplied proof against the stored verification key and current `state_hash`, then applies the move, recomputes the hash, and switches turns. | +| `resign` | `player: Address` | — | Marks the game `Resigned` if called by either registered player. | +| `get_board` | — | `BoardState` | Current piece map and `state_hash`. | +| `get_state` | — | `GameState` | Full game state: both players, board, turn state, and last proof record. | +| `set_vk` | `vk: VerificationKey` | — | Admin function to set/replace the Groth16 verification key used for move proofs. | -```text -56 57 58 59 60 61 62 63 ← Black pieces -48 49 50 51 52 53 54 55 ← Black pawns -... - 8 9 10 11 12 13 14 15 ← White pawns - 0 1 2 3 4 5 6 7 ← White pieces -``` +## Architecture overview -### Move Results +There is no `GameApp` tick loop. `submit_move` runs a fixed pipeline synchronously on each +invocation: -```rust -pub enum MoveResult { - Success, // Move applied, proof valid - InvalidProof, // ZK proof verification failed - WrongTurn, // Not player's turn - GameOver, // Game already ended -} ``` - -## Building & Testing - -### Prerequisites - -| Requirement | Version | -|------------|---------| -| Rust | 1.70.0+ | -| Stellar CLI | 25.0.0+ | - -```bash -cargo install stellar-cli +submit_move + └─ TurnSystem → rejects calls from the wrong player or a finished game + └─ ProofVerificationSystem → verifies the Groth16 proof against state_hash/from/to via CustomCircuit + └─ BoardUpdateSystem → applies the move to the piece map, recomputes state_hash + └─ TurnSystem → increments move_count, switches current player + └─ EndGameSystem → marks Checkmate if either king is no longer on the board ``` -### Build - -```bash -# Development build -cargo build - -# Optimized WASM -stellar contract build -``` - -### Test +`components.rs` defines the data (`Piece`, `BoardState`, `TurnState`, `ProofRecord`, +`GameState`, `MoveResult`) and the `ComponentTrait` implementations for `BoardState` and +`TurnState`. `systems.rs` holds the pure, storage-free game logic (`init_board`, +`compute_state_hash`, `apply_move`, `check_endgame`). `zk.rs` holds the proof-verification +wrapper (`build_move_circuit`, `decode_proof`) built on `cougr_core::privacy::experimental`. +`lib.rs` owns the single `GameState` aggregate, is the only module that touches contract +storage, and wires the systems and the ZK verification step together inside `submit_move`. + +## Storage model + +The entire game lives under one instance-storage key (`GAME_KEY`, +`symbol_short!("GAME")`) as a single `GameState` struct bundling both player addresses, the +board, turn state, and the last proof record. The verification key is stored separately +under its own instance-storage key (`VK_KEY`, `symbol_short!("VK")`) so it can be rotated +with `set_vk` independently of game state. Instance storage is used for both — not persistent +or temporary — because there is exactly one game and one verification key per contract +instance, with no per-entry TTL management needed. + +## Main gameplay flow + +1. Deployer calls `set_vk(vk)` once to register the Groth16 verification key matching the + off-chain move-validation circuit. +2. Deployer (or either player) calls `new_game(white, black)`; the board is seeded with the + standard opening position, White to move, and the initial `state_hash` is computed. +3. Off-chain, White picks a move (`from`, `to`), runs the move-validation circuit against the + current `state_hash`, and produces a Groth16 proof that the move is legal. +4. White calls `submit_move(white, from, to, proof)`. The contract checks turn order, verifies + the proof via `CustomCircuit`, and on success applies the move, recomputes `state_hash`, + records the proof, and switches the turn to Black. +5. Players alternate steps 3-4. If a king is ever captured, `EndGameSystem` sets the status to + `Checkmate`; either player may also call `resign` at any time to end the game early. +6. Any caller reads `get_board` or `get_state` to render the current position or check status. + +## Cougr APIs used + +- `cougr_core::component::ComponentTrait` — gives `BoardState` and `TurnState` a + `component_type()` symbol and byte-level `serialize`/`deserialize`, chosen because chess has + a single fixed-shape game state per contract instance rather than a dynamic entity + population that would benefit from `SimpleWorld`/`SimpleQueryBuilder` scanning. +- `cougr_core::privacy::experimental` (`CustomCircuit`) — chosen specifically because this + example demonstrates Groth16 proof submission: the contract never evaluates chess rules + itself, it only binds a proof's public inputs (`state_hash`, `from`, `to`) to a + verification key and checks the proof verifies. This is the canonical use case the + `privacy::experimental` guidance in the Cougr API table calls out (Groth16 proof submission + / pairing-based verification), as opposed to `privacy::stable`, which is for commit-reveal + and Merkle-proof patterns this example does not use. Newer examples (`hidden_hand`, + `fog_explorer`, `dice_duel`, `blind_auction`) use the higher-level `cougr_core::circuits` + pre-built builders instead of hand-rolling a `CustomCircuit`; this example predates that + module and is kept as a reference for the lower-level builder API. +- `cougr_core::privacy::{Groth16Proof, VerificationKey}` — the concrete proof and key types + the `CustomCircuit` builder and verifier operate on; stored on-chain (`VerificationKey`) or + passed per-call (`Groth16Proof`, decoded from the `proof: Bytes` argument) so the contract + has everything it needs to verify a move without re-deriving any chess logic. + +## Build and test commands ```bash cargo test -``` - -**Test Coverage:** - -| Category | Tests | Coverage | -|----------|-------|----------| -| Initialization | 3 | Game setup, board layout, state hashing | -| Turn Management | 3 | Turn switching, wrong turn rejection, move counting | -| Piece Movement | 3 | Pawn, knight, rook movement simulation | -| Endgame | 3 | Resignation, checkmate detection, post-game moves | -| Components | 2 | ComponentTrait serialization | -| Validation | 2 | Uninitialized game, proof verification | -| **Total** | **16** | **All passing** | - -## Key Constraints - -### Off-Chain Legality, On-Chain Verification - -The contract **never** checks if a knight can move in an L-shape or if a path is clear. It only verifies the ZK proof that says "this move is legal given the current board state." - -### State Hashing - -After each move, the board state is hashed (SHA-256). The proof ties the move to this specific hash, preventing: -- **Replay attacks**: Old proofs can't be reused -- **State manipulation**: Proof only valid for exact board state -- **Parallel game attacks**: Each game has unique state progression - -### Simplified Rules - -This implementation demonstrates the ZK verification pattern with basic movement: -- ✅ Basic piece movement (6 types) -- ✅ Turn enforcement -- ✅ King capture detection -- ❌ Castling (future) -- ❌ En passant (future) -- ❌ Pawn promotion (future) -- ❌ Full check/checkmate (future) - -These can be added by extending the circuit without changing the contract. - -## Deployment - -### Deploy to Testnet - -```bash -# Generate funded account -stellar keys generate chess-deployer --network --fund - -# Build contract stellar contract build - -# Deploy -stellar contract deploy \ - --wasm target/wasm32v1-none/release/chess.wasm \ - --source chess-deployer \ - --network -``` - -### Set Verification Key - -```bash -# Generate VK off-chain (using your circuit compiler) -# Then set it on-chain: - -stellar contract invoke \ - --id \ - --network \ - -- set_vk \ - --vk ``` -### Play a Game - -```bash -# Initialize game -stellar contract invoke \ - --id \ - --network \ - -- new_game \ - --white \ - --black - -# Submit move (white's turn) -stellar contract invoke \ - --id \ - --network \ - --source white-player \ - -- submit_move \ - --player \ - --from 12 \ - --to 20 \ - --proof - -# Get game state -stellar contract invoke \ - --id \ - --network \ - -- get_state -``` - -## Why This Matters for Complex Games - -### Scalability - -Traditional on-chain games hit limits quickly: -- **Chess**: 64 squares, 6 piece types, complex rules -- **Go**: 361 squares, ko rule, territory calculation -- **Magic: The Gathering**: Thousands of cards, stack resolution - -ZK proofs make these feasible: -- **Constant verification cost** regardless of rule complexity -- **Off-chain computation** scales with player hardware, not blockchain -- **Privacy potential**: Hide moves until reveal phase - -### Extensibility - -Add new rules without contract upgrades: -1. Update circuit off-chain -2. Generate new verification key -3. Call `set_vk()` on existing contract -4. Players use new circuit for proofs - -No redeployment, no migration, no downtime. - -### Future Enhancements - -- **Fog of war**: Prove valid move without revealing destination -- **Simultaneous turns**: Commit moves, reveal with proofs -- **AI opponents**: Prove AI made legal move without revealing strategy -- **Tournament brackets**: Prove game outcome without replaying all moves - -## Resources - -- [Cougr Repository](https://github.com/salazarsebas/Cougr) -- [GameCircuit Trait](https://github.com/salazarsebas/Cougr/blob/main/src/zk/traits.rs) -- [Circuit Implementations](https://github.com/salazarsebas/Cougr/blob/main/src/zk/circuits.rs) -- [Soroban Documentation](https://developers.stellar.org/docs/build/smart-contracts) -- [Groth16 Paper](https://eprint.iacr.org/2016/260.pdf) - -## License - -MIT OR Apache-2.0 +## Known limitations + +- Proof decoding (`zk::decode_proof`) is not implemented — it intentionally panics, since + wiring up a real off-chain Groth16 prover/serializer is out of scope for this example. Tests + exercise the turn/resign/game-over paths that return before proof verification is reached. +- The move-validation circuit itself (piece movement rules, path obstruction, check/checkmate + detection) lives off-chain and is not part of this repository; only the on-chain + verification wrapper is shown. +- Checkmate detection is simplified to "a king is missing from the board" rather than full + check/checkmate/stalemate rules. Castling, en passant, and pawn promotion are not modeled. +- Does not use `GameApp`, `ScheduleStage`, or `SimpleWorld` — `submit_move` runs its pipeline + directly from the contract entrypoint since each call has exactly one decision point and + does not need staged scheduling. diff --git a/examples/chess/src/components.rs b/examples/chess/src/components.rs new file mode 100644 index 00000000..9fdedea8 --- /dev/null +++ b/examples/chess/src/components.rs @@ -0,0 +1,114 @@ +use cougr_core::component::ComponentTrait; +use soroban_sdk::{contracttype, symbol_short, Address, Bytes, BytesN, Env, Map, Symbol}; + +#[contracttype] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum PieceKind { + King, + Queen, + Rook, + Bishop, + Knight, + Pawn, +} + +#[contracttype] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Color { + White, + Black, +} + +#[contracttype] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Piece { + pub kind: PieceKind, + pub color: Color, +} + +#[contracttype] +#[derive(Clone, Debug)] +pub struct BoardState { + pub state_hash: BytesN<32>, + pub pieces: Map, +} + +impl ComponentTrait for BoardState { + fn component_type() -> Symbol { + symbol_short!("board") + } + + fn serialize(&self, env: &Env) -> Bytes { + let mut bytes = Bytes::new(env); + for i in 0..32 { + bytes.push_back(self.state_hash.get(i).unwrap()); + } + bytes + } + + fn deserialize(_env: &Env, _data: &Bytes) -> Option { + None + } +} + +#[contracttype] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum GameStatus { + Playing, + Checkmate, + Draw, + Resigned, +} + +#[contracttype] +#[derive(Clone, Debug)] +pub struct TurnState { + pub current: Address, + pub move_count: u32, + pub status: GameStatus, +} + +impl ComponentTrait for TurnState { + fn component_type() -> Symbol { + symbol_short!("turn") + } + + fn serialize(&self, env: &Env) -> Bytes { + let mut bytes = Bytes::new(env); + bytes.append(&Bytes::from_array(env, &self.move_count.to_be_bytes())); + bytes + } + + fn deserialize(_env: &Env, _data: &Bytes) -> Option { + None + } +} + +#[contracttype] +#[derive(Clone, Debug)] +pub struct ProofRecord { + pub last_proof: Bytes, + pub verified: bool, +} + +#[contracttype] +#[derive(Clone, Debug)] +pub struct GameState { + pub white: Address, + pub black: Address, + pub board: BoardState, + pub turn: TurnState, + pub proof_record: ProofRecord, +} + +#[contracttype] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum MoveResult { + Success, + InvalidProof, + WrongTurn, + GameOver, +} + +pub(crate) const GAME_KEY: Symbol = symbol_short!("GAME"); +pub(crate) const VK_KEY: Symbol = symbol_short!("VK"); diff --git a/examples/chess/src/lib.rs b/examples/chess/src/lib.rs index ea8d72ef..d9e05329 100644 --- a/examples/chess/src/lib.rs +++ b/examples/chess/src/lib.rs @@ -1,123 +1,19 @@ #![no_std] -use cougr_core::component::ComponentTrait; -use cougr_core::privacy::experimental::CustomCircuit; -use cougr_core::privacy::{Groth16Proof, VerificationKey}; -use soroban_sdk::{ - contract, contractimpl, contracttype, symbol_short, Address, Bytes, BytesN, Env, Map, Symbol, -}; - -#[contracttype] -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum PieceKind { - King, - Queen, - Rook, - Bishop, - Knight, - Pawn, -} - -#[contracttype] -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Color { - White, - Black, -} - -#[contracttype] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Piece { - pub kind: PieceKind, - pub color: Color, -} - -#[contracttype] -#[derive(Clone, Debug)] -pub struct BoardState { - pub state_hash: BytesN<32>, - pub pieces: Map, -} - -impl ComponentTrait for BoardState { - fn component_type() -> Symbol { - symbol_short!("board") - } - - fn serialize(&self, env: &Env) -> Bytes { - let mut bytes = Bytes::new(env); - for i in 0..32 { - bytes.push_back(self.state_hash.get(i).unwrap()); - } - bytes - } - - fn deserialize(_env: &Env, _data: &Bytes) -> Option { - None - } -} - -#[contracttype] -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum GameStatus { - Playing, - Checkmate, - Draw, - Resigned, -} - -#[contracttype] -#[derive(Clone, Debug)] -pub struct TurnState { - pub current: Address, - pub move_count: u32, - pub status: GameStatus, -} - -impl ComponentTrait for TurnState { - fn component_type() -> Symbol { - symbol_short!("turn") - } - - fn serialize(&self, env: &Env) -> Bytes { - let mut bytes = Bytes::new(env); - bytes.append(&Bytes::from_array(env, &self.move_count.to_be_bytes())); - bytes - } - - fn deserialize(_env: &Env, _data: &Bytes) -> Option { - None - } -} - -#[contracttype] -#[derive(Clone, Debug)] -pub struct ProofRecord { - pub last_proof: Bytes, - pub verified: bool, -} - -#[contracttype] -#[derive(Clone, Debug)] -pub struct GameState { - pub white: Address, - pub black: Address, - pub board: BoardState, - pub turn: TurnState, - pub proof_record: ProofRecord, -} +mod components; +mod systems; +#[cfg(test)] +mod test; +mod zk; -#[contracttype] -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum MoveResult { - Success, - InvalidProof, - WrongTurn, - GameOver, -} +use components::{ + BoardState, GameState, GameStatus, MoveResult, ProofRecord, TurnState, GAME_KEY, VK_KEY, +}; +use systems::{apply_move, check_endgame, compute_state_hash, init_board}; +use zk::{build_move_circuit, decode_proof}; -const GAME_KEY: Symbol = symbol_short!("GAME"); -const VK_KEY: Symbol = symbol_short!("VK"); +use cougr_core::privacy::VerificationKey; +use soroban_sdk::{contract, contractimpl, Address, Bytes, Env}; #[contract] pub struct ChessContract; @@ -126,8 +22,8 @@ pub struct ChessContract; impl ChessContract { /// Initialize a new chess game pub fn new_game(env: Env, white: Address, black: Address) { - let board = Self::init_board(&env); - let state_hash = Self::compute_state_hash(&env, &board); + let board = init_board(&env); + let state_hash = compute_state_hash(&env, &board); let game_state = GameState { white: white.clone(), @@ -176,8 +72,8 @@ impl ChessContract { .get(&VK_KEY) .unwrap_or_else(|| panic!("VK not set")); - let groth16_proof = Self::decode_proof(&env, &proof); - let circuit = Self::build_move_circuit(&env, &vk, &game.board.state_hash, from, to); + let groth16_proof = decode_proof(&env, &proof); + let circuit = build_move_circuit(&env, &vk, &game.board.state_hash, from, to); let verified = circuit.verify(&env, &groth16_proof).unwrap_or(false); @@ -186,10 +82,10 @@ impl ChessContract { } // BoardUpdateSystem: apply the move - Self::apply_move(&mut game, from, to); + apply_move(&mut game, from, to); // Update state hash - game.board.state_hash = Self::compute_state_hash(&env, &game.board.pieces); + game.board.state_hash = compute_state_hash(&env, &game.board.pieces); // Update proof record game.proof_record.last_proof = proof; @@ -204,7 +100,7 @@ impl ChessContract { }; // EndGameSystem: check for checkmate (simplified: king captured) - Self::check_endgame(&mut game); + check_endgame(&mut game); env.storage().instance().set(&GAME_KEY, &game); MoveResult::Success @@ -248,220 +144,4 @@ impl ChessContract { pub fn set_vk(env: Env, vk: VerificationKey) { env.storage().instance().set(&VK_KEY, &vk); } - - // Internal helper functions - - fn init_board(env: &Env) -> Map { - let mut board = Map::new(env); - - // White pieces (bottom) - board.set( - 0, - Piece { - kind: PieceKind::Rook, - color: Color::White, - }, - ); - board.set( - 1, - Piece { - kind: PieceKind::Knight, - color: Color::White, - }, - ); - board.set( - 2, - Piece { - kind: PieceKind::Bishop, - color: Color::White, - }, - ); - board.set( - 3, - Piece { - kind: PieceKind::Queen, - color: Color::White, - }, - ); - board.set( - 4, - Piece { - kind: PieceKind::King, - color: Color::White, - }, - ); - board.set( - 5, - Piece { - kind: PieceKind::Bishop, - color: Color::White, - }, - ); - board.set( - 6, - Piece { - kind: PieceKind::Knight, - color: Color::White, - }, - ); - board.set( - 7, - Piece { - kind: PieceKind::Rook, - color: Color::White, - }, - ); - for i in 8..16 { - board.set( - i, - Piece { - kind: PieceKind::Pawn, - color: Color::White, - }, - ); - } - - // Black pieces (top) - board.set( - 56, - Piece { - kind: PieceKind::Rook, - color: Color::Black, - }, - ); - board.set( - 57, - Piece { - kind: PieceKind::Knight, - color: Color::Black, - }, - ); - board.set( - 58, - Piece { - kind: PieceKind::Bishop, - color: Color::Black, - }, - ); - board.set( - 59, - Piece { - kind: PieceKind::Queen, - color: Color::Black, - }, - ); - board.set( - 60, - Piece { - kind: PieceKind::King, - color: Color::Black, - }, - ); - board.set( - 61, - Piece { - kind: PieceKind::Bishop, - color: Color::Black, - }, - ); - board.set( - 62, - Piece { - kind: PieceKind::Knight, - color: Color::Black, - }, - ); - board.set( - 63, - Piece { - kind: PieceKind::Rook, - color: Color::Black, - }, - ); - for i in 48..56 { - board.set( - i, - Piece { - kind: PieceKind::Pawn, - color: Color::Black, - }, - ); - } - - board - } - - fn compute_state_hash(env: &Env, board: &Map) -> BytesN<32> { - let mut data = Bytes::new(env); - for pos in 0..64u32 { - if let Some(piece) = board.get(pos) { - data.append(&Bytes::from_array(env, &pos.to_be_bytes())); - let kind_byte = match piece.kind { - PieceKind::King => 1u8, - PieceKind::Queen => 2u8, - PieceKind::Rook => 3u8, - PieceKind::Bishop => 4u8, - PieceKind::Knight => 5u8, - PieceKind::Pawn => 6u8, - }; - let color_byte = match piece.color { - Color::White => 0u8, - Color::Black => 1u8, - }; - data.append(&Bytes::from_array(env, &[kind_byte, color_byte])); - } - } - env.crypto().sha256(&data).into() - } - - fn build_move_circuit( - env: &Env, - vk: &VerificationKey, - state_hash: &BytesN<32>, - from: u32, - to: u32, - ) -> CustomCircuit { - CustomCircuit::builder(vk.clone()) - .add_bytes32(state_hash) - .add_u32(env, from) - .add_u32(env, to) - .build() - } - - fn decode_proof(_env: &Env, _proof_bytes: &Bytes) -> Groth16Proof { - // In a real implementation, this would decode the proof from bytes - // For now, we create a dummy proof structure - // The actual proof would be serialized from the off-chain prover - panic!("Proof decoding not implemented - use mock in tests") - } - - fn apply_move(game: &mut GameState, from: u32, to: u32) { - if let Some(piece) = game.board.pieces.get(from) { - game.board.pieces.set(to, piece); - game.board.pieces.remove(from); - } - } - - fn check_endgame(game: &mut GameState) { - let mut white_king_exists = false; - let mut black_king_exists = false; - - for pos in 0..64u32 { - if let Some(piece) = game.board.pieces.get(pos) { - if piece.kind == PieceKind::King { - match piece.color { - Color::White => white_king_exists = true, - Color::Black => black_king_exists = true, - } - } - } - } - - if !white_king_exists || !black_king_exists { - game.turn.status = GameStatus::Checkmate; - } - } } - -#[cfg(test)] -mod test; diff --git a/examples/chess/src/systems.rs b/examples/chess/src/systems.rs new file mode 100644 index 00000000..6a374359 --- /dev/null +++ b/examples/chess/src/systems.rs @@ -0,0 +1,193 @@ +use soroban_sdk::{Bytes, BytesN, Env, Map}; + +use crate::components::{Color, GameState, GameStatus, Piece, PieceKind}; + +pub(crate) fn init_board(env: &Env) -> Map { + let mut board = Map::new(env); + + // White pieces (bottom) + board.set( + 0, + Piece { + kind: PieceKind::Rook, + color: Color::White, + }, + ); + board.set( + 1, + Piece { + kind: PieceKind::Knight, + color: Color::White, + }, + ); + board.set( + 2, + Piece { + kind: PieceKind::Bishop, + color: Color::White, + }, + ); + board.set( + 3, + Piece { + kind: PieceKind::Queen, + color: Color::White, + }, + ); + board.set( + 4, + Piece { + kind: PieceKind::King, + color: Color::White, + }, + ); + board.set( + 5, + Piece { + kind: PieceKind::Bishop, + color: Color::White, + }, + ); + board.set( + 6, + Piece { + kind: PieceKind::Knight, + color: Color::White, + }, + ); + board.set( + 7, + Piece { + kind: PieceKind::Rook, + color: Color::White, + }, + ); + for i in 8..16 { + board.set( + i, + Piece { + kind: PieceKind::Pawn, + color: Color::White, + }, + ); + } + + // Black pieces (top) + board.set( + 56, + Piece { + kind: PieceKind::Rook, + color: Color::Black, + }, + ); + board.set( + 57, + Piece { + kind: PieceKind::Knight, + color: Color::Black, + }, + ); + board.set( + 58, + Piece { + kind: PieceKind::Bishop, + color: Color::Black, + }, + ); + board.set( + 59, + Piece { + kind: PieceKind::Queen, + color: Color::Black, + }, + ); + board.set( + 60, + Piece { + kind: PieceKind::King, + color: Color::Black, + }, + ); + board.set( + 61, + Piece { + kind: PieceKind::Bishop, + color: Color::Black, + }, + ); + board.set( + 62, + Piece { + kind: PieceKind::Knight, + color: Color::Black, + }, + ); + board.set( + 63, + Piece { + kind: PieceKind::Rook, + color: Color::Black, + }, + ); + for i in 48..56 { + board.set( + i, + Piece { + kind: PieceKind::Pawn, + color: Color::Black, + }, + ); + } + + board +} + +pub(crate) fn compute_state_hash(env: &Env, board: &Map) -> BytesN<32> { + let mut data = Bytes::new(env); + for pos in 0..64u32 { + if let Some(piece) = board.get(pos) { + data.append(&Bytes::from_array(env, &pos.to_be_bytes())); + let kind_byte = match piece.kind { + PieceKind::King => 1u8, + PieceKind::Queen => 2u8, + PieceKind::Rook => 3u8, + PieceKind::Bishop => 4u8, + PieceKind::Knight => 5u8, + PieceKind::Pawn => 6u8, + }; + let color_byte = match piece.color { + Color::White => 0u8, + Color::Black => 1u8, + }; + data.append(&Bytes::from_array(env, &[kind_byte, color_byte])); + } + } + env.crypto().sha256(&data).into() +} + +pub(crate) fn apply_move(game: &mut GameState, from: u32, to: u32) { + if let Some(piece) = game.board.pieces.get(from) { + game.board.pieces.set(to, piece); + game.board.pieces.remove(from); + } +} + +pub(crate) fn check_endgame(game: &mut GameState) { + let mut white_king_exists = false; + let mut black_king_exists = false; + + for pos in 0..64u32 { + if let Some(piece) = game.board.pieces.get(pos) { + if piece.kind == PieceKind::King { + match piece.color { + Color::White => white_king_exists = true, + Color::Black => black_king_exists = true, + } + } + } + } + + if !white_king_exists || !black_king_exists { + game.turn.status = GameStatus::Checkmate; + } +} diff --git a/examples/chess/src/test.rs b/examples/chess/src/test.rs index 27c38cbd..8d75d149 100644 --- a/examples/chess/src/test.rs +++ b/examples/chess/src/test.rs @@ -1,6 +1,8 @@ use super::*; +use crate::components::{Color, Piece, PieceKind}; +use cougr_core::component::ComponentTrait; use cougr_core::zk::{G1Point, G2Point}; -use soroban_sdk::{testutils::Address as _, Address, BytesN, Env, Vec}; +use soroban_sdk::{symbol_short, testutils::Address as _, Address, BytesN, Env, Map, Vec}; fn setup_game() -> (Env, ChessContractClient<'static>, Address, Address) { let env = Env::default(); @@ -359,6 +361,6 @@ fn test_proof_record_update() { client.new_game(&white, &black); let initial_state = client.get_state(); - assert_eq!(initial_state.proof_record.verified, false); + assert!(!initial_state.proof_record.verified); assert_eq!(initial_state.proof_record.last_proof.len(), 0); } diff --git a/examples/chess/src/zk.rs b/examples/chess/src/zk.rs new file mode 100644 index 00000000..7abee271 --- /dev/null +++ b/examples/chess/src/zk.rs @@ -0,0 +1,24 @@ +use cougr_core::privacy::experimental::CustomCircuit; +use cougr_core::privacy::{Groth16Proof, VerificationKey}; +use soroban_sdk::{Bytes, BytesN, Env}; + +pub(crate) fn build_move_circuit( + env: &Env, + vk: &VerificationKey, + state_hash: &BytesN<32>, + from: u32, + to: u32, +) -> CustomCircuit { + CustomCircuit::builder(vk.clone()) + .add_bytes32(state_hash) + .add_u32(env, from) + .add_u32(env, to) + .build() +} + +pub(crate) fn decode_proof(_env: &Env, _proof_bytes: &Bytes) -> Groth16Proof { + // In a real implementation, this would decode the proof from bytes + // For now, we create a dummy proof structure + // The actual proof would be serialized from the off-chain prover + panic!("Proof decoding not implemented - use mock in tests") +} diff --git a/examples/connect_four/Cargo.lock b/examples/connect_four/Cargo.lock new file mode 100644 index 00000000..87b8cb11 --- /dev/null +++ b/examples/connect_four/Cargo.lock @@ -0,0 +1,1934 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[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 = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[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.118", +] + +[[package]] +name = "cc" +version = "1.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_eval" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "connect_four" +version = "0.1.0" +dependencies = [ + "cougr-core", + "soroban-sdk", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cougr-core" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc489e355664841b5fb4d0b19e014c3be1a9b81cd1cd9f7ad29f02fe54a4135" +dependencies = [ + "soroban-sdk", + "wee_alloc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "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 = "ctor" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dtor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "escape-bytes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" + +[[package]] +name = "ethnum" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if 1.0.4", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[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 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if 1.0.4", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if 1.0.4", + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.118", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[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", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[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_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.8.22", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7192e3a5551a7aeee90d2110b11b615798e81951fd8c8293c87ea7f88b0168f5" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "soroban-env-common" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc49a80a68fc1005847308e63b9fce39874de731940b1807b721d472de3ff01" +dependencies = [ + "arbitrary", + "crate-git-revision", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr", + "wasmparser", +] + +[[package]] +name = "soroban-env-guest" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2334ba1cfe0a170ab744d96db0b4ca86934de9ff68187ceebc09dc342def55" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43af5d53c57bc2f546e122adc0b1cca6f93942c718977379aa19ddd04f06fcec" +dependencies = [ + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "curve25519-dalek", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "generic-array", + "getrandom", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "p256", + "rand", + "rand_chacha", + "sec1", + "sha2", + "sha3", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", + "static_assertions", + "stellar-strkey 0.0.13", + "wasmparser", +] + +[[package]] +name = "soroban-env-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a989167512e3592d455b1e204d703cfe578a36672a77ed2f9e6f7e1bbfd9cc5c" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr", + "syn 2.0.118", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ca06e6c5029d1285e66219cb387a234224e26969ce8ad2bc2d5017e9395d63b" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common", + "soroban-env-host", + "thiserror", +] + +[[package]] +name = "soroban-sdk" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4502f2e018f238a4c5d3212d7d20ea6abcdc6e58babd63b642b693739db30fd1" +dependencies = [ + "arbitrary", + "bytes-lit", + "crate-git-revision", + "ctor", + "derive_arbitrary", + "ed25519-dalek", + "rand", + "rustc_version", + "serde", + "serde_json", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey 0.0.16", + "visibility", +] + +[[package]] +name = "soroban-sdk-macros" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca03e9cf61d241cb9afdd6ddf41f6c25698b3f566a875e7009ea799b89e2bf0a" +dependencies = [ + "darling 0.20.11", + "heck", + "itertools", + "macro-string", + "proc-macro2", + "quote", + "sha2", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", + "syn 2.0.118", +] + +[[package]] +name = "soroban-spec" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa02e07f507cc27406ae0834db4dcf309b78c4cc8776eb3b2d662d66e8859d25" +dependencies = [ + "base64", + "sha2", + "stellar-xdr", + "thiserror", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6835bb510763ef3fa5405e89036e3c8ea6ef5abe55fc52cfe9ac0e38be9d531c" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2", + "soroban-spec", + "stellar-xdr", + "syn 2.0.118", + "thiserror", +] + +[[package]] +name = "soroban-wasmi" +version = "0.31.1-soroban.20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" +dependencies = [ + "smallvec", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[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.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1832fb50c651ad10f734aaf5d31ca5acdfb197a6ecda64d93fcdb8885af913" +dependencies = [ + "crate-git-revision", + "data-encoding", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084afcb0d458c3d5d5baa2d294b18f881e62cc258ef539d8fdf68be7dbe45520" +dependencies = [ + "crate-git-revision", + "data-encoding", + "heapless", +] + +[[package]] +name = "stellar-xdr" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d20dafed80076b227d4b17c0c508a4bbc4d5e4c3d4c1de7cd42242df4b1eaf" +dependencies = [ + "arbitrary", + "base64", + "cfg_eval", + "crate-git-revision", + "escape-bytes", + "ethnum", + "hex", + "serde", + "serde_with", + "sha2", + "stellar-strkey 0.0.13", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[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.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "time" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.118", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmi_arena" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "wasmparser" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" +dependencies = [ + "indexmap 2.14.0", + "semver", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[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-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/examples/connect_four/README.md b/examples/connect_four/README.md index b8f1093d..15d6e565 100644 --- a/examples/connect_four/README.md +++ b/examples/connect_four/README.md @@ -1,320 +1,111 @@ -# Connect Four On-Chain Game +# Connect Four -A fully functional Connect Four game implemented as a Soroban smart contract on the Stellar blockchain, demonstrating the **Cougr-Core** ECS (Entity Component System) framework for on-chain gaming. +> **Transitional example**: This example uses an older Cougr pattern and is preserved +> for compatibility reference. For the current recommended approach, see `snake`. -| | | -| --------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +An on-chain Connect Four game built with the [Cougr](../../README.md) ECS framework on +Stellar Soroban. -## Why Cougr-Core? +## Purpose and pattern -Cougr-Core provides an ECS architecture that simplifies on-chain game development. Here's how it compares to vanilla Soroban: +This example demonstrates a turn-based, perfect-information board game driven by +gravity-based piece placement: a move only specifies a column, and the contract resolves +which row the piece lands in before checking for a win. It showcases Cougr's +`ComponentTrait` pattern for typed, byte-serializable game state — components are plain +Rust structs annotated with `#[contracttype]` that implement `ComponentTrait` directly, +rather than being scanned through `SimpleWorld`/`SimpleQueryBuilder`. This is a +lighter-weight integration suited to a single fixed-shape game state (one board, two +players) with no dynamic entity population. -| Aspect | Vanilla Soroban | With Cougr-Core | -| ---------------------- | ------------------------------------- | --------------------------------------------------------- | -| **Data Serialization** | Manual byte packing/unpacking | `ComponentTrait` with type-safe `serialize`/`deserialize` | -| **Code Organization** | Monolithic contract logic | Modular components and systems | -| **Type Safety** | Runtime errors from format mismatches | Compile-time checking via traits | -| **Reusability** | Copy-paste between projects | Shared component interfaces across games | -| **Extensibility** | Refactor existing code | Add new systems without modification | +## Public contract API -### ComponentTrait Integration +| Function | Parameters | Returns | Description | +|---|---|---|---| +| `init_game` | `player_one: Address`, `player_two: Address` | `GameState` | Initializes an empty 7×6 board; `player_one` moves first. | +| `drop_piece` | `player: Address`, `column: u32` | `DropResult` | Validates the move, drops a piece into the lowest empty row of `column` (0–6), checks for a win/draw, and advances the turn. | +| `get_state` | — | `GameState` | Current board, turn, move count, and status. | +| `get_board` | — | `Vec` | Raw 42-cell board array (row-major, 0=empty/1=player one/2=player two). | +| `is_valid_column` | `column: u32` | `bool` | Whether `column` is in bounds, not full, and the game is still in progress. | +| `is_finished` | — | `bool` | Whether the game has ended (win or draw). | +| `get_winner` | — | `Option
` | The winning player's address, or `None` if the game is a draw or still in progress. | +| `reset_game` | — | `GameState` | Re-initializes the board with the same two players. | -All game components implement `cougr_core::component::ComponentTrait`: +## Architecture overview -```rust -impl ComponentTrait for BoardComponent { - fn component_type() -> Symbol { - symbol_short!("board") - } +There is no `GameApp` tick loop — `drop_piece` runs all systems synchronously in a fixed +pipeline on each invocation: - fn serialize(&self, env: &Env) -> Bytes { /* ... */ } - fn deserialize(env: &Env, data: &Bytes) -> Option { /* ... */ } -} ``` - -### ECS System Pattern - -Game logic is organized into discrete systems: - -| System | Responsibility | -| -------------------- | ------------------------------------------------------- | -| `validation_system` | Enforces game rules (turn order, column bounds, full) | -| `gravity_system` | Finds lowest empty row for piece placement | -| `execution_system` | Places piece on the board | -| `win_detection_system` | Checks horizontal, vertical, and diagonal wins | -| `draw_system` | Detects full board with no winner | -| `turn_system` | Manages turn transitions | - -## Features - -| Feature | Description | -| ---------------------- | -------------------------------------------------------------- | -| Two-player gameplay | Uses Stellar addresses for player identification | -| Turn-based mechanics | Player One goes first, enforced turn order | -| Gravity-based placement| Pieces automatically fall to lowest available row | -| Win detection | Horizontal, vertical, and both diagonal patterns | -| Draw detection | Recognizes full board with no winner | -| Move validation | Rejects invalid columns, full columns, wrong turns | -| Game reset | Restart with same players | -| Last move tracking | Tracks which column was last played | - -## Prerequisites - -| Requirement | Version | -| ----------- | --------------------- | -| Rust | 1.70.0+ | -| Stellar CLI | 25.0.0+ (recommended) | - -```bash -cargo install stellar-cli -``` - -## Building - -```bash -# Build for testing -cargo build - -# Build optimized WASM -stellar contract build +drop_piece + └─ validation_system → rejects out-of-turn, out-of-bounds, full-column, or post-game moves + └─ gravity_system → finds the lowest empty row in the target column + └─ execution_system → places the piece and increments the move counter + └─ win_detection_system → scans the board for horizontal/vertical/diagonal 4-in-a-row + └─ draw_system → marks the game a draw if the board fills with no winner + └─ turn_system → advances to the other player if the game is still active ``` -## Testing +Each system is a pure function over `components.rs` types (`BoardComponent`, +`PlayerComponent`, `GameStateComponent`, bundled into `ECSWorldState`); `lib.rs` owns that +aggregate and is the only module that touches contract storage. + +## Storage model + +The entire game lives under one instance-storage key (`WORLD_KEY`, +`symbol_short!("WORLD")`) as a single `ECSWorldState` struct bundling the board, both +player addresses, and the game-state component (turn, move count, status, last move). +Instance storage is used — not persistent or temporary — because there is exactly one game +per contract instance and the state must survive for the lifetime of that instance with no +per-entry TTL management. + +## Main gameplay flow + +1. Deployer calls `init_game(player_one, player_two)`; the board is seeded empty, + `player_one` to move, status in-progress. +2. `player_one` calls `drop_piece(player, column)`. The contract checks turn order, column + bounds, and that the column isn't full. +3. On a legal move: `gravity_system` finds the lowest empty row in that column, + `execution_system` places the piece there and increments the move count, + `win_detection_system` scans the whole board for a 4-in-a-row, and `draw_system` marks + the game a draw if all 42 cells are filled with no winner. +4. If the game is still in progress, `turn_system` switches to the other player; the + updated `ECSWorldState` is written back to instance storage. +5. Players alternate `drop_piece` calls until `is_finished` returns `true`. Either player + reads `get_winner` to see who won (or `None` for a draw). +6. `reset_game` can be called at any point to start a fresh game with the same two players. + +## Cougr APIs used + +- `cougr_core::component::ComponentTrait` — gives each component (`BoardComponent`, + `GameStateComponent`) a `component_type()` symbol and explicit byte-level + `serialize`/`deserialize`. This was chosen because the game has one fixed-shape + `ECSWorldState` per contract instance (a single board, two fixed player slots, one game- + state record) rather than a dynamic population of entities that would benefit from + `SimpleWorld`/`SimpleQueryBuilder` scanning — per the guidance in + [`EXAMPLE_STANDARD.md` §8](../EXAMPLE_STANDARD.md#8-cougr-api-usage-guidance), `SimpleWorld` + and `SimpleQueryBuilder` are for examples that store and query entities by component type, + which doesn't apply here. `PlayerComponent` is also `#[contracttype]` but does not + implement `ComponentTrait`, since it is embedded directly in `ECSWorldState` rather than + serialized standalone. +- No `GameApp`, `ScheduleStage`, `auth`, `privacy::stable`/`experimental`, or `ops` + standards are used — `drop_piece` has exactly one decision point per call, so there is no + multi-stage tick to schedule, no hidden information to commit-reveal, and no + pausability/ownership requirement for a two-player, always-on game. + +## Build and test commands ```bash cargo test -``` - -| Test Category | Count | Coverage | -| --------------------- | ----- | ----------------------------------------------------- | -| Initialization | 2 | Game setup, board retrieval | -| Legal token drop | 3 | First move, gravity stacking, alternating turns | -| Full column rejection | 2 | Column filled completely, validation after fill | -| Wrong turn rejection | 2 | Out-of-turn play, invalid player | -| Horizontal win | 3 | Bottom row, middle row, any row | -| Vertical win | 2 | Player 1 vertical, Player 2 vertical | -| Diagonal win | 2 | Positive slope, negative slope | -| Draw detection | 2 | Full board scenarios | -| Edge cases | 7 | Out of bounds, game over, reset, winner tracking, etc | -| **Total** | **25**| **All passing** | - -## Contract API - -### Functions - -| Function | Parameters | Returns | Description | -| -------------- | -------------------------------------- | ------------- | ------------------------------------ | -| `init_game` | `player_one: Address, player_two: Address` | `GameState` | Initialize new game | -| `drop_piece` | `player: Address, column: u32` | `DropResult` | Drop piece in column (0-6) | -| `get_state` | - | `GameState` | Get current game state | -| `get_board` | - | `Vec` | Get flattened board array | -| `is_valid_column` | `column: u32` | `bool` | Check if column is valid and not full| -| `is_finished` | - | `bool` | Check if game is over | -| `get_winner` | - | `Option
` | Get winner's address if game over | -| `reset_game` | - | `GameState` | Reset with same players | - -### Board Layout - -``` -Columns: 0 1 2 3 4 5 6 - ┌───────────────────┐ -Row 0 │ . . . . . . . │ -Row 1 │ . . . . . . . │ -Row 2 │ . . . . . . . │ -Row 3 │ . . . . . . . │ -Row 4 │ . . . . . . . │ -Row 5 │ . . . . . . . │ - └───────────────────┘ -``` - -- **Rows**: 6 (indexed 0-5, top to bottom) -- **Columns**: 7 (indexed 0-6, left to right) -- **Cell values**: 0 = Empty, 1 = Player One, 2 = Player Two - -### Data Structures - -**GameState** -| Field | Type | Description | -| -------------------- | ------------ | ---------------------------------------------------- | -| `board` | `Vec` | Flattened 7×6 board (row-major order) | -| `rows` | `u32` | Number of rows (6) | -| `cols` | `u32` | Number of columns (7) | -| `player_one` | `Address` | Player One's address | -| `player_two` | `Address` | Player Two's address | -| `is_player_one_turn` | `bool` | True if Player One's turn | -| `move_count` | `u32` | Total moves made | -| `status` | `u32` | 0=InProgress, 1=P1Wins, 2=P2Wins, 3=Draw | -| `last_move_col` | `Option`| Column index of last move | - -**DropResult** -| Field | Type | Description | -| ------------- | ----------- | ------------------------------------- | -| `success` | `bool` | Whether move succeeded | -| `game_state` | `GameState` | Updated game state | -| `message` | `Symbol` | Status code | -| `row_placed` | `Option`| Row where piece landed (if success) | - -### Error Messages - -| Code | Meaning | -| ---------- | -------------------------------------------- | -| `ok` | Move successful | -| `invalid` | Column out of bounds (not 0-6) | -| `full` | Column is already full | -| `notturn` | Not the player's turn | -| `notplay` | Address is not a registered player | -| `gameover` | Game has already ended | - -## Architecture - -```text -ECSWorldState -├── BoardComponent (entity_id: 0) -│ └── cells: Vec [42 cells - 7 columns × 6 rows] -├── PlayerComponent (entity_id: 1) -│ ├── player_one: Address -│ └── player_two: Address -├── GameStateComponent (entity_id: 2) -│ ├── is_player_one_turn: bool -│ ├── move_count: u32 -│ ├── status: u32 -│ ├── last_move_col: Option -│ └── entity_id: u32 -└── next_entity_id: u32 -``` - -### Win Detection Algorithm - -The win detection system checks all four directions from every occupied cell: - -1. **Horizontal**: Check 4 consecutive cells in the same row -2. **Vertical**: Check 4 consecutive cells in the same column -3. **Diagonal (positive slope)**: Check diagonal from bottom-left to top-right -4. **Diagonal (negative slope)**: Check diagonal from top-left to bottom-right - -```rust -// Example: Horizontal check -fn check_horizontal(board: &BoardComponent, env: &Env, row: u32, col: u32, cell: u32) -> bool { - if col + 3 >= COLS { return false; } - for i in 0..4 { - if board.get_cell(env, row, col + i) != cell { - return false; - } - } - true -} -``` - -## Gameplay Example - -```text -Initial State (empty board): -┌───────────────────┐ -│ . . . . . . . │ -│ . . . . . . . │ -│ . . . . . . . │ -│ . . . . . . . │ -│ . . . . . . . │ -│ . . . . . . . │ -└───────────────────┘ - -After moves: P1→col3, P2→col4, P1→col3, P2→col4, P1→col3, P2→col4 -┌───────────────────┐ -│ . . . . . . . │ -│ . . . . . . . │ -│ . . . . . . . │ -│ . . . . . . . │ -│ . . . 2 1 . . │ -│ . . . 1 2 . . │ -└───────────────────┘ - -Player 1 wins with vertical line in column 3: -┌───────────────────┐ -│ . . . . . . . │ -│ . . . . . . . │ -│ . . . 1 . . . │ -│ . . . 1 . . . │ -│ . . . 2 1 . . │ -│ . . . 1 2 . . │ -└───────────────────┘ -``` - -## Deployment - -### Deploy to Testnet - -```bash -# Generate funded account -stellar keys generate deployer --network --fund - -# Build contract stellar contract build - -# Deploy -stellar contract deploy \ - --wasm target/wasm32v1-none/release/connect_four.wasm \ - --source deployer \ - --network ``` -### Interact with Deployed Contract - -```bash -# Initialize a game -stellar contract invoke \ - --id \ - --network \ - -- init_game \ - --player_one \ - --player_two - -# Drop a piece in column 3 -stellar contract invoke \ - --id \ - --network \ - -- drop_piece \ - --player \ - --column 3 - -# Get game state -stellar contract invoke \ - --id \ - --network \ - -- get_state - -# Check if game is finished -stellar contract invoke \ - --id \ - --network \ - -- is_finished -``` - -## Game Rules - -1. **Objective**: Be the first to connect 4 pieces of your color in a row -2. **Turn Order**: Player One always goes first -3. **Placement**: - - Choose a column (0-6) - - Piece automatically falls to the lowest empty row in that column - - Cannot place in a full column -4. **Winning**: Connect 4 pieces horizontally, vertically, or diagonally -5. **Draw**: If all 42 spaces are filled with no winner -6. **Invalid Moves**: - - Playing out of turn - - Choosing an out-of-bounds column - - Choosing a full column - - Playing after game is over - -## Strategy Tips - -- **Center Control**: Columns 3 and 4 offer the most winning opportunities -- **Blocking**: Watch for opponent's 3-in-a-row patterns -- **Setup Moves**: Create multiple threats simultaneously -- **Gravity Awareness**: Remember pieces stack from bottom to top - -## Resources +## Known limitations -- [Cougr Repository](https://github.com/salazarsebas/Cougr) -- [Soroban Documentation](https://developers.stellar.org/docs/build/smart-contracts) -- [Stellar CLI Reference](https://developers.stellar.org/docs/tools/cli) -- [Connect Four Wikipedia](https://en.wikipedia.org/wiki/Connect_Four) +- Does not use `GameApp`, `ScheduleStage`, or `SimpleWorld` — game logic is invoked + directly from contract entrypoints rather than through a tick-based scheduler, since a + turn-based game with one decision point per call does not need staged scheduling. +- No timeout/forfeit mechanism for an unresponsive player. +- `win_detection_system` re-scans the entire board on every move rather than checking only + the cell that was just placed; this is simple and correct but not the most efficient + approach for a larger board. +- No spectator or replay API beyond the read-only getters. diff --git a/examples/connect_four/src/components.rs b/examples/connect_four/src/components.rs new file mode 100644 index 00000000..3169a8a8 --- /dev/null +++ b/examples/connect_four/src/components.rs @@ -0,0 +1,280 @@ +use cougr_core::component::ComponentTrait; +use soroban_sdk::{contracttype, symbol_short, Address, Bytes, Env, Symbol, Vec}; + +/// Board dimensions: 7 columns x 6 rows +pub(crate) const ROWS: u32 = 6; +pub(crate) const COLS: u32 = 7; + +pub(crate) const WORLD_KEY: Symbol = symbol_short!("WORLD"); + +/// Cell value: 0=Empty, 1=Player1, 2=Player2 +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Cell { + Empty = 0, + Player1 = 1, + Player2 = 2, +} + +/// Board component - stores the 7x6 game board state +#[contracttype] +#[derive(Clone, Debug)] +pub struct BoardComponent { + pub cells: Vec, // Flattened 2D array: index = row * COLS + col + pub entity_id: u32, +} + +impl BoardComponent { + pub fn new(env: &Env, entity_id: u32) -> Self { + let mut cells = Vec::new(env); + for _ in 0..(ROWS * COLS) { + cells.push_back(0u32); + } + Self { cells, entity_id } + } + + /// Get cell value at (row, col) + pub fn get_cell(&self, _env: &Env, row: u32, col: u32) -> u32 { + if row >= ROWS || col >= COLS { + return 0; + } + let index = row * COLS + col; + self.cells.get(index).unwrap_or(0) + } + + /// Set cell value at (row, col) + pub fn set_cell(&mut self, _env: &Env, row: u32, col: u32, value: u32) { + if row >= ROWS || col >= COLS { + return; + } + let index = row * COLS + col; + self.cells.set(index, value); + } + + /// Find the lowest empty row in a column (gravity-based placement) + pub fn get_lowest_empty_row(&self, _env: &Env, col: u32) -> Option { + if col >= COLS { + return None; + } + + // Start from bottom row and go up + for row in (0..ROWS).rev() { + let index = row * COLS + col; + let cell = self.cells.get(index).unwrap_or(0); + if cell == 0 { + return Some(row); + } + } + None // Column is full + } + + /// Check if column is full + pub fn is_column_full(&self, env: &Env, col: u32) -> bool { + self.get_lowest_empty_row(env, col).is_none() + } +} + +impl ComponentTrait for BoardComponent { + fn component_type() -> Symbol { + symbol_short!("board") + } + + fn serialize(&self, env: &Env) -> Bytes { + let mut bytes = Bytes::new(env); + bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); + let len = self.cells.len(); + bytes.append(&Bytes::from_array(env, &len.to_be_bytes())); + for i in 0..len { + let cell = self.cells.get(i).unwrap_or(0); + bytes.append(&Bytes::from_array(env, &cell.to_be_bytes())); + } + bytes + } + + fn deserialize(env: &Env, data: &Bytes) -> Option { + if data.len() < 8 { + return None; + } + let entity_id = u32::from_be_bytes([ + data.get(0).unwrap(), + data.get(1).unwrap(), + data.get(2).unwrap(), + data.get(3).unwrap(), + ]); + let len = u32::from_be_bytes([ + data.get(4).unwrap(), + data.get(5).unwrap(), + data.get(6).unwrap(), + data.get(7).unwrap(), + ]); + + let mut cells = Vec::new(env); + for i in 0..len { + let offset = 8 + i * 4; + if offset + 4 > data.len() { + break; + } + let cell = u32::from_be_bytes([ + data.get(offset).unwrap(), + data.get(offset + 1).unwrap(), + data.get(offset + 2).unwrap(), + data.get(offset + 3).unwrap(), + ]); + cells.push_back(cell); + } + Some(Self { cells, entity_id }) + } +} + +/// Player component - stores both players' addresses +#[contracttype] +#[derive(Clone, Debug)] +pub struct PlayerComponent { + pub player_one: Address, + pub player_two: Address, + pub entity_id: u32, +} + +impl PlayerComponent { + pub fn new(player_one: Address, player_two: Address, entity_id: u32) -> Self { + Self { + player_one, + player_two, + entity_id, + } + } +} + +/// Game state component +#[contracttype] +#[derive(Clone, Debug)] +pub struct GameStateComponent { + pub is_player_one_turn: bool, + pub move_count: u32, + pub status: u32, // 0=InProgress, 1=Player1Wins, 2=Player2Wins, 3=Draw + pub last_move_col: Option, + pub entity_id: u32, +} + +impl GameStateComponent { + pub fn new(entity_id: u32) -> Self { + Self { + is_player_one_turn: true, + move_count: 0, + status: 0, + last_move_col: None, + entity_id, + } + } +} + +impl ComponentTrait for GameStateComponent { + fn component_type() -> Symbol { + symbol_short!("gstate") + } + + fn serialize(&self, env: &Env) -> Bytes { + let mut bytes = Bytes::new(env); + bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); + bytes.append(&Bytes::from_array( + env, + &[if self.is_player_one_turn { 1 } else { 0 }], + )); + bytes.append(&Bytes::from_array(env, &self.move_count.to_be_bytes())); + bytes.append(&Bytes::from_array(env, &self.status.to_be_bytes())); + + // Serialize Option for last_move_col + match self.last_move_col { + Some(col) => { + bytes.append(&Bytes::from_array(env, &[1u8])); // Some + bytes.append(&Bytes::from_array(env, &col.to_be_bytes())); + } + None => { + bytes.append(&Bytes::from_array(env, &[0u8])); // None + } + } + + bytes + } + + fn deserialize(_env: &Env, data: &Bytes) -> Option { + if data.len() < 14 { + return None; + } + let entity_id = u32::from_be_bytes([ + data.get(0).unwrap(), + data.get(1).unwrap(), + data.get(2).unwrap(), + data.get(3).unwrap(), + ]); + let is_player_one_turn = data.get(4).unwrap() != 0; + let move_count = u32::from_be_bytes([ + data.get(5).unwrap(), + data.get(6).unwrap(), + data.get(7).unwrap(), + data.get(8).unwrap(), + ]); + let status = u32::from_be_bytes([ + data.get(9).unwrap(), + data.get(10).unwrap(), + data.get(11).unwrap(), + data.get(12).unwrap(), + ]); + + let has_last_move = data.get(13).unwrap() != 0; + let last_move_col = if has_last_move && data.len() >= 18 { + let col = u32::from_be_bytes([ + data.get(14).unwrap(), + data.get(15).unwrap(), + data.get(16).unwrap(), + data.get(17).unwrap(), + ]); + Some(col) + } else { + None + }; + + Some(Self { + is_player_one_turn, + move_count, + status, + last_move_col, + entity_id, + }) + } +} + +/// ECS World State - stores all game entities and components +#[contracttype] +#[derive(Clone, Debug)] +pub struct ECSWorldState { + pub board: BoardComponent, + pub players: PlayerComponent, + pub game_state: GameStateComponent, + pub next_entity_id: u32, +} + +/// External game state for API consumers +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GameState { + pub board: Vec, // Flattened 7x6 board + pub rows: u32, + pub cols: u32, + pub player_one: Address, + pub player_two: Address, + pub is_player_one_turn: bool, + pub move_count: u32, + pub status: u32, // 0=InProgress, 1=P1Wins, 2=P2Wins, 3=Draw + pub last_move_col: Option, +} + +/// Move result returned after each move +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DropResult { + pub success: bool, + pub game_state: GameState, + pub message: Symbol, + pub row_placed: Option, +} diff --git a/examples/connect_four/src/lib.rs b/examples/connect_four/src/lib.rs index 789bc1e0..dc914134 100644 --- a/examples/connect_four/src/lib.rs +++ b/examples/connect_four/src/lib.rs @@ -1,287 +1,20 @@ #![no_std] -use cougr_core::component::ComponentTrait; -use soroban_sdk::{ - contract, contractimpl, contracttype, symbol_short, Address, Bytes, Env, Symbol, Vec, -}; - -/// Board dimensions: 7 columns x 6 rows -const ROWS: u32 = 6; -const COLS: u32 = 7; - -/// Cell value: 0=Empty, 1=Player1, 2=Player2 -#[contracttype] -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Cell { - Empty = 0, - Player1 = 1, - Player2 = 2, -} - -/// Board component - stores the 7x6 game board state -#[contracttype] -#[derive(Clone, Debug)] -pub struct BoardComponent { - pub cells: Vec, // Flattened 2D array: index = row * COLS + col - pub entity_id: u32, -} - -impl BoardComponent { - pub fn new(env: &Env, entity_id: u32) -> Self { - let mut cells = Vec::new(env); - for _ in 0..(ROWS * COLS) { - cells.push_back(0u32); - } - Self { cells, entity_id } - } - - /// Get cell value at (row, col) - pub fn get_cell(&self, _env: &Env, row: u32, col: u32) -> u32 { - if row >= ROWS || col >= COLS { - return 0; - } - let index = row * COLS + col; - self.cells.get(index).unwrap_or(0) - } - - /// Set cell value at (row, col) - pub fn set_cell(&mut self, _env: &Env, row: u32, col: u32, value: u32) { - if row >= ROWS || col >= COLS { - return; - } - let index = row * COLS + col; - self.cells.set(index, value); - } - - /// Find the lowest empty row in a column (gravity-based placement) - pub fn get_lowest_empty_row(&self, _env: &Env, col: u32) -> Option { - if col >= COLS { - return None; - } - - // Start from bottom row and go up - for row in (0..ROWS).rev() { - let index = row * COLS + col; - let cell = self.cells.get(index).unwrap_or(0); - if cell == 0 { - return Some(row); - } - } - None // Column is full - } - - /// Check if column is full - pub fn is_column_full(&self, env: &Env, col: u32) -> bool { - self.get_lowest_empty_row(env, col).is_none() - } -} - -impl ComponentTrait for BoardComponent { - fn component_type() -> Symbol { - symbol_short!("board") - } - - fn serialize(&self, env: &Env) -> Bytes { - let mut bytes = Bytes::new(env); - bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); - let len = self.cells.len(); - bytes.append(&Bytes::from_array(env, &len.to_be_bytes())); - for i in 0..len { - let cell = self.cells.get(i).unwrap_or(0); - bytes.append(&Bytes::from_array(env, &cell.to_be_bytes())); - } - bytes - } - - fn deserialize(env: &Env, data: &Bytes) -> Option { - if data.len() < 8 { - return None; - } - let entity_id = u32::from_be_bytes([ - data.get(0).unwrap(), - data.get(1).unwrap(), - data.get(2).unwrap(), - data.get(3).unwrap(), - ]); - let len = u32::from_be_bytes([ - data.get(4).unwrap(), - data.get(5).unwrap(), - data.get(6).unwrap(), - data.get(7).unwrap(), - ]); - - let mut cells = Vec::new(env); - for i in 0..len { - let offset = 8 + i * 4; - if offset + 4 > data.len() { - break; - } - let cell = u32::from_be_bytes([ - data.get(offset).unwrap(), - data.get(offset + 1).unwrap(), - data.get(offset + 2).unwrap(), - data.get(offset + 3).unwrap(), - ]); - cells.push_back(cell); - } - Some(Self { cells, entity_id }) - } -} - -/// Player component - stores both players' addresses -#[contracttype] -#[derive(Clone, Debug)] -pub struct PlayerComponent { - pub player_one: Address, - pub player_two: Address, - pub entity_id: u32, -} - -impl PlayerComponent { - pub fn new(player_one: Address, player_two: Address, entity_id: u32) -> Self { - Self { - player_one, - player_two, - entity_id, - } - } -} - -/// Game state component -#[contracttype] -#[derive(Clone, Debug)] -pub struct GameStateComponent { - pub is_player_one_turn: bool, - pub move_count: u32, - pub status: u32, // 0=InProgress, 1=Player1Wins, 2=Player2Wins, 3=Draw - pub last_move_col: Option, - pub entity_id: u32, -} - -impl GameStateComponent { - pub fn new(entity_id: u32) -> Self { - Self { - is_player_one_turn: true, - move_count: 0, - status: 0, - last_move_col: None, - entity_id, - } - } -} - -impl ComponentTrait for GameStateComponent { - fn component_type() -> Symbol { - symbol_short!("gstate") - } - - fn serialize(&self, env: &Env) -> Bytes { - let mut bytes = Bytes::new(env); - bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); - bytes.append(&Bytes::from_array( - env, - &[if self.is_player_one_turn { 1 } else { 0 }], - )); - bytes.append(&Bytes::from_array(env, &self.move_count.to_be_bytes())); - bytes.append(&Bytes::from_array(env, &self.status.to_be_bytes())); - - // Serialize Option for last_move_col - match self.last_move_col { - Some(col) => { - bytes.append(&Bytes::from_array(env, &[1u8])); // Some - bytes.append(&Bytes::from_array(env, &col.to_be_bytes())); - } - None => { - bytes.append(&Bytes::from_array(env, &[0u8])); // None - } - } - - bytes - } - - fn deserialize(_env: &Env, data: &Bytes) -> Option { - if data.len() < 14 { - return None; - } - let entity_id = u32::from_be_bytes([ - data.get(0).unwrap(), - data.get(1).unwrap(), - data.get(2).unwrap(), - data.get(3).unwrap(), - ]); - let is_player_one_turn = data.get(4).unwrap() != 0; - let move_count = u32::from_be_bytes([ - data.get(5).unwrap(), - data.get(6).unwrap(), - data.get(7).unwrap(), - data.get(8).unwrap(), - ]); - let status = u32::from_be_bytes([ - data.get(9).unwrap(), - data.get(10).unwrap(), - data.get(11).unwrap(), - data.get(12).unwrap(), - ]); - - let has_last_move = data.get(13).unwrap() != 0; - let last_move_col = if has_last_move && data.len() >= 18 { - let col = u32::from_be_bytes([ - data.get(14).unwrap(), - data.get(15).unwrap(), - data.get(16).unwrap(), - data.get(17).unwrap(), - ]); - Some(col) - } else { - None - }; - - Some(Self { - is_player_one_turn, - move_count, - status, - last_move_col, - entity_id, - }) - } -} - -/// ECS World State - stores all game entities and components -#[contracttype] -#[derive(Clone, Debug)] -pub struct ECSWorldState { - pub board: BoardComponent, - pub players: PlayerComponent, - pub game_state: GameStateComponent, - pub next_entity_id: u32, -} - -/// External game state for API consumers -#[contracttype] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct GameState { - pub board: Vec, // Flattened 7x6 board - pub rows: u32, - pub cols: u32, - pub player_one: Address, - pub player_two: Address, - pub is_player_one_turn: bool, - pub move_count: u32, - pub status: u32, // 0=InProgress, 1=P1Wins, 2=P2Wins, 3=Draw - pub last_move_col: Option, -} +mod components; +mod systems; +#[cfg(test)] +mod tests; -/// Move result returned after each move -#[contracttype] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct DropResult { - pub success: bool, - pub game_state: GameState, - pub message: Symbol, - pub row_placed: Option, -} +use components::{ + BoardComponent, DropResult, ECSWorldState, GameState, GameStateComponent, PlayerComponent, + COLS, ROWS, WORLD_KEY, +}; +use systems::{ + draw_system, execution_system, gravity_system, turn_system, validation_system, + win_detection_system, +}; -const WORLD_KEY: Symbol = symbol_short!("WORLD"); +use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env, Vec}; #[contract] pub struct ConnectFourContract; @@ -322,7 +55,7 @@ impl ConnectFourContract { .unwrap_or_else(|| panic!("Game not initialized")); // Validation system - let validation = Self::validation_system(&world_state, &player, column); + let validation = validation_system(&world_state, &player, column); if !validation.0 { return DropResult { success: false, @@ -333,19 +66,19 @@ impl ConnectFourContract { } // Gravity placement system - let row_placed = Self::gravity_system(&mut world_state, column); + let row_placed = gravity_system(&mut world_state, column); // Execution system - place the piece - Self::execution_system(&mut world_state, row_placed, column); + execution_system(&mut world_state, row_placed, column); // Win detection system - Self::win_detection_system(&mut world_state); + win_detection_system(&mut world_state); // Draw detection system - Self::draw_system(&mut world_state); + draw_system(&mut world_state); // Turn system - Self::turn_system(&mut world_state); + turn_system(&mut world_state); env.storage().instance().set(&WORLD_KEY, &world_state); @@ -439,187 +172,6 @@ impl ConnectFourContract { ) } - /// Validation System - checks if move is legal - fn validation_system(world: &ECSWorldState, player: &Address, column: u32) -> (bool, Symbol) { - // Check if game is over - if world.game_state.status != 0 { - return (false, symbol_short!("gameover")); - } - - // Check column bounds - if column >= COLS { - return (false, symbol_short!("invalid")); - } - - // Check if player is registered - let is_player_one = *player == world.players.player_one; - let is_player_two = *player == world.players.player_two; - - if !is_player_one && !is_player_two { - return (false, symbol_short!("notplay")); - } - - // Check turn order - if world.game_state.is_player_one_turn && !is_player_one { - return (false, symbol_short!("notturn")); - } - if !world.game_state.is_player_one_turn && !is_player_two { - return (false, symbol_short!("notturn")); - } - - // Check if column is full - if world.board.is_column_full(&Env::default(), column) { - return (false, symbol_short!("full")); - } - - (true, symbol_short!("ok")) - } - - /// Gravity System - finds lowest empty row in column - fn gravity_system(world: &mut ECSWorldState, column: u32) -> u32 { - world - .board - .get_lowest_empty_row(&Env::default(), column) - .expect("Column should not be full - validated before calling") - } - - /// Execution System - places piece on board - fn execution_system(world: &mut ECSWorldState, row: u32, column: u32) { - let cell_value = if world.game_state.is_player_one_turn { - 1u32 - } else { - 2u32 - }; - world - .board - .set_cell(&Env::default(), row, column, cell_value); - world.game_state.move_count += 1; - world.game_state.last_move_col = Some(column); - } - - /// Win Detection System - checks for 4-in-a-row patterns - fn win_detection_system(world: &mut ECSWorldState) { - let board = &world.board; - let env = Env::default(); - - // Check all positions for a winning piece - for row in 0..ROWS { - for col in 0..COLS { - let cell = board.get_cell(&env, row, col); - if cell == 0 { - continue; - } - - // Check horizontal - if Self::check_horizontal(board, &env, row, col, cell) { - world.game_state.status = cell; - return; - } - - // Check vertical - if Self::check_vertical(board, &env, row, col, cell) { - world.game_state.status = cell; - return; - } - - // Check diagonal (bottom-left to top-right) - if Self::check_diagonal_positive(board, &env, row, col, cell) { - world.game_state.status = cell; - return; - } - - // Check diagonal (top-left to bottom-right) - if Self::check_diagonal_negative(board, &env, row, col, cell) { - world.game_state.status = cell; - return; - } - } - } - } - - /// Check horizontal connection (4 in a row) - fn check_horizontal(board: &BoardComponent, env: &Env, row: u32, col: u32, cell: u32) -> bool { - if col + 3 >= COLS { - return false; - } - - for i in 0..4 { - if board.get_cell(env, row, col + i) != cell { - return false; - } - } - true - } - - /// Check vertical connection (4 in a column) - fn check_vertical(board: &BoardComponent, env: &Env, row: u32, col: u32, cell: u32) -> bool { - if row + 3 >= ROWS { - return false; - } - - for i in 0..4 { - if board.get_cell(env, row + i, col) != cell { - return false; - } - } - true - } - - /// Check diagonal with positive slope (bottom-left to top-right) - fn check_diagonal_positive( - board: &BoardComponent, - env: &Env, - row: u32, - col: u32, - cell: u32, - ) -> bool { - if row + 3 >= ROWS || col + 3 >= COLS { - return false; - } - - for i in 0..4 { - if board.get_cell(env, row + i, col + i) != cell { - return false; - } - } - true - } - - /// Check diagonal with negative slope (top-left to bottom-right) - fn check_diagonal_negative( - board: &BoardComponent, - env: &Env, - row: u32, - col: u32, - cell: u32, - ) -> bool { - if row < 3 || col + 3 >= COLS { - return false; - } - - for i in 0..4 { - if board.get_cell(env, row - i, col + i) != cell { - return false; - } - } - true - } - - /// Draw System - detects full board with no winner - fn draw_system(world: &mut ECSWorldState) { - // Maximum moves = ROWS * COLS = 42 - if world.game_state.move_count >= ROWS * COLS && world.game_state.status == 0 { - world.game_state.status = 3; // Draw - } - } - - /// Turn System - switches turns between players - fn turn_system(world: &mut ECSWorldState) { - if world.game_state.status == 0 { - world.game_state.is_player_one_turn = !world.game_state.is_player_one_turn; - } - } - fn to_game_state(env: &Env, world: &ECSWorldState) -> GameState { let mut board = Vec::new(env); for i in 0..(ROWS * COLS) { @@ -639,6 +191,3 @@ impl ConnectFourContract { } } } - -#[cfg(test)] -mod tests; diff --git a/examples/connect_four/src/systems.rs b/examples/connect_four/src/systems.rs new file mode 100644 index 00000000..9b5412ac --- /dev/null +++ b/examples/connect_four/src/systems.rs @@ -0,0 +1,188 @@ +use soroban_sdk::{symbol_short, Address, Env, Symbol}; + +use crate::components::{BoardComponent, ECSWorldState, COLS, ROWS}; + +/// Validation System - checks if move is legal +pub(crate) fn validation_system( + world: &ECSWorldState, + player: &Address, + column: u32, +) -> (bool, Symbol) { + // Check if game is over + if world.game_state.status != 0 { + return (false, symbol_short!("gameover")); + } + + // Check column bounds + if column >= COLS { + return (false, symbol_short!("invalid")); + } + + // Check if player is registered + let is_player_one = *player == world.players.player_one; + let is_player_two = *player == world.players.player_two; + + if !is_player_one && !is_player_two { + return (false, symbol_short!("notplay")); + } + + // Check turn order + if world.game_state.is_player_one_turn && !is_player_one { + return (false, symbol_short!("notturn")); + } + if !world.game_state.is_player_one_turn && !is_player_two { + return (false, symbol_short!("notturn")); + } + + // Check if column is full + if world.board.is_column_full(&Env::default(), column) { + return (false, symbol_short!("full")); + } + + (true, symbol_short!("ok")) +} + +/// Gravity System - finds lowest empty row in column +pub(crate) fn gravity_system(world: &mut ECSWorldState, column: u32) -> u32 { + world + .board + .get_lowest_empty_row(&Env::default(), column) + .expect("Column should not be full - validated before calling") +} + +/// Execution System - places piece on board +pub(crate) fn execution_system(world: &mut ECSWorldState, row: u32, column: u32) { + let cell_value = if world.game_state.is_player_one_turn { + 1u32 + } else { + 2u32 + }; + world + .board + .set_cell(&Env::default(), row, column, cell_value); + world.game_state.move_count += 1; + world.game_state.last_move_col = Some(column); +} + +/// Win Detection System - checks for 4-in-a-row patterns +pub(crate) fn win_detection_system(world: &mut ECSWorldState) { + let board = &world.board; + let env = Env::default(); + + // Check all positions for a winning piece + for row in 0..ROWS { + for col in 0..COLS { + let cell = board.get_cell(&env, row, col); + if cell == 0 { + continue; + } + + // Check horizontal + if check_horizontal(board, &env, row, col, cell) { + world.game_state.status = cell; + return; + } + + // Check vertical + if check_vertical(board, &env, row, col, cell) { + world.game_state.status = cell; + return; + } + + // Check diagonal (bottom-left to top-right) + if check_diagonal_positive(board, &env, row, col, cell) { + world.game_state.status = cell; + return; + } + + // Check diagonal (top-left to bottom-right) + if check_diagonal_negative(board, &env, row, col, cell) { + world.game_state.status = cell; + return; + } + } + } +} + +/// Check horizontal connection (4 in a row) +fn check_horizontal(board: &BoardComponent, env: &Env, row: u32, col: u32, cell: u32) -> bool { + if col + 3 >= COLS { + return false; + } + + for i in 0..4 { + if board.get_cell(env, row, col + i) != cell { + return false; + } + } + true +} + +/// Check vertical connection (4 in a column) +fn check_vertical(board: &BoardComponent, env: &Env, row: u32, col: u32, cell: u32) -> bool { + if row + 3 >= ROWS { + return false; + } + + for i in 0..4 { + if board.get_cell(env, row + i, col) != cell { + return false; + } + } + true +} + +/// Check diagonal with positive slope (bottom-left to top-right) +fn check_diagonal_positive( + board: &BoardComponent, + env: &Env, + row: u32, + col: u32, + cell: u32, +) -> bool { + if row + 3 >= ROWS || col + 3 >= COLS { + return false; + } + + for i in 0..4 { + if board.get_cell(env, row + i, col + i) != cell { + return false; + } + } + true +} + +/// Check diagonal with negative slope (top-left to bottom-right) +fn check_diagonal_negative( + board: &BoardComponent, + env: &Env, + row: u32, + col: u32, + cell: u32, +) -> bool { + if row < 3 || col + 3 >= COLS { + return false; + } + + for i in 0..4 { + if board.get_cell(env, row - i, col + i) != cell { + return false; + } + } + true +} + +/// Draw System - detects full board with no winner +pub(crate) fn draw_system(world: &mut ECSWorldState) { + // Maximum moves = ROWS * COLS = 42 + if world.game_state.move_count >= ROWS * COLS && world.game_state.status == 0 { + world.game_state.status = 3; // Draw + } +} + +/// Turn System - switches turns between players +pub(crate) fn turn_system(world: &mut ECSWorldState) { + if world.game_state.status == 0 { + world.game_state.is_player_one_turn = !world.game_state.is_player_one_turn; + } +} diff --git a/examples/memory_match/Cargo.lock b/examples/memory_match/Cargo.lock new file mode 100644 index 00000000..a5069855 --- /dev/null +++ b/examples/memory_match/Cargo.lock @@ -0,0 +1,1934 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[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 = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[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.118", +] + +[[package]] +name = "cc" +version = "1.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_eval" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cougr-core" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc489e355664841b5fb4d0b19e014c3be1a9b81cd1cd9f7ad29f02fe54a4135" +dependencies = [ + "soroban-sdk", + "wee_alloc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "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 = "ctor" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dtor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "escape-bytes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" + +[[package]] +name = "ethnum" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if 1.0.4", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[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 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if 1.0.4", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if 1.0.4", + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "memory_match" +version = "0.1.0" +dependencies = [ + "cougr-core", + "soroban-sdk", +] + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.118", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[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", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[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_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.8.22", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7192e3a5551a7aeee90d2110b11b615798e81951fd8c8293c87ea7f88b0168f5" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "soroban-env-common" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc49a80a68fc1005847308e63b9fce39874de731940b1807b721d472de3ff01" +dependencies = [ + "arbitrary", + "crate-git-revision", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr", + "wasmparser", +] + +[[package]] +name = "soroban-env-guest" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2334ba1cfe0a170ab744d96db0b4ca86934de9ff68187ceebc09dc342def55" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43af5d53c57bc2f546e122adc0b1cca6f93942c718977379aa19ddd04f06fcec" +dependencies = [ + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "curve25519-dalek", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "generic-array", + "getrandom", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "p256", + "rand", + "rand_chacha", + "sec1", + "sha2", + "sha3", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", + "static_assertions", + "stellar-strkey 0.0.13", + "wasmparser", +] + +[[package]] +name = "soroban-env-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a989167512e3592d455b1e204d703cfe578a36672a77ed2f9e6f7e1bbfd9cc5c" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr", + "syn 2.0.118", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ca06e6c5029d1285e66219cb387a234224e26969ce8ad2bc2d5017e9395d63b" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common", + "soroban-env-host", + "thiserror", +] + +[[package]] +name = "soroban-sdk" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4502f2e018f238a4c5d3212d7d20ea6abcdc6e58babd63b642b693739db30fd1" +dependencies = [ + "arbitrary", + "bytes-lit", + "crate-git-revision", + "ctor", + "derive_arbitrary", + "ed25519-dalek", + "rand", + "rustc_version", + "serde", + "serde_json", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey 0.0.16", + "visibility", +] + +[[package]] +name = "soroban-sdk-macros" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca03e9cf61d241cb9afdd6ddf41f6c25698b3f566a875e7009ea799b89e2bf0a" +dependencies = [ + "darling 0.20.11", + "heck", + "itertools", + "macro-string", + "proc-macro2", + "quote", + "sha2", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", + "syn 2.0.118", +] + +[[package]] +name = "soroban-spec" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa02e07f507cc27406ae0834db4dcf309b78c4cc8776eb3b2d662d66e8859d25" +dependencies = [ + "base64", + "sha2", + "stellar-xdr", + "thiserror", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6835bb510763ef3fa5405e89036e3c8ea6ef5abe55fc52cfe9ac0e38be9d531c" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2", + "soroban-spec", + "stellar-xdr", + "syn 2.0.118", + "thiserror", +] + +[[package]] +name = "soroban-wasmi" +version = "0.31.1-soroban.20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" +dependencies = [ + "smallvec", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[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.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1832fb50c651ad10f734aaf5d31ca5acdfb197a6ecda64d93fcdb8885af913" +dependencies = [ + "crate-git-revision", + "data-encoding", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084afcb0d458c3d5d5baa2d294b18f881e62cc258ef539d8fdf68be7dbe45520" +dependencies = [ + "crate-git-revision", + "data-encoding", + "heapless", +] + +[[package]] +name = "stellar-xdr" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d20dafed80076b227d4b17c0c508a4bbc4d5e4c3d4c1de7cd42242df4b1eaf" +dependencies = [ + "arbitrary", + "base64", + "cfg_eval", + "crate-git-revision", + "escape-bytes", + "ethnum", + "hex", + "serde", + "serde_with", + "sha2", + "stellar-strkey 0.0.13", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[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.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "time" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.118", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmi_arena" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "wasmparser" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" +dependencies = [ + "indexmap 2.14.0", + "semver", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[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-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/examples/memory_match/README.md b/examples/memory_match/README.md index ea55bab8..103e4134 100644 --- a/examples/memory_match/README.md +++ b/examples/memory_match/README.md @@ -1,193 +1,97 @@ -# Memory Match Contract +# Memory Match -A memory match card game implemented as a Soroban smart contract using the Cougr ECS framework. +> **Transitional example**: This example uses an older Cougr pattern and is preserved +> for compatibility reference. For the current recommended approach, see `snake`. -## Overview +An on-chain Memory Match card game built with the [Cougr](../../README.md) ECS framework on Stellar Soroban. -The Memory Match contract implements a classic memory card game where players flip cards to find matching pairs. The game features: +## Purpose and pattern -- 16 cards arranged in a 4x4 grid (8 matching pairs) -- Turn-based gameplay with card revealing mechanics -- Match detection and automatic card hiding for non-matches -- Game state tracking and reset functionality -- Player authorization to prevent unauthorized access +This example demonstrates a single-player, turn-based reveal-and-match game where pairs of +cards are flipped and compared against each other rather than against fixed rules. It +showcases Cougr's `ComponentTrait` pattern for typed, byte-serializable game state: cards, +the board, and game progress are each modeled as a `#[contracttype]` struct with manual +`serialize`/`deserialize`, rather than being scanned through `SimpleWorld`/`SimpleQueryBuilder`. +This is a lighter-weight integration suited to a single fixed-shape game state (16 cards, one +board, one player) with no dynamic entity population. -## Architecture +## Public contract API -The contract is built using the Cougr ECS (Entity Component System) framework with the following components: +| Function | Parameters | Returns | Description | +|---|---|---|---| +| `init_game` | `player: Address` | `GameState` | Seeds a fresh 16-card board (8 deterministic pairs) for `player` and returns the initial state. | +| `reveal_card` | `player: Address`, `position: u32` | `RevealInfo` | Reveals the card at `position`. Resolves a pair once two cards are revealed (match, no-match, or game-over). Panics if not initialized, the caller isn't the registered player, the game is over, two cards are already revealed, the position is out of range, or the card is already revealed/matched. | +| `get_game_state` | — | `GameState` | Current board view, revealed/matched counts, move count, and game-over flag. | +| `reset_game` | `player: Address` | `GameState` | Hides all cards and clears progress, keeping the same board layout. Panics if not initialized or the caller isn't the registered player. | -### Components +## Architecture overview -- **CardComponent**: Represents individual cards with their state (Hidden, Revealed, Matched) and value -- **BoardComponent**: Manages the game board, tracking revealed cards and matched pairs -- **GameStateComponent**: Tracks overall game state including player, moves, and game over status - -### World State - -The `ECSWorldState` struct encapsulates all game components and provides methods for: -- Card lookup and state updates -- Game state management -- Serialization/deserialization for contract storage - -## Contract Functions - -### Core Functions - -- `init_game(env: Env, player: Address) -> GameState` - - Initializes a new game with the specified player - - Returns the initial game state - -- `reveal_card(env: Env, player: Address, position: u32) -> RevealInfo` - - Reveals a card at the specified position - - Handles match detection and game logic - - Returns information about the reveal operation - -- `get_game_state(env: Env) -> GameState` - - Returns the current game state - - Useful for UI updates and state queries - -- `reset_game(env: Env, player: Address) -> GameState` - - Resets the game to initial state - - Only authorized players can reset - -### Game Rules - -1. Players can only reveal 2 cards at a time -2. After revealing 2 cards, they are automatically processed: - - If matching: cards remain revealed and marked as matched - - If not matching: cards are hidden again -3. The game ends when all 8 pairs are found -4. Only the initializing player can make moves or reset the game - -## Card Layout - -The game uses a deterministic card layout for testing and consistency: +There is no `GameApp` tick loop — `reveal_card` runs a fixed pipeline synchronously on each +invocation: ``` -Positions: 0 1 2 3 4 5 6 7 -Values: 0 1 2 3 4 5 6 7 - -Positions: 8 9 10 11 12 13 14 15 -Values: 0 1 2 3 4 5 6 7 -``` - -Cards at positions (0,8), (1,9), (2,10), etc., form matching pairs. - -## Building and Testing - -### Prerequisites - -- Rust toolchain -- Soroban CLI tools -- Cougr framework dependencies - -### Build - -```bash -cargo build +reveal_card + └─ ValidationSystem → checks caller, game-over state, two-card limit, position bounds + └─ RevealSystem → flips the card to Revealed, increments the move counter + └─ (when 2 cards are revealed) + └─ PairResolutionSystem → compares values; marks Matched + checks for game-over, + or hides both cards again on a mismatch ``` -### Run Tests +`reset_game` runs a separate `ResetSystem` that rehides every card and zeroes progress +counters without regenerating the board layout. `components.rs` defines the data +(`CardComponent`, `BoardComponent`, `GameStateComponent`, the `ECSWorldState` aggregate, and +the public `GameState`/`RevealInfo`/`RevealResult` types); `systems.rs` holds the pure +validation, reveal, resolution, reset, and projection functions; `lib.rs` owns the single +`ECSWorldState` and is the only module that touches contract storage. + +## Storage model + +The entire game lives under one instance-storage key (`WORLD_KEY`, `symbol_short!("WORLD")`) +as a single `ECSWorldState` struct bundling all 16 card components, the board component, and +the game-state component. Instance storage is used — not persistent or temporary — because +there is exactly one game per contract instance and the state must survive for the lifetime +of that instance with no per-entry TTL management. + +## Main gameplay flow + +1. Deployer calls `init_game(player)`; 16 cards are created with a deterministic layout + (positions 0–7 and 8–15 hold matching values 0–7), all starting `Hidden`. +2. Player calls `reveal_card(player, position)` for a hidden card; it flips to `Revealed` + and the move counter increments. +3. Once two cards are revealed, the contract compares their values: + - **Match**: both cards become `Matched`, `matched_pairs` increments, and reveals are + re-enabled. If all 8 pairs are now matched, the game is marked over. + - **No match**: both cards flip back to `Hidden` and reveals are re-enabled. +4. Player repeats step 2–3 until `matched_pairs` reaches 8 and `game_over` is `true`. +5. Player (or anyone reading state) calls `get_game_state` at any point to read the board, + or calls `reset_game` to rehide all cards and start over on the same layout. + +## Cougr APIs used + +- `cougr_core::component::ComponentTrait` — gives each component (`CardComponent`, + `BoardComponent`, `GameStateComponent`) a `component_type()` symbol and explicit byte-level + `serialize`/`deserialize`. This is the only Cougr API the example uses: the game has one + fixed-shape `ECSWorldState` per contract instance (16 cards, one board, one player) rather + than a dynamic entity population, so `SimpleWorld`/`SimpleQueryBuilder` scanning would add + overhead with no benefit. There is a single decision point per call (reveal or reset), so + `GameApp`/`ScheduleStage` staged ticking is not needed either, and the game has no hidden + information, multi-device auth, or proof-verification requirements that would call for + `auth`, `privacy::stable`, `privacy::experimental`, or `ops` standards. + +## Build and test commands ```bash cargo test +stellar contract build ``` -### Test Coverage - -The contract includes comprehensive tests covering: - -- Game initialization -- Card revealing mechanics -- Match detection -- Non-match handling -- Game reset functionality -- Error conditions (unauthorized access, invalid positions, etc.) -- Edge cases and game completion - -## Usage Example - -```rust -use soroban_sdk::{Env, Address}; -use memory_match::MemoryMatchContractClient; - -// Initialize environment and contract -let env = Env::default(); -let contract_id = env.register(MemoryMatchContract, ()); -let client = MemoryMatchContractClient::new(&env, &contract_id); - -// Create player and initialize game -let player = Address::generate(&env); -let game_state = client.init_game(&player); - -// Reveal first card -let reveal_info = client.reveal_card(&player, &0); -println!("Card value: {}", reveal_info.value); - -// Reveal matching card -let match_info = client.reveal_card(&player, &8); -assert!(matches!(match_info.result, RevealResult::MatchFound)); -``` - -## Data Structures - -### GameState - -```rust -pub struct GameState { - pub board_state: Vec, // 0=Hidden, 1-8=Revealed, 9=Matched - pub revealed_count: u32, - pub matched_pairs: u32, - pub total_pairs: u32, - pub moves_count: u32, - pub game_over: bool, -} -``` - -### RevealInfo - -```rust -pub struct RevealInfo { - pub result: RevealResult, - pub position: u32, - pub value: u32, - pub positions: Vec, -} -``` - -### RevealResult - -```rust -pub enum RevealResult { - CardRevealed, - MatchFound, - NoMatch, - GameOver, -} -``` - -## Storage - -The contract uses Soroban's instance storage to persist the `ECSWorldState`. The state is stored under a fixed key and includes all game components, allowing the game to be resumed across contract invocations. - -## Security Considerations - -- Player authorization ensures only the game creator can make moves -- Input validation prevents invalid card positions -- State consistency is maintained through atomic updates -- No external dependencies or privileged operations - -## Future Enhancements - -Potential improvements for future versions: - -- Multiple game modes (different grid sizes, card counts) -- Score tracking and leaderboards -- Time limits or move counters -- Multiplayer support -- Card shuffling for random layouts -- Visual themes and customization - -## License +## Known limitations -This contract is part of the Cougr framework examples and follows the same licensing terms. +- Does not use `GameApp`, `ScheduleStage`, or `SimpleWorld` — game logic is invoked directly + from contract entrypoints rather than through a tick-based scheduler, since a single-player + game with one decision point per call does not need staged scheduling. +- The card layout is fixed and deterministic (not shuffled), so the pairing is the same for + every game; randomized layouts are out of scope for this example. +- `reset_game` reuses the same fixed layout rather than generating a new one. +- No timeout/forfeit mechanism, scoring across multiple games, or multiplayer support. diff --git a/examples/memory_match/src/components.rs b/examples/memory_match/src/components.rs new file mode 100644 index 00000000..d2ecebc5 --- /dev/null +++ b/examples/memory_match/src/components.rs @@ -0,0 +1,448 @@ +#![allow(dead_code)] + +use cougr_core::component::ComponentTrait; +use soroban_sdk::{contracttype, symbol_short, Address, Bytes, Env, Symbol, Vec}; + +// ── Constants ──────────────────────────────────────────────────────────────── + +pub(crate) const WORLD_KEY: Symbol = symbol_short!("WORLD"); + +// ── Card states ────────────────────────────────────────────────────────────── + +#[derive(Clone, Debug, Eq, PartialEq, Copy)] +#[contracttype] +pub enum CardState { + Hidden = 0, + Revealed = 1, + Matched = 2, +} + +// ── Components ─────────────────────────────────────────────────────────────── + +// Card component representing a single card +#[contracttype] +#[derive(Clone, Debug)] +pub struct CardComponent { + pub card_id: u32, + pub value: u32, // 0-7 for 8 pairs (16 cards total) + pub state: CardState, + pub position: u32, // 0-15 board position + pub entity_id: u32, +} + +impl CardComponent { + pub fn new(card_id: u32, value: u32, position: u32, entity_id: u32) -> Self { + Self { + card_id, + value, + state: CardState::Hidden, + position, + entity_id, + } + } +} + +impl ComponentTrait for CardComponent { + fn component_type() -> Symbol { + symbol_short!("card") + } + + fn serialize(&self, env: &Env) -> Bytes { + let mut bytes = Bytes::new(env); + bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); + bytes.append(&Bytes::from_array(env, &self.card_id.to_be_bytes())); + bytes.append(&Bytes::from_array(env, &self.value.to_be_bytes())); + bytes.append(&Bytes::from_array(env, &(self.state as u32).to_be_bytes())); + bytes.append(&Bytes::from_array(env, &self.position.to_be_bytes())); + bytes + } + + fn deserialize(_env: &Env, data: &Bytes) -> Option { + if data.len() != 20 { + return None; + } + + let entity_id = u32::from_be_bytes([ + data.get(0).unwrap(), + data.get(1).unwrap(), + data.get(2).unwrap(), + data.get(3).unwrap(), + ]); + + let card_id = u32::from_be_bytes([ + data.get(4).unwrap(), + data.get(5).unwrap(), + data.get(6).unwrap(), + data.get(7).unwrap(), + ]); + + let value = u32::from_be_bytes([ + data.get(8).unwrap(), + data.get(9).unwrap(), + data.get(10).unwrap(), + data.get(11).unwrap(), + ]); + + let state = u32::from_be_bytes([ + data.get(12).unwrap(), + data.get(13).unwrap(), + data.get(14).unwrap(), + data.get(15).unwrap(), + ]); + + let position = u32::from_be_bytes([ + data.get(16).unwrap(), + data.get(17).unwrap(), + data.get(18).unwrap(), + data.get(19).unwrap(), + ]); + + Some(CardComponent { + card_id, + value, + state: match state { + 0 => CardState::Hidden, + 1 => CardState::Revealed, + 2 => CardState::Matched, + _ => return None, + }, + position, + entity_id, + }) + } +} + +// Board component managing the game board +#[contracttype] +#[derive(Clone, Debug)] +pub struct BoardComponent { + pub cards: Vec, // Card entity IDs + pub revealed_cards: Vec, // Currently revealed card positions + pub matched_pairs: u32, + pub total_pairs: u32, + pub entity_id: u32, +} + +impl BoardComponent { + pub fn new(env: &Env, entity_id: u32) -> Self { + let mut cards = Vec::new(env); + for i in 0..16 { + cards.push_back(i); + } + + Self { + cards, + revealed_cards: Vec::new(env), + matched_pairs: 0, + total_pairs: 8, + entity_id, + } + } +} + +impl ComponentTrait for BoardComponent { + fn component_type() -> Symbol { + symbol_short!("board") + } + + fn serialize(&self, env: &Env) -> Bytes { + let mut bytes = Bytes::new(env); + bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); + bytes.append(&Bytes::from_array(env, &self.matched_pairs.to_be_bytes())); + bytes.append(&Bytes::from_array(env, &self.total_pairs.to_be_bytes())); + + // Serialize cards vector + bytes.append(&Bytes::from_array(env, &self.cards.len().to_be_bytes())); + for i in 0..self.cards.len() { + let card_id = self.cards.get(i).unwrap(); + bytes.append(&Bytes::from_array(env, &card_id.to_be_bytes())); + } + + // Serialize revealed_cards vector + bytes.append(&Bytes::from_array( + env, + &self.revealed_cards.len().to_be_bytes(), + )); + for i in 0..self.revealed_cards.len() { + let pos = self.revealed_cards.get(i).unwrap(); + bytes.append(&Bytes::from_array(env, &pos.to_be_bytes())); + } + + bytes + } + + fn deserialize(env: &Env, data: &Bytes) -> Option { + if data.len() < 12 { + return None; + } + + let entity_id = u32::from_be_bytes([ + data.get(0).unwrap(), + data.get(1).unwrap(), + data.get(2).unwrap(), + data.get(3).unwrap(), + ]); + + let matched_pairs = u32::from_be_bytes([ + data.get(4).unwrap(), + data.get(5).unwrap(), + data.get(6).unwrap(), + data.get(7).unwrap(), + ]); + + let total_pairs = u32::from_be_bytes([ + data.get(8).unwrap(), + data.get(9).unwrap(), + data.get(10).unwrap(), + data.get(11).unwrap(), + ]); + + let mut offset = 12; + + // Deserialize cards + let cards_len = u32::from_be_bytes([ + data.get(offset).unwrap(), + data.get(offset + 1).unwrap(), + data.get(offset + 2).unwrap(), + data.get(offset + 3).unwrap(), + ]); + offset += 4; + + let mut cards = Vec::new(env); + for _ in 0..cards_len { + let card_id = u32::from_be_bytes([ + data.get(offset).unwrap(), + data.get(offset + 1).unwrap(), + data.get(offset + 2).unwrap(), + data.get(offset + 3).unwrap(), + ]); + cards.push_back(card_id); + offset += 4; + } + + // Deserialize revealed_cards + let revealed_len = u32::from_be_bytes([ + data.get(offset).unwrap(), + data.get(offset + 1).unwrap(), + data.get(offset + 2).unwrap(), + data.get(offset + 3).unwrap(), + ]); + offset += 4; + + let mut revealed_cards = Vec::new(env); + for _ in 0..revealed_len { + let pos = u32::from_be_bytes([ + data.get(offset).unwrap(), + data.get(offset + 1).unwrap(), + data.get(offset + 2).unwrap(), + data.get(offset + 3).unwrap(), + ]); + revealed_cards.push_back(pos); + offset += 4; + } + + Some(BoardComponent { + cards, + revealed_cards, + matched_pairs, + total_pairs, + entity_id, + }) + } +} + +// Game state component +#[contracttype] +#[derive(Clone, Debug)] +pub struct GameStateComponent { + pub player: Address, + pub moves_count: u32, + pub game_over: bool, + pub can_reveal: bool, // Can reveal a card (true if 0 or 1 cards revealed) + pub entity_id: u32, +} + +impl GameStateComponent { + pub fn new(player: Address, entity_id: u32) -> Self { + Self { + player, + moves_count: 0, + game_over: false, + can_reveal: true, + entity_id, + } + } +} + +impl ComponentTrait for GameStateComponent { + fn component_type() -> Symbol { + symbol_short!("gamestate") + } + + fn serialize(&self, env: &Env) -> Bytes { + let mut bytes = Bytes::new(env); + bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); + bytes.append(&self.player.to_string().to_bytes()); + bytes.append(&Bytes::from_array(env, &self.moves_count.to_be_bytes())); + bytes.append(&Bytes::from_array( + env, + &(if self.game_over { 1u32 } else { 0u32 }).to_be_bytes(), + )); + bytes.append(&Bytes::from_array( + env, + &(if self.can_reveal { 1u32 } else { 0u32 }).to_be_bytes(), + )); + bytes + } + + fn deserialize(_env: &Env, data: &Bytes) -> Option { + if data.len() < 48 { + return None; + } + + let entity_id = u32::from_be_bytes([ + data.get(0).unwrap(), + data.get(1).unwrap(), + data.get(2).unwrap(), + data.get(3).unwrap(), + ]); + + let player_bytes = data.slice(4..36); + let player = Address::from_string_bytes(&player_bytes); + + let moves_count = u32::from_be_bytes([ + data.get(36).unwrap(), + data.get(37).unwrap(), + data.get(38).unwrap(), + data.get(39).unwrap(), + ]); + + let game_over = u32::from_be_bytes([ + data.get(40).unwrap(), + data.get(41).unwrap(), + data.get(42).unwrap(), + data.get(43).unwrap(), + ]) == 1; + + let can_reveal = u32::from_be_bytes([ + data.get(44).unwrap(), + data.get(45).unwrap(), + data.get(46).unwrap(), + data.get(47).unwrap(), + ]) == 1; + + Some(GameStateComponent { + player, + moves_count, + game_over, + can_reveal, + entity_id, + }) + } +} + +// ── ECS World ──────────────────────────────────────────────────────────────── + +/// ECS World State - stores all game entities and components +#[contracttype] +#[derive(Clone, Debug)] +pub struct ECSWorldState { + pub cards: Vec, + pub board: BoardComponent, + pub game_state: GameStateComponent, +} + +impl ECSWorldState { + pub fn new(env: &Env, player: Address) -> Self { + // Create card components + let mut cards = Vec::new(env); + for i in 0..16 { + let value = match i { + 0 => 0, + 1 => 1, + 2 => 2, + 3 => 3, + 4 => 4, + 5 => 5, + 6 => 6, + 7 => 7, + 8 => 0, + 9 => 1, + 10 => 2, + 11 => 3, + 12 => 4, + 13 => 5, + 14 => 6, + 15 => 7, + _ => 0, + }; + cards.push_back(CardComponent::new(i, value, i, i)); + } + + // Create board + let board = BoardComponent::new(env, 16); + + // Create game state + let game_state = GameStateComponent::new(player, 17); + + Self { + cards, + board, + game_state, + } + } + + pub fn get_card(&self, entity_id: u32) -> Option { + for i in 0..self.cards.len() { + let card = self.cards.get(i).unwrap(); + if card.entity_id == entity_id { + return Some(card.clone()); + } + } + None + } + + pub fn update_card(&mut self, entity_id: u32, new_state: CardState) { + let mut new_cards = Vec::new(self.cards.env()); + for i in 0..self.cards.len() { + let mut card = self.cards.get(i).unwrap().clone(); + if card.entity_id == entity_id { + card.state = new_state; + } + new_cards.push_back(card); + } + self.cards = new_cards; + } +} + +// ── Public API types ───────────────────────────────────────────────────────── + +#[contracttype] +#[derive(Clone, Debug)] +pub struct GameState { + pub board_state: Vec, // 0=Hidden, 1-8=Revealed values, 9=Matched + pub revealed_count: u32, + pub matched_pairs: u32, + pub total_pairs: u32, + pub moves_count: u32, + pub game_over: bool, +} + +#[contracttype] +#[derive(Clone, Debug)] +pub enum RevealResult { + Success = 0, + CardRevealed = 1, + MatchFound = 2, + NoMatch = 3, + GameOver = 4, +} + +#[contracttype] +#[derive(Clone, Debug)] +pub struct RevealInfo { + pub result: RevealResult, + pub position: u32, + pub value: u32, + pub positions: Vec, +} diff --git a/examples/memory_match/src/lib.rs b/examples/memory_match/src/lib.rs index 55ec6de5..2a0c9d01 100644 --- a/examples/memory_match/src/lib.rs +++ b/examples/memory_match/src/lib.rs @@ -1,448 +1,14 @@ #![no_std] -use cougr_core::component::ComponentTrait; -use soroban_sdk::{ - contract, contractimpl, contracttype, symbol_short, Address, Bytes, Env, Symbol, Vec, -}; - +mod components; +mod systems; #[cfg(test)] mod test; -// Card states -#[derive(Clone, Debug, Eq, PartialEq, Copy)] -#[contracttype] -pub enum CardState { - Hidden = 0, - Revealed = 1, - Matched = 2, -} - -// Card component representing a single card -#[contracttype] -#[derive(Clone, Debug)] -pub struct CardComponent { - pub card_id: u32, - pub value: u32, // 0-7 for 8 pairs (16 cards total) - pub state: CardState, - pub position: u32, // 0-15 board position - pub entity_id: u32, -} - -impl CardComponent { - pub fn new(card_id: u32, value: u32, position: u32, entity_id: u32) -> Self { - Self { - card_id, - value, - state: CardState::Hidden, - position, - entity_id, - } - } -} - -impl ComponentTrait for CardComponent { - fn component_type() -> Symbol { - symbol_short!("card") - } - - fn serialize(&self, env: &Env) -> Bytes { - let mut bytes = Bytes::new(env); - bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); - bytes.append(&Bytes::from_array(env, &self.card_id.to_be_bytes())); - bytes.append(&Bytes::from_array(env, &self.value.to_be_bytes())); - bytes.append(&Bytes::from_array(env, &(self.state as u32).to_be_bytes())); - bytes.append(&Bytes::from_array(env, &self.position.to_be_bytes())); - bytes - } - - fn deserialize(_env: &Env, data: &Bytes) -> Option { - if data.len() != 20 { - return None; - } - - let entity_id = u32::from_be_bytes([ - data.get(0).unwrap(), - data.get(1).unwrap(), - data.get(2).unwrap(), - data.get(3).unwrap(), - ]); - - let card_id = u32::from_be_bytes([ - data.get(4).unwrap(), - data.get(5).unwrap(), - data.get(6).unwrap(), - data.get(7).unwrap(), - ]); - - let value = u32::from_be_bytes([ - data.get(8).unwrap(), - data.get(9).unwrap(), - data.get(10).unwrap(), - data.get(11).unwrap(), - ]); - - let state = u32::from_be_bytes([ - data.get(12).unwrap(), - data.get(13).unwrap(), - data.get(14).unwrap(), - data.get(15).unwrap(), - ]); - - let position = u32::from_be_bytes([ - data.get(16).unwrap(), - data.get(17).unwrap(), - data.get(18).unwrap(), - data.get(19).unwrap(), - ]); - - Some(CardComponent { - card_id, - value, - state: match state { - 0 => CardState::Hidden, - 1 => CardState::Revealed, - 2 => CardState::Matched, - _ => return None, - }, - position, - entity_id, - }) - } -} - -// Board component managing the game board -#[contracttype] -#[derive(Clone, Debug)] -pub struct BoardComponent { - pub cards: Vec, // Card entity IDs - pub revealed_cards: Vec, // Currently revealed card positions - pub matched_pairs: u32, - pub total_pairs: u32, - pub entity_id: u32, -} - -impl BoardComponent { - pub fn new(env: &Env, entity_id: u32) -> Self { - let mut cards = Vec::new(env); - for i in 0..16 { - cards.push_back(i); - } - - Self { - cards, - revealed_cards: Vec::new(env), - matched_pairs: 0, - total_pairs: 8, - entity_id, - } - } -} - -impl ComponentTrait for BoardComponent { - fn component_type() -> Symbol { - symbol_short!("board") - } - - fn serialize(&self, env: &Env) -> Bytes { - let mut bytes = Bytes::new(env); - bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); - bytes.append(&Bytes::from_array(env, &self.matched_pairs.to_be_bytes())); - bytes.append(&Bytes::from_array(env, &self.total_pairs.to_be_bytes())); - - // Serialize cards vector - bytes.append(&Bytes::from_array(env, &self.cards.len().to_be_bytes())); - for i in 0..self.cards.len() { - let card_id = self.cards.get(i).unwrap(); - bytes.append(&Bytes::from_array(env, &card_id.to_be_bytes())); - } - - // Serialize revealed_cards vector - bytes.append(&Bytes::from_array( - env, - &self.revealed_cards.len().to_be_bytes(), - )); - for i in 0..self.revealed_cards.len() { - let pos = self.revealed_cards.get(i).unwrap(); - bytes.append(&Bytes::from_array(env, &pos.to_be_bytes())); - } - - bytes - } - - fn deserialize(env: &Env, data: &Bytes) -> Option { - if data.len() < 12 { - return None; - } - - let entity_id = u32::from_be_bytes([ - data.get(0).unwrap(), - data.get(1).unwrap(), - data.get(2).unwrap(), - data.get(3).unwrap(), - ]); - - let matched_pairs = u32::from_be_bytes([ - data.get(4).unwrap(), - data.get(5).unwrap(), - data.get(6).unwrap(), - data.get(7).unwrap(), - ]); - - let total_pairs = u32::from_be_bytes([ - data.get(8).unwrap(), - data.get(9).unwrap(), - data.get(10).unwrap(), - data.get(11).unwrap(), - ]); - - let mut offset = 12; - - // Deserialize cards - let cards_len = u32::from_be_bytes([ - data.get(offset).unwrap(), - data.get(offset + 1).unwrap(), - data.get(offset + 2).unwrap(), - data.get(offset + 3).unwrap(), - ]); - offset += 4; - - let mut cards = Vec::new(env); - for _ in 0..cards_len { - let card_id = u32::from_be_bytes([ - data.get(offset).unwrap(), - data.get(offset + 1).unwrap(), - data.get(offset + 2).unwrap(), - data.get(offset + 3).unwrap(), - ]); - cards.push_back(card_id); - offset += 4; - } - - // Deserialize revealed_cards - let revealed_len = u32::from_be_bytes([ - data.get(offset).unwrap(), - data.get(offset + 1).unwrap(), - data.get(offset + 2).unwrap(), - data.get(offset + 3).unwrap(), - ]); - offset += 4; - - let mut revealed_cards = Vec::new(env); - for _ in 0..revealed_len { - let pos = u32::from_be_bytes([ - data.get(offset).unwrap(), - data.get(offset + 1).unwrap(), - data.get(offset + 2).unwrap(), - data.get(offset + 3).unwrap(), - ]); - revealed_cards.push_back(pos); - offset += 4; - } - - Some(BoardComponent { - cards, - revealed_cards, - matched_pairs, - total_pairs, - entity_id, - }) - } -} - -// Game state component -#[contracttype] -#[derive(Clone, Debug)] -pub struct GameStateComponent { - pub player: Address, - pub moves_count: u32, - pub game_over: bool, - pub can_reveal: bool, // Can reveal a card (true if 0 or 1 cards revealed) - pub entity_id: u32, -} - -impl GameStateComponent { - pub fn new(player: Address, entity_id: u32) -> Self { - Self { - player, - moves_count: 0, - game_over: false, - can_reveal: true, - entity_id, - } - } -} - -impl ComponentTrait for GameStateComponent { - fn component_type() -> Symbol { - symbol_short!("gamestate") - } - - fn serialize(&self, env: &Env) -> Bytes { - let mut bytes = Bytes::new(env); - bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); - bytes.append(&self.player.to_string().to_bytes()); - bytes.append(&Bytes::from_array(env, &self.moves_count.to_be_bytes())); - bytes.append(&Bytes::from_array( - env, - &(if self.game_over { 1u32 } else { 0u32 }).to_be_bytes(), - )); - bytes.append(&Bytes::from_array( - env, - &(if self.can_reveal { 1u32 } else { 0u32 }).to_be_bytes(), - )); - bytes - } +use components::{ECSWorldState, GameState, RevealInfo, WORLD_KEY}; +use systems::{reset_system, reveal_card_system, to_game_state, validate_player, validate_reveal}; - fn deserialize(_env: &Env, data: &Bytes) -> Option { - if data.len() < 48 { - return None; - } - - let entity_id = u32::from_be_bytes([ - data.get(0).unwrap(), - data.get(1).unwrap(), - data.get(2).unwrap(), - data.get(3).unwrap(), - ]); - - let player_bytes = data.slice(4..36); - let player = Address::from_string_bytes(&player_bytes); - - let moves_count = u32::from_be_bytes([ - data.get(36).unwrap(), - data.get(37).unwrap(), - data.get(38).unwrap(), - data.get(39).unwrap(), - ]); - - let game_over = u32::from_be_bytes([ - data.get(40).unwrap(), - data.get(41).unwrap(), - data.get(42).unwrap(), - data.get(43).unwrap(), - ]) == 1; - - let can_reveal = u32::from_be_bytes([ - data.get(44).unwrap(), - data.get(45).unwrap(), - data.get(46).unwrap(), - data.get(47).unwrap(), - ]) == 1; - - Some(GameStateComponent { - player, - moves_count, - game_over, - can_reveal, - entity_id, - }) - } -} - -/// ECS World State - stores all game entities and components -#[contracttype] -#[derive(Clone, Debug)] -pub struct ECSWorldState { - pub cards: Vec, - pub board: BoardComponent, - pub game_state: GameStateComponent, -} - -impl ECSWorldState { - pub fn new(env: &Env, player: Address) -> Self { - // Create card components - let mut cards = Vec::new(env); - for i in 0..16 { - let value = match i { - 0 => 0, - 1 => 1, - 2 => 2, - 3 => 3, - 4 => 4, - 5 => 5, - 6 => 6, - 7 => 7, - 8 => 0, - 9 => 1, - 10 => 2, - 11 => 3, - 12 => 4, - 13 => 5, - 14 => 6, - 15 => 7, - _ => 0, - }; - cards.push_back(CardComponent::new(i, value, i, i)); - } - - // Create board - let board = BoardComponent::new(env, 16); - - // Create game state - let game_state = GameStateComponent::new(player, 17); - - Self { - cards, - board, - game_state, - } - } - - pub fn get_card(&self, entity_id: u32) -> Option { - for i in 0..self.cards.len() { - let card = self.cards.get(i).unwrap(); - if card.entity_id == entity_id { - return Some(card.clone()); - } - } - None - } - - pub fn update_card(&mut self, entity_id: u32, new_state: CardState) { - let mut new_cards = Vec::new(self.cards.env()); - for i in 0..self.cards.len() { - let mut card = self.cards.get(i).unwrap().clone(); - if card.entity_id == entity_id { - card.state = new_state; - } - new_cards.push_back(card); - } - self.cards = new_cards; - } -} - -// Contract types -#[contracttype] -#[derive(Clone, Debug)] -pub struct GameState { - pub board_state: Vec, // 0=Hidden, 1-8=Revealed values, 9=Matched - pub revealed_count: u32, - pub matched_pairs: u32, - pub total_pairs: u32, - pub moves_count: u32, - pub game_over: bool, -} - -#[contracttype] -#[derive(Clone, Debug)] -pub enum RevealResult { - Success = 0, - CardRevealed = 1, - MatchFound = 2, - NoMatch = 3, - GameOver = 4, -} - -#[contracttype] -#[derive(Clone, Debug)] -pub struct RevealInfo { - pub result: RevealResult, - pub position: u32, - pub value: u32, - pub positions: Vec, -} - -const WORLD_KEY: Symbol = symbol_short!("WORLD"); +use soroban_sdk::{contract, contractimpl, Address, Env}; // Main contract #[contract] @@ -453,7 +19,7 @@ impl MemoryMatchContract { pub fn init_game(env: Env, player: Address) -> GameState { let world_state = ECSWorldState::new(&env, player); env.storage().instance().set(&WORLD_KEY, &world_state); - Self::to_game_state(&env, &world_state) + to_game_state(&env, &world_state) } pub fn reveal_card(env: Env, player: Address, position: u32) -> RevealInfo { @@ -463,119 +29,9 @@ impl MemoryMatchContract { .get(&WORLD_KEY) .unwrap_or_else(|| panic!("Game not initialized")); - // Validate player - if world_state.game_state.player != player { - panic!("Not authorized player"); - } + validate_reveal(&world_state, &player, position); - // Validate reveal - if world_state.game_state.game_over { - panic!("Game is over"); - } - - if !world_state.game_state.can_reveal { - panic!("Cannot reveal more than 2 cards"); - } - - if position >= world_state.board.cards.len() { - panic!("Invalid position"); - } - - // Get card and check state - let card_entity_id = world_state.board.cards.get(position).unwrap(); - let card_value = { - let card = world_state.get_card(card_entity_id).unwrap(); - - if matches!(card.state, CardState::Revealed | CardState::Matched) { - panic!("Card already revealed or matched"); - } - - card.value - }; - - // Reveal the card - world_state.update_card(card_entity_id, CardState::Revealed); - - // Add to revealed cards - world_state.board.revealed_cards.push_back(position); - - // Update game state - world_state.game_state.moves_count += 1; - - // If 2 cards are revealed, disable further reveals - if world_state.board.revealed_cards.len() == 2 { - world_state.game_state.can_reveal = false; - } - - // Check if we have 2 cards revealed - let result = if world_state.board.revealed_cards.len() == 2 { - let pos1 = world_state.board.revealed_cards.get(0).unwrap(); - let pos2 = world_state.board.revealed_cards.get(1).unwrap(); - - let card1_entity_id = world_state.board.cards.get(pos1).unwrap(); - let card2_entity_id = world_state.board.cards.get(pos2).unwrap(); - - let card1_value = world_state.get_card(card1_entity_id).unwrap().value; - let card2_value = world_state.get_card(card2_entity_id).unwrap().value; - - let is_match = card1_value == card2_value; - let mut positions = Vec::new(&env); - positions.push_back(pos1); - positions.push_back(pos2); - - if is_match { - // Mark cards as matched - world_state.update_card(card1_entity_id, CardState::Matched); - world_state.update_card(card2_entity_id, CardState::Matched); - - world_state.board.matched_pairs += 1; - - // Clear revealed cards and re-enable reveals - world_state.board.revealed_cards = Vec::new(&env); - world_state.game_state.can_reveal = true; - - // Check for game over - if world_state.board.matched_pairs == world_state.board.total_pairs { - world_state.game_state.game_over = true; - - RevealInfo { - result: RevealResult::GameOver, - position, - value: card_value, - positions, - } - } else { - RevealInfo { - result: RevealResult::MatchFound, - position, - value: card_value, - positions, - } - } - } else { - // Hide cards again - world_state.update_card(card1_entity_id, CardState::Hidden); - world_state.update_card(card2_entity_id, CardState::Hidden); - - // Clear revealed cards and re-enable reveals - world_state.board.revealed_cards = Vec::new(&env); - world_state.game_state.can_reveal = true; - - RevealInfo { - result: RevealResult::NoMatch, - position, - value: card_value, - positions, - } - } - } else { - RevealInfo { - result: RevealResult::CardRevealed, - position, - value: card_value, - positions: Vec::new(&env), - } - }; + let result = reveal_card_system(&env, &mut world_state, position); // Save world state env.storage().instance().set(&WORLD_KEY, &world_state); @@ -590,7 +46,7 @@ impl MemoryMatchContract { .get(&WORLD_KEY) .unwrap_or_else(|| panic!("Game not initialized")); - Self::to_game_state(&env, &world_state) + to_game_state(&env, &world_state) } pub fn reset_game(env: Env, player: Address) -> GameState { @@ -600,57 +56,13 @@ impl MemoryMatchContract { .get(&WORLD_KEY) .unwrap_or_else(|| panic!("Game not initialized")); - // Validate player - if world_state.game_state.player != player { - panic!("Not authorized player"); - } + validate_player(&world_state, &player); - // Reset all cards to hidden - // This is complex with Soroban Vec - we'll need to recreate the cards - let mut new_cards = Vec::new(&env); - for i in 0..world_state.cards.len() { - let mut card = world_state.cards.get(i).unwrap().clone(); - card.state = CardState::Hidden; - new_cards.push_back(card); - } - world_state.cards = new_cards; - - // Reset board - world_state.board.revealed_cards = Vec::new(&env); - world_state.board.matched_pairs = 0; - - // Reset game state - world_state.game_state.moves_count = 0; - world_state.game_state.game_over = false; - world_state.game_state.can_reveal = true; + reset_system(&env, &mut world_state); // Save world state env.storage().instance().set(&WORLD_KEY, &world_state); - Self::to_game_state(&env, &world_state) - } - - fn to_game_state(env: &Env, world: &ECSWorldState) -> GameState { - let mut board_state = Vec::new(env); - for i in 0..16 { - let card_entity_id = world.board.cards.get(i).unwrap(); - let card = world.get_card(card_entity_id).unwrap(); - - let state_value = match card.state { - CardState::Hidden => 0u32, - CardState::Revealed => card.value + 1, // 1-8 for revealed values - CardState::Matched => 9u32, - }; - board_state.push_back(state_value); - } - - GameState { - board_state, - revealed_count: world.board.revealed_cards.len(), - matched_pairs: world.board.matched_pairs, - total_pairs: world.board.total_pairs, - moves_count: world.game_state.moves_count, - game_over: world.game_state.game_over, - } + to_game_state(&env, &world_state) } } diff --git a/examples/memory_match/src/systems.rs b/examples/memory_match/src/systems.rs new file mode 100644 index 00000000..d58249d3 --- /dev/null +++ b/examples/memory_match/src/systems.rs @@ -0,0 +1,201 @@ +use soroban_sdk::{Address, Env, Vec}; + +use crate::components::{CardState, ECSWorldState, GameState, RevealInfo, RevealResult}; + +// ── ValidationSystem ───────────────────────────────────────────────────────── + +/// Validates that `player` is authorized to act on `world` and that the game +/// is in a state where a reveal can be attempted. +pub(crate) fn validate_reveal(world: &ECSWorldState, player: &Address, position: u32) { + if world.game_state.player != *player { + panic!("Not authorized player"); + } + + if world.game_state.game_over { + panic!("Game is over"); + } + + if !world.game_state.can_reveal { + panic!("Cannot reveal more than 2 cards"); + } + + if position >= world.board.cards.len() { + panic!("Invalid position"); + } +} + +/// Validates that `player` is authorized to reset `world`. +pub(crate) fn validate_player(world: &ECSWorldState, player: &Address) { + if world.game_state.player != *player { + panic!("Not authorized player"); + } +} + +// ── RevealSystem ───────────────────────────────────────────────────────────── + +/// Reveals the card at `position`, resolves a pair if two cards are now +/// revealed (match, no-match, or game-over), and returns the outcome. Mutates +/// `world` in place; the caller is responsible for persisting it. +pub(crate) fn reveal_card_system( + env: &Env, + world: &mut ECSWorldState, + position: u32, +) -> RevealInfo { + // Get card and check state + let card_entity_id = world.board.cards.get(position).unwrap(); + let card_value = { + let card = world.get_card(card_entity_id).unwrap(); + + if matches!(card.state, CardState::Revealed | CardState::Matched) { + panic!("Card already revealed or matched"); + } + + card.value + }; + + // Reveal the card + world.update_card(card_entity_id, CardState::Revealed); + + // Add to revealed cards + world.board.revealed_cards.push_back(position); + + // Update game state + world.game_state.moves_count += 1; + + // If 2 cards are revealed, disable further reveals + if world.board.revealed_cards.len() == 2 { + world.game_state.can_reveal = false; + } + + // Check if we have 2 cards revealed + if world.board.revealed_cards.len() == 2 { + resolve_pair_system(env, world, position, card_value) + } else { + RevealInfo { + result: RevealResult::CardRevealed, + position, + value: card_value, + positions: Vec::new(env), + } + } +} + +/// Resolves the two currently-revealed cards: marks them matched (and checks +/// for game completion) or hides them again on a mismatch. +fn resolve_pair_system( + env: &Env, + world: &mut ECSWorldState, + position: u32, + card_value: u32, +) -> RevealInfo { + let pos1 = world.board.revealed_cards.get(0).unwrap(); + let pos2 = world.board.revealed_cards.get(1).unwrap(); + + let card1_entity_id = world.board.cards.get(pos1).unwrap(); + let card2_entity_id = world.board.cards.get(pos2).unwrap(); + + let card1_value = world.get_card(card1_entity_id).unwrap().value; + let card2_value = world.get_card(card2_entity_id).unwrap().value; + + let is_match = card1_value == card2_value; + let mut positions = Vec::new(env); + positions.push_back(pos1); + positions.push_back(pos2); + + if is_match { + // Mark cards as matched + world.update_card(card1_entity_id, CardState::Matched); + world.update_card(card2_entity_id, CardState::Matched); + + world.board.matched_pairs += 1; + + // Clear revealed cards and re-enable reveals + world.board.revealed_cards = Vec::new(env); + world.game_state.can_reveal = true; + + // Check for game over + if world.board.matched_pairs == world.board.total_pairs { + world.game_state.game_over = true; + + RevealInfo { + result: RevealResult::GameOver, + position, + value: card_value, + positions, + } + } else { + RevealInfo { + result: RevealResult::MatchFound, + position, + value: card_value, + positions, + } + } + } else { + // Hide cards again + world.update_card(card1_entity_id, CardState::Hidden); + world.update_card(card2_entity_id, CardState::Hidden); + + // Clear revealed cards and re-enable reveals + world.board.revealed_cards = Vec::new(env); + world.game_state.can_reveal = true; + + RevealInfo { + result: RevealResult::NoMatch, + position, + value: card_value, + positions, + } + } +} + +// ── ResetSystem ─────────────────────────────────────────────────────────────── + +/// Resets every card to hidden and clears board/game-state progress. +pub(crate) fn reset_system(env: &Env, world: &mut ECSWorldState) { + // Reset all cards to hidden + // This is complex with Soroban Vec - we'll need to recreate the cards + let mut new_cards = Vec::new(env); + for i in 0..world.cards.len() { + let mut card = world.cards.get(i).unwrap().clone(); + card.state = CardState::Hidden; + new_cards.push_back(card); + } + world.cards = new_cards; + + // Reset board + world.board.revealed_cards = Vec::new(env); + world.board.matched_pairs = 0; + + // Reset game state + world.game_state.moves_count = 0; + world.game_state.game_over = false; + world.game_state.can_reveal = true; +} + +// ── ProjectionSystem ───────────────────────────────────────────────────────── + +/// Projects the internal `ECSWorldState` into the public `GameState` view. +pub(crate) fn to_game_state(env: &Env, world: &ECSWorldState) -> GameState { + let mut board_state = Vec::new(env); + for i in 0..16 { + let card_entity_id = world.board.cards.get(i).unwrap(); + let card = world.get_card(card_entity_id).unwrap(); + + let state_value = match card.state { + CardState::Hidden => 0u32, + CardState::Revealed => card.value + 1, // 1-8 for revealed values + CardState::Matched => 9u32, + }; + board_state.push_back(state_value); + } + + GameState { + board_state, + revealed_count: world.board.revealed_cards.len(), + matched_pairs: world.board.matched_pairs, + total_pairs: world.board.total_pairs, + moves_count: world.game_state.moves_count, + game_over: world.game_state.game_over, + } +} diff --git a/examples/memory_match/src/test.rs b/examples/memory_match/src/test.rs index 64e8e31c..d45fae20 100644 --- a/examples/memory_match/src/test.rs +++ b/examples/memory_match/src/test.rs @@ -1,4 +1,5 @@ use super::*; +use crate::components::RevealResult; use soroban_sdk::{testutils::Address as _, Env}; fn setup_game() -> (Env, MemoryMatchContractClient<'static>, Address) { diff --git a/examples/minesweeper/Cargo.lock b/examples/minesweeper/Cargo.lock new file mode 100644 index 00000000..767711da --- /dev/null +++ b/examples/minesweeper/Cargo.lock @@ -0,0 +1,1934 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[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 = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[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.118", +] + +[[package]] +name = "cc" +version = "1.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_eval" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cougr-core" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc489e355664841b5fb4d0b19e014c3be1a9b81cd1cd9f7ad29f02fe54a4135" +dependencies = [ + "soroban-sdk", + "wee_alloc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "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 = "ctor" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dtor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "escape-bytes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" + +[[package]] +name = "ethnum" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if 1.0.4", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[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 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if 1.0.4", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if 1.0.4", + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "minesweeper" +version = "0.1.0" +dependencies = [ + "cougr-core", + "soroban-sdk", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.118", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[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", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[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_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.8.22", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7192e3a5551a7aeee90d2110b11b615798e81951fd8c8293c87ea7f88b0168f5" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "soroban-env-common" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc49a80a68fc1005847308e63b9fce39874de731940b1807b721d472de3ff01" +dependencies = [ + "arbitrary", + "crate-git-revision", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr", + "wasmparser", +] + +[[package]] +name = "soroban-env-guest" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2334ba1cfe0a170ab744d96db0b4ca86934de9ff68187ceebc09dc342def55" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43af5d53c57bc2f546e122adc0b1cca6f93942c718977379aa19ddd04f06fcec" +dependencies = [ + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "curve25519-dalek", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "generic-array", + "getrandom", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "p256", + "rand", + "rand_chacha", + "sec1", + "sha2", + "sha3", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", + "static_assertions", + "stellar-strkey 0.0.13", + "wasmparser", +] + +[[package]] +name = "soroban-env-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a989167512e3592d455b1e204d703cfe578a36672a77ed2f9e6f7e1bbfd9cc5c" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr", + "syn 2.0.118", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ca06e6c5029d1285e66219cb387a234224e26969ce8ad2bc2d5017e9395d63b" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common", + "soroban-env-host", + "thiserror", +] + +[[package]] +name = "soroban-sdk" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4502f2e018f238a4c5d3212d7d20ea6abcdc6e58babd63b642b693739db30fd1" +dependencies = [ + "arbitrary", + "bytes-lit", + "crate-git-revision", + "ctor", + "derive_arbitrary", + "ed25519-dalek", + "rand", + "rustc_version", + "serde", + "serde_json", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey 0.0.16", + "visibility", +] + +[[package]] +name = "soroban-sdk-macros" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca03e9cf61d241cb9afdd6ddf41f6c25698b3f566a875e7009ea799b89e2bf0a" +dependencies = [ + "darling 0.20.11", + "heck", + "itertools", + "macro-string", + "proc-macro2", + "quote", + "sha2", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", + "syn 2.0.118", +] + +[[package]] +name = "soroban-spec" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa02e07f507cc27406ae0834db4dcf309b78c4cc8776eb3b2d662d66e8859d25" +dependencies = [ + "base64", + "sha2", + "stellar-xdr", + "thiserror", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6835bb510763ef3fa5405e89036e3c8ea6ef5abe55fc52cfe9ac0e38be9d531c" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2", + "soroban-spec", + "stellar-xdr", + "syn 2.0.118", + "thiserror", +] + +[[package]] +name = "soroban-wasmi" +version = "0.31.1-soroban.20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" +dependencies = [ + "smallvec", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[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.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1832fb50c651ad10f734aaf5d31ca5acdfb197a6ecda64d93fcdb8885af913" +dependencies = [ + "crate-git-revision", + "data-encoding", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084afcb0d458c3d5d5baa2d294b18f881e62cc258ef539d8fdf68be7dbe45520" +dependencies = [ + "crate-git-revision", + "data-encoding", + "heapless", +] + +[[package]] +name = "stellar-xdr" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d20dafed80076b227d4b17c0c508a4bbc4d5e4c3d4c1de7cd42242df4b1eaf" +dependencies = [ + "arbitrary", + "base64", + "cfg_eval", + "crate-git-revision", + "escape-bytes", + "ethnum", + "hex", + "serde", + "serde_with", + "sha2", + "stellar-strkey 0.0.13", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[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.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "time" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.118", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmi_arena" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "wasmparser" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" +dependencies = [ + "indexmap 2.14.0", + "semver", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[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-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/examples/minesweeper/README.md b/examples/minesweeper/README.md index b385e0db..1d380856 100644 --- a/examples/minesweeper/README.md +++ b/examples/minesweeper/README.md @@ -1,343 +1,107 @@ -# Minesweeper On-Chain Game +# Minesweeper -A fully functional Minesweeper game implemented as a Soroban smart contract on the Stellar blockchain, demonstrating the **Cougr-Core** ECS (Entity Component System) framework for on-chain gaming. +> **Transitional example**: This example uses an older Cougr pattern and is preserved +> for compatibility reference. For the current recommended approach, see `snake`. -| | | -| --------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +An on-chain Minesweeper game built with the [Cougr](../../README.md) ECS framework on Stellar Soroban. -## Why Cougr-Core? +## Purpose and pattern -Cougr-Core provides an ECS architecture that simplifies on-chain game development with modular, testable systems. +This example demonstrates a single-player reveal-and-deduce puzzle over a fixed, deterministic +mine layout — no player input or randomness influences where mines are placed, which keeps the +board auditable and reproducible across deployments. It showcases Cougr's `ComponentTrait` +pattern for typed, byte-serializable game state: the board, the (hidden) mine layout, and the +game status are each their own component with manual `serialize`/`deserialize`, demonstrating +explicit byte-layout control for a fixed-size 9×9 grid. -### ComponentTrait Integration +## Public contract API -All game components implement `cougr_core::component::ComponentTrait`: +| Function | Parameters | Returns | Description | +|---|---|---|---| +| `init_game` | — | `GameState` | Initializes a 9×9 board with mines placed at fixed, deterministic positions. Overwrites any existing game. | +| `reveal_cell` | `row: u32`, `col: u32` | `RevealResult` | Reveals the cell at `(row, col)`. Returns `success`, `is_mine`, `adjacent_mines`, and a status `message` symbol (`ok`, `invalid`, `revealed`, `boom`, or `over`). | +| `get_state` | — | `GameState` | Current status, revealed-cell count, and remaining safe cells. | +| `get_visible_cell` | `row: u32`, `col: u32` | `VisibleCellState` | Whether the cell is revealed, whether it's a mine (only meaningful once revealed), and its adjacent-mine count. | +| `is_finished` | — | `bool` | Whether the game has reached `Won` or `Lost`. | +| `get_board` | — | `Vec` | Raw 81-cell board array (row-major); each entry is `0`–`8` for a revealed count, `9` for hidden, or `10` for a revealed mine. | +| `reset_game` | — | `GameState` | Re-initializes the game (equivalent to calling `init_game` again). | -```rust -impl ComponentTrait for BoardComponent { - fn component_type() -> Symbol { - symbol_short!("board") - } +## Architecture overview - fn serialize(&self, env: &Env) -> Bytes { /* ... */ } - fn deserialize(env: &Env, data: &Bytes) -> Option { /* ... */ } -} -``` - -### ECS System Pattern - -Game logic is organized into discrete systems: - -| System | Responsibility | -| -------------------- | ------------------------------------------------------- | -| `RevealSystem` | Handles cell revelation and mine detection | -| `AdjacencySystem` | Calculates adjacent mine count for each cell | -| `LossSystem` | Detects mine reveal and triggers game over | -| `CompletionSystem` | Checks if all safe cells are revealed (win condition) | - -## Features +There is no `GameApp` tick loop — `reveal_cell` runs all logic synchronously in a fixed +pipeline on each invocation: -| Feature | Description | -| ---------------------- | -------------------------------------------------------------- | -| Deterministic layout | Fixed mine positions for verifiability | -| Cell reveal mechanics | Reveal cells with adjacent mine count feedback | -| Adjacency counting | Shows number of mines in 8 neighboring cells | -| Win detection | Tracks when all safe cells are revealed | -| Loss detection | Game ends immediately on mine reveal | -| Compact board state | 9×9 grid optimized for on-chain storage | -| Proof-friendly | Deterministic layout enables verification | - -## Prerequisites - -| Requirement | Version | -| ----------- | --------------------- | -| Rust | 1.70.0+ | -| Stellar CLI | 25.0.0+ (recommended) | - -```bash -cargo install stellar-cli ``` - -## Building - -```bash -# Build for testing -cargo build - -# Build optimized WASM -stellar contract build +reveal_cell + └─ status/bounds/already-revealed checks → rejects finished games, out-of-bounds + coordinates, and cells already revealed with the matching `message` symbol + └─ mine check → if the cell holds a mine, marks the cell, + sets status to Lost, and returns immediately + └─ adjacency count + board update → writes the computed adjacent-mine count + into the board for a safe reveal + └─ CompletionSystem → sets status to Won once every safe cell + has been revealed ``` -## Testing +`components.rs` defines the data (`BoardComponent`, `MineLayoutComponent`, +`GameStateComponent`, plus the `ECSWorldState` aggregate and the public API types); +`systems.rs` holds the pure functions (`place_mines_deterministic`, `completion_system`); +`lib.rs` owns the single `ECSWorldState` per contract instance and is the only module that +touches contract storage. + +## Storage model + +The entire game lives under one instance-storage key (`WORLD_KEY`, `symbol_short!("WORLD")`) +as a single `ECSWorldState` struct bundling the board, the mine layout, and the game status. +Instance storage is used — not persistent or temporary — because there is exactly one game per +contract instance and the state must survive for the lifetime of that instance with no +per-entry TTL management. + +## Main gameplay flow + +1. Deployer (or any caller) calls `init_game()`. The board starts fully hidden and + `place_mines_deterministic` seeds 10 mines at fixed coordinates across the 9×9 grid. +2. Player calls `reveal_cell(row, col)` for a hidden cell. +3. If the cell is a mine, the board marks it revealed-as-mine, status flips to `Lost`, and the + call returns `message = boom`. No further reveals are accepted until `reset_game`. +4. If the cell is safe, the contract counts mines among its up-to-8 neighbors, writes that + count into the board, and increments the revealed-cell counter. +5. After each safe reveal, `CompletionSystem` checks whether all 71 safe cells (81 total minus + 10 mines) have been revealed; if so, status flips to `Won`. +6. Caller reads `get_state`, `get_visible_cell`, or `is_finished` to check progress or + terminal state; `reset_game` starts over with the same deterministic layout. + +## Cougr APIs used + +- `cougr_core::component::ComponentTrait` — gives each component (`BoardComponent`, + `MineLayoutComponent`, `GameStateComponent`) a `component_type()` symbol and explicit byte + `serialize`/`deserialize`, chosen because the game has one fixed-shape 81-cell grid rather + than a dynamic entity population that would benefit from `SimpleWorld`/`SimpleQueryBuilder` + scanning. +- No other `cougr-core` APIs are used. `GameApp`/`ScheduleStage` are unnecessary because + `reveal_cell` has a single decision point per call with no multi-stage ordering; `SimpleWorld`/ + `SimpleQueryBuilder` are unnecessary because the game has exactly three fixed components and + never scans entities by type; `auth`, `privacy::stable`/`experimental`, and `ops` standards + are unused because this example has no multi-party authorization, hidden-information + disclosure, or pausability/ownership requirements — the entire board state is openly + readable via `get_board` and `get_visible_cell`. + +## Build and test commands ```bash cargo test -``` - -| Test Category | Count | Coverage | -| -------------------------- | ----- | ----------------------------------------------------- | -| Initialization | 2 | Game setup, board retrieval | -| Safe cell reveal | 3 | Basic reveal, adjacent count, counter increment | -| Mine reveal | 2 | Hit mine, game over | -| Repeated reveals | 2 | Already revealed, multiple different cells | -| Out of bounds | 1 | Invalid coordinates | -| Win condition | 2 | State tracking, after loss | -| Visible cell state | 4 | Hidden, revealed, mine revealed, out of bounds | -| Reset game | 1 | Mid-game reset | -| Adjacent count verification| 1 | Multiple cells across board | -| Edge cases | 3 | Corners, game over, deterministic placement | -| **Total** | **21**| **All passing** | - -## Contract API - -### Functions - -| Function | Parameters | Returns | Description | -| ----------------- | ----------------------- | ------------------ | ------------------------------------ | -| `init_game` | - | `GameState` | Initialize new game with fixed mines | -| `reveal_cell` | `row: u32, col: u32` | `RevealResult` | Reveal cell at position | -| `get_state` | - | `GameState` | Get current game state | -| `get_visible_cell`| `row: u32, col: u32` | `VisibleCellState` | Get state of specific cell | -| `is_finished` | - | `bool` | Check if game is over | -| `get_board` | - | `Vec` | Get full board state (debug) | -| `reset_game` | - | `GameState` | Reset game with new layout | - -### Board Layout - -``` -9×9 Grid (81 cells total, 10 mines) - - 0 1 2 3 4 5 6 7 8 - ┌──────────────────────────┐ -0 │ . . . . . . . . . │ -1 │ . * . . . * . . . │ -2 │ . . . . . . . * . │ -3 │ . . . * . . . . * │ -4 │ . . . . . . * . . │ -5 │ . . * . * . . * . │ -6 │ . . . . . . . . . │ -7 │ * . . . . * . . . │ -8 │ . . . . . . . . . │ - └──────────────────────────┘ - -Legend: - . = Hidden cell - * = Mine (hidden until revealed) - 0-8 = Revealed cell with adjacent mine count -``` - -### Cell States - -| Value | Meaning | -|-------|---------| -| 0-8 | Revealed cell with N adjacent mines | -| 9 | Hidden cell | -| 10 | Mine (revealed) | - -### Data Structures - -**GameState** -| Field | Type | Description | -| -------------------- | ------- | ---------------------------------------------- | -| `rows` | `u32` | Number of rows (9) | -| `cols` | `u32` | Number of columns (9) | -| `total_mines` | `u32` | Total mines on board (10) | -| `status` | `u32` | 0=Playing, 1=Won, 2=Lost | -| `revealed_count` | `u32` | Number of safe cells revealed | -| `safe_cells_remaining`| `u32` | Safe cells yet to be revealed | - -**RevealResult** -| Field | Type | Description | -| ------------------ | --------- | ------------------------------------- | -| `success` | `bool` | Whether reveal succeeded | -| `is_mine` | `bool` | True if cell contains mine | -| `adjacent_mines` | `u32` | Count of mines in 8 neighboring cells | -| `message` | `Symbol` | Status code | - -**VisibleCellState** -| Field | Type | Description | -| ------------------ | --------- | ------------------------------------- | -| `is_revealed` | `bool` | Whether cell is revealed | -| `is_mine` | `bool` | Whether cell is a mine (if revealed) | -| `adjacent_mines` | `u32` | Adjacent mine count (if revealed) | - -### Error Messages - -| Code | Meaning | -| ---------- | -------------------------------------------- | -| `ok` | Reveal successful | -| `invalid` | Coordinates out of bounds (not 0-8) | -| `revealed` | Cell already revealed | -| `boom` | Hit mine - game over | -| `over` | Game already finished | - -## Architecture - -```text -ECSWorldState -├── BoardComponent (entity_id: 0) -│ └── cells: Vec [81 cells - visible state] -├── MineLayoutComponent (entity_id: 1) -│ └── mines: Vec [81 cells - hidden mine positions] -├── GameStateComponent (entity_id: 2) -│ ├── status: u32 -│ ├── revealed_count: u32 -│ └── entity_id: u32 -└── next_entity_id: u32 -``` - -### Adjacency Calculation - -The contract calculates adjacent mines by checking all 8 neighbors: - -```rust -fn count_adjacent_mines(&self, env: &Env, row: u32, col: u32) -> u32 { - let mut count = 0; - - // Check all 8 neighbors - for dr in -1i32..=1 { - for dc in -1i32..=1 { - if dr == 0 && dc == 0 { continue; } - - let nr = row as i32 + dr; - let nc = col as i32 + dc; - - if nr >= 0 && nr < ROWS as i32 && nc >= 0 && nc < COLS as i32 { - if self.has_mine(env, nr as u32, nc as u32) { - count += 1; - } - } - } - } - - count -} -``` - -### Win/Loss Conditions - -**Loss:** Player reveals a cell containing a mine -**Win:** All safe cells revealed (total cells - mines = 71 safe cells) - -## Gameplay Example - -```text -Initial State (all hidden): -┌──────────────────────────┐ -│ # # # # # # # # # │ -│ # # # # # # # # # │ -│ # # # # # # # # # │ -│ # # # # # # # # # │ -│ # # # # # # # # # │ -│ # # # # # # # # # │ -│ # # # # # # # # # │ -│ # # # # # # # # # │ -│ # # # # # # # # # │ -└──────────────────────────┘ - -After revealing (0,0) - shows 1 adjacent mine: -┌──────────────────────────┐ -│ 1 # # # # # # # # │ -│ # * # # # # # # # │ -│ # # # # # # # # # │ -│ # # # # # # # # # │ -│ # # # # # # # # # │ -│ # # # # # # # # # │ -│ # # # # # # # # # │ -│ # # # # # # # # # │ -│ # # # # # # # # # │ -└──────────────────────────┘ - -Player hits mine at (1,1) - GAME OVER: -┌──────────────────────────┐ -│ 1 💥 # # # # # # # │ -│ # # # # # # # # # │ -... -Status: Lost -``` - -## Deployment - -### Deploy to Testnet - -```bash -# Generate funded account -stellar keys generate deployer --network --fund - -# Build contract stellar contract build - -# Deploy -stellar contract deploy \ - --wasm target/wasm32v1-none/release/minesweeper.wasm \ - --source deployer \ - --network ``` -### Interact with Deployed Contract - -```bash -# Initialize a game -stellar contract invoke \ - --id \ - --network \ - -- init_game - -# Reveal cell at row 0, col 0 -stellar contract invoke \ - --id \ - --network \ - -- reveal_cell \ - --row 0 \ - --col 0 - -# Get game state -stellar contract invoke \ - --id \ - --network \ - -- get_state - -# Check if game is finished -stellar contract invoke \ - --id \ - --network \ - -- is_finished -``` - -## Strategy Tips - -1. **Start from corners**: Corners have fewer neighbors, making them safer -2. **Use adjacent counts**: Numbers reveal information about surrounding cells -3. **Avoid random guesses**: Each reveal should be informed by adjacent counts -4. **Track revealed cells**: Don't waste moves on already-revealed cells - -## Design Decisions - -### Deterministic Mine Placement - -Unlike traditional Minesweeper with random layouts, this implementation uses a **fixed, deterministic mine pattern** for several reasons: - -1. **Proof-friendly**: Players can verify the layout is fair -2. **Reproducible**: Same inputs always produce same results -3. **On-chain friendly**: No need for randomness oracles -4. **Auditability**: Anyone can verify the mine positions - -### Compact 9×9 Board - -- **81 cells total** - manageable on-chain storage -- **10 mines (~12% density)** - beginner-friendly difficulty -- **71 safe cells** - reasonable completion goal - -### No Flagging System - -Flags omitted to keep scope focused on core reveal mechanics. The game emphasizes: -- Safe cell discovery -- Adjacent count interpretation -- Risk assessment - -## Resources - -- [Cougr Repository](https://github.com/salazarsebas/Cougr) -- [Soroban Documentation](https://developers.stellar.org/docs/build/smart-contracts) -- [Stellar CLI Reference](https://developers.stellar.org/docs/tools/cli) -- [Minesweeper Wikipedia](https://en.wikipedia.org/wiki/Minesweeper_(video_game)) +## Known limitations + +- Does not use `GameApp`, `ScheduleStage`, or `SimpleWorld` — reveal logic is invoked directly + from contract entrypoints since a single-player puzzle with one decision point per call has + no need for staged scheduling. +- Mine placement is fixed and deterministic rather than randomized per game; this keeps the + layout auditable but means every new game (or `reset_game` call) has mines in the same + positions. +- No flagging system — the contract only supports revealing cells, not marking suspected + mines. +- No multi-player or wagering support; any caller can call any entrypoint for the single + shared game instance. diff --git a/examples/minesweeper/src/components.rs b/examples/minesweeper/src/components.rs new file mode 100644 index 00000000..283e6e7a --- /dev/null +++ b/examples/minesweeper/src/components.rs @@ -0,0 +1,345 @@ +#![allow(dead_code)] + +use cougr_core::component::ComponentTrait; +use soroban_sdk::{contracttype, symbol_short, Bytes, Env, Symbol, Vec}; + +/// Board dimensions: 9×9 (compact for on-chain) +pub(crate) const ROWS: u32 = 9; +pub(crate) const COLS: u32 = 9; + +/// Number of mines (10 mines for ~12% density - beginner friendly) +pub(crate) const MINES: u32 = 10; + +/// Cell state: 0-8 = revealed with count, 9 = hidden, 10 = mine, 11 = flag (optional) +pub(crate) const CELL_HIDDEN: u32 = 9; +pub(crate) const CELL_MINE: u32 = 10; + +/// Game status: 0=Playing, 1=Won, 2=Lost +pub(crate) const STATUS_PLAYING: u32 = 0; +pub(crate) const STATUS_WON: u32 = 1; +pub(crate) const STATUS_LOST: u32 = 2; + +pub(crate) const WORLD_KEY: Symbol = symbol_short!("WORLD"); + +/// Board component - stores the mine layout and revealed state +#[contracttype] +#[derive(Clone, Debug)] +pub struct BoardComponent { + pub cells: Vec, // Flattened 2D array: index = row * COLS + col + pub entity_id: u32, +} + +impl BoardComponent { + pub fn new(env: &Env, entity_id: u32) -> Self { + let mut cells = Vec::new(env); + for _ in 0..(ROWS * COLS) { + cells.push_back(CELL_HIDDEN); + } + Self { cells, entity_id } + } + + /// Get cell value at (row, col) + pub fn get_cell(&self, _env: &Env, row: u32, col: u32) -> u32 { + if row >= ROWS || col >= COLS { + return CELL_HIDDEN; + } + let index = row * COLS + col; + self.cells.get(index).unwrap_or(CELL_HIDDEN) + } + + /// Set cell value at (row, col) + pub fn set_cell(&mut self, _env: &Env, row: u32, col: u32, value: u32) { + if row >= ROWS || col >= COLS { + return; + } + let index = row * COLS + col; + self.cells.set(index, value); + } + + /// Check if cell is hidden + pub fn is_hidden(&self, env: &Env, row: u32, col: u32) -> bool { + self.get_cell(env, row, col) == CELL_HIDDEN + } + + /// Check if cell is a mine + pub fn is_mine(&self, env: &Env, row: u32, col: u32) -> bool { + self.get_cell(env, row, col) == CELL_MINE + } + + /// Check if cell is revealed (0-8 or mine) + pub fn is_revealed(&self, env: &Env, row: u32, col: u32) -> bool { + let cell = self.get_cell(env, row, col); + cell < CELL_HIDDEN || cell == CELL_MINE + } +} + +impl ComponentTrait for BoardComponent { + fn component_type() -> Symbol { + symbol_short!("board") + } + + fn serialize(&self, env: &Env) -> Bytes { + let mut bytes = Bytes::new(env); + bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); + let len = self.cells.len(); + bytes.append(&Bytes::from_array(env, &len.to_be_bytes())); + for i in 0..len { + let cell = self.cells.get(i).unwrap_or(CELL_HIDDEN); + bytes.append(&Bytes::from_array(env, &cell.to_be_bytes())); + } + bytes + } + + fn deserialize(env: &Env, data: &Bytes) -> Option { + if data.len() < 8 { + return None; + } + let entity_id = u32::from_be_bytes([ + data.get(0).unwrap(), + data.get(1).unwrap(), + data.get(2).unwrap(), + data.get(3).unwrap(), + ]); + let len = u32::from_be_bytes([ + data.get(4).unwrap(), + data.get(5).unwrap(), + data.get(6).unwrap(), + data.get(7).unwrap(), + ]); + + let mut cells = Vec::new(env); + for i in 0..len { + let offset = 8 + (i * 4); + if offset + 4 > data.len() { + break; + } + let cell = u32::from_be_bytes([ + data.get(offset).unwrap(), + data.get(offset + 1).unwrap(), + data.get(offset + 2).unwrap(), + data.get(offset + 3).unwrap(), + ]); + cells.push_back(cell); + } + Some(Self { cells, entity_id }) + } +} + +/// Mine layout component - stores where mines are placed (hidden from players) +#[contracttype] +#[derive(Clone, Debug)] +pub struct MineLayoutComponent { + pub mines: Vec, // Bit-packed or simple array of mine positions + pub entity_id: u32, +} + +impl MineLayoutComponent { + pub fn new(env: &Env, entity_id: u32) -> Self { + let mut mines = Vec::new(env); + for _ in 0..(ROWS * COLS) { + mines.push_back(0u32); // 0 = no mine, 1 = mine + } + Self { mines, entity_id } + } + + /// Check if position has a mine + pub fn has_mine(&self, _env: &Env, row: u32, col: u32) -> bool { + if row >= ROWS || col >= COLS { + return false; + } + let index = row * COLS + col; + self.mines.get(index).unwrap_or(0) == 1 + } + + /// Set mine at position + pub fn set_mine(&mut self, _env: &Env, row: u32, col: u32) { + if row >= ROWS || col >= COLS { + return; + } + let index = row * COLS + col; + self.mines.set(index, 1); + } + + /// Count adjacent mines + pub fn count_adjacent_mines(&self, env: &Env, row: u32, col: u32) -> u32 { + let mut count = 0; + + // Check all 8 neighbors + for dr in -1i32..=1 { + for dc in -1i32..=1 { + if dr == 0 && dc == 0 { + continue; + } + + let nr = row as i32 + dr; + let nc = col as i32 + dc; + + if nr >= 0 + && nr < ROWS as i32 + && nc >= 0 + && nc < COLS as i32 + && self.has_mine(env, nr as u32, nc as u32) + { + count += 1; + } + } + } + + count + } +} + +impl ComponentTrait for MineLayoutComponent { + fn component_type() -> Symbol { + symbol_short!("minelyt") + } + + fn serialize(&self, env: &Env) -> Bytes { + let mut bytes = Bytes::new(env); + bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); + let len = self.mines.len(); + bytes.append(&Bytes::from_array(env, &len.to_be_bytes())); + for i in 0..len { + let mine = self.mines.get(i).unwrap_or(0); + bytes.append(&Bytes::from_array(env, &mine.to_be_bytes())); + } + bytes + } + + fn deserialize(env: &Env, data: &Bytes) -> Option { + if data.len() < 8 { + return None; + } + let entity_id = u32::from_be_bytes([ + data.get(0).unwrap(), + data.get(1).unwrap(), + data.get(2).unwrap(), + data.get(3).unwrap(), + ]); + let len = u32::from_be_bytes([ + data.get(4).unwrap(), + data.get(5).unwrap(), + data.get(6).unwrap(), + data.get(7).unwrap(), + ]); + + let mut mines = Vec::new(env); + for i in 0..len { + let offset = 8 + (i * 4); + if offset + 4 > data.len() { + break; + } + let mine = u32::from_be_bytes([ + data.get(offset).unwrap(), + data.get(offset + 1).unwrap(), + data.get(offset + 2).unwrap(), + data.get(offset + 3).unwrap(), + ]); + mines.push_back(mine); + } + Some(Self { mines, entity_id }) + } +} + +/// Game state component +#[contracttype] +#[derive(Clone, Debug)] +pub struct GameStateComponent { + pub status: u32, // 0=Playing, 1=Won, 2=Lost + pub revealed_count: u32, + pub entity_id: u32, +} + +impl GameStateComponent { + pub fn new(entity_id: u32) -> Self { + Self { + status: STATUS_PLAYING, + revealed_count: 0, + entity_id, + } + } +} + +impl ComponentTrait for GameStateComponent { + fn component_type() -> Symbol { + symbol_short!("gstate") + } + + fn serialize(&self, env: &Env) -> Bytes { + let mut bytes = Bytes::new(env); + bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); + bytes.append(&Bytes::from_array(env, &self.status.to_be_bytes())); + bytes.append(&Bytes::from_array(env, &self.revealed_count.to_be_bytes())); + bytes + } + + fn deserialize(_env: &Env, data: &Bytes) -> Option { + if data.len() < 12 { + return None; + } + let entity_id = u32::from_be_bytes([ + data.get(0).unwrap(), + data.get(1).unwrap(), + data.get(2).unwrap(), + data.get(3).unwrap(), + ]); + let status = u32::from_be_bytes([ + data.get(4).unwrap(), + data.get(5).unwrap(), + data.get(6).unwrap(), + data.get(7).unwrap(), + ]); + let revealed_count = u32::from_be_bytes([ + data.get(8).unwrap(), + data.get(9).unwrap(), + data.get(10).unwrap(), + data.get(11).unwrap(), + ]); + Some(Self { + status, + revealed_count, + entity_id, + }) + } +} + +/// ECS World State +#[contracttype] +#[derive(Clone, Debug)] +pub struct ECSWorldState { + pub board: BoardComponent, + pub mine_layout: MineLayoutComponent, + pub game_state: GameStateComponent, + pub next_entity_id: u32, +} + +/// External game state for API consumers +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GameState { + pub rows: u32, + pub cols: u32, + pub total_mines: u32, + pub status: u32, + pub revealed_count: u32, + pub safe_cells_remaining: u32, +} + +/// Reveal result +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct RevealResult { + pub success: bool, + pub is_mine: bool, + pub adjacent_mines: u32, + pub message: Symbol, +} + +/// Visible cell state for querying +#[contracttype] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct VisibleCellState { + pub is_revealed: bool, + pub is_mine: bool, + pub adjacent_mines: u32, +} diff --git a/examples/minesweeper/src/lib.rs b/examples/minesweeper/src/lib.rs index cc653616..47c3ef8b 100644 --- a/examples/minesweeper/src/lib.rs +++ b/examples/minesweeper/src/lib.rs @@ -1,348 +1,21 @@ #![no_std] -use cougr_core::component::ComponentTrait; -use soroban_sdk::{contract, contractimpl, contracttype, symbol_short, Bytes, Env, Symbol, Vec}; - -/// Board dimensions: 9×9 (compact for on-chain) -const ROWS: u32 = 9; -const COLS: u32 = 9; - -/// Number of mines (10 mines for ~12% density - beginner friendly) -const MINES: u32 = 10; - -/// Cell state: 0-8 = revealed with count, 9 = hidden, 10 = mine, 11 = flag (optional) -const CELL_HIDDEN: u32 = 9; -const CELL_MINE: u32 = 10; - -/// Game status: 0=Playing, 1=Won, 2=Lost -const STATUS_PLAYING: u32 = 0; -const STATUS_WON: u32 = 1; -const STATUS_LOST: u32 = 2; - -/// Board component - stores the mine layout and revealed state -#[contracttype] -#[derive(Clone, Debug)] -pub struct BoardComponent { - pub cells: Vec, // Flattened 2D array: index = row * COLS + col - pub entity_id: u32, -} - -impl BoardComponent { - pub fn new(env: &Env, entity_id: u32) -> Self { - let mut cells = Vec::new(env); - for _ in 0..(ROWS * COLS) { - cells.push_back(CELL_HIDDEN); - } - Self { cells, entity_id } - } - - /// Get cell value at (row, col) - pub fn get_cell(&self, _env: &Env, row: u32, col: u32) -> u32 { - if row >= ROWS || col >= COLS { - return CELL_HIDDEN; - } - let index = row * COLS + col; - self.cells.get(index).unwrap_or(CELL_HIDDEN) - } - - /// Set cell value at (row, col) - pub fn set_cell(&mut self, _env: &Env, row: u32, col: u32, value: u32) { - if row >= ROWS || col >= COLS { - return; - } - let index = row * COLS + col; - self.cells.set(index, value); - } - - /// Check if cell is hidden - pub fn is_hidden(&self, env: &Env, row: u32, col: u32) -> bool { - self.get_cell(env, row, col) == CELL_HIDDEN - } - - /// Check if cell is a mine - pub fn is_mine(&self, env: &Env, row: u32, col: u32) -> bool { - self.get_cell(env, row, col) == CELL_MINE - } - - /// Check if cell is revealed (0-8 or mine) - pub fn is_revealed(&self, env: &Env, row: u32, col: u32) -> bool { - let cell = self.get_cell(env, row, col); - cell < CELL_HIDDEN || cell == CELL_MINE - } -} - -impl ComponentTrait for BoardComponent { - fn component_type() -> Symbol { - symbol_short!("board") - } - - fn serialize(&self, env: &Env) -> Bytes { - let mut bytes = Bytes::new(env); - bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); - let len = self.cells.len(); - bytes.append(&Bytes::from_array(env, &len.to_be_bytes())); - for i in 0..len { - let cell = self.cells.get(i).unwrap_or(CELL_HIDDEN); - bytes.append(&Bytes::from_array(env, &cell.to_be_bytes())); - } - bytes - } - - fn deserialize(env: &Env, data: &Bytes) -> Option { - if data.len() < 8 { - return None; - } - let entity_id = u32::from_be_bytes([ - data.get(0).unwrap(), - data.get(1).unwrap(), - data.get(2).unwrap(), - data.get(3).unwrap(), - ]); - let len = u32::from_be_bytes([ - data.get(4).unwrap(), - data.get(5).unwrap(), - data.get(6).unwrap(), - data.get(7).unwrap(), - ]); - - let mut cells = Vec::new(env); - for i in 0..len { - let offset = 8 + (i * 4); - if offset + 4 > data.len() { - break; - } - let cell = u32::from_be_bytes([ - data.get(offset).unwrap(), - data.get(offset + 1).unwrap(), - data.get(offset + 2).unwrap(), - data.get(offset + 3).unwrap(), - ]); - cells.push_back(cell); - } - Some(Self { cells, entity_id }) - } -} - -/// Mine layout component - stores where mines are placed (hidden from players) -#[contracttype] -#[derive(Clone, Debug)] -pub struct MineLayoutComponent { - pub mines: Vec, // Bit-packed or simple array of mine positions - pub entity_id: u32, -} - -impl MineLayoutComponent { - pub fn new(env: &Env, entity_id: u32) -> Self { - let mut mines = Vec::new(env); - for _ in 0..(ROWS * COLS) { - mines.push_back(0u32); // 0 = no mine, 1 = mine - } - Self { mines, entity_id } - } - - /// Check if position has a mine - pub fn has_mine(&self, _env: &Env, row: u32, col: u32) -> bool { - if row >= ROWS || col >= COLS { - return false; - } - let index = row * COLS + col; - self.mines.get(index).unwrap_or(0) == 1 - } - - /// Set mine at position - pub fn set_mine(&mut self, _env: &Env, row: u32, col: u32) { - if row >= ROWS || col >= COLS { - return; - } - let index = row * COLS + col; - self.mines.set(index, 1); - } - - /// Count adjacent mines - pub fn count_adjacent_mines(&self, env: &Env, row: u32, col: u32) -> u32 { - let mut count = 0; - - // Check all 8 neighbors - for dr in -1i32..=1 { - for dc in -1i32..=1 { - if dr == 0 && dc == 0 { - continue; - } - - let nr = row as i32 + dr; - let nc = col as i32 + dc; - - if nr >= 0 - && nr < ROWS as i32 - && nc >= 0 - && nc < COLS as i32 - && self.has_mine(env, nr as u32, nc as u32) - { - count += 1; - } - } - } - - count - } -} - -impl ComponentTrait for MineLayoutComponent { - fn component_type() -> Symbol { - symbol_short!("minelyt") - } - - fn serialize(&self, env: &Env) -> Bytes { - let mut bytes = Bytes::new(env); - bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); - let len = self.mines.len(); - bytes.append(&Bytes::from_array(env, &len.to_be_bytes())); - for i in 0..len { - let mine = self.mines.get(i).unwrap_or(0); - bytes.append(&Bytes::from_array(env, &mine.to_be_bytes())); - } - bytes - } - - fn deserialize(env: &Env, data: &Bytes) -> Option { - if data.len() < 8 { - return None; - } - let entity_id = u32::from_be_bytes([ - data.get(0).unwrap(), - data.get(1).unwrap(), - data.get(2).unwrap(), - data.get(3).unwrap(), - ]); - let len = u32::from_be_bytes([ - data.get(4).unwrap(), - data.get(5).unwrap(), - data.get(6).unwrap(), - data.get(7).unwrap(), - ]); - - let mut mines = Vec::new(env); - for i in 0..len { - let offset = 8 + (i * 4); - if offset + 4 > data.len() { - break; - } - let mine = u32::from_be_bytes([ - data.get(offset).unwrap(), - data.get(offset + 1).unwrap(), - data.get(offset + 2).unwrap(), - data.get(offset + 3).unwrap(), - ]); - mines.push_back(mine); - } - Some(Self { mines, entity_id }) - } -} - -/// Game state component -#[contracttype] -#[derive(Clone, Debug)] -pub struct GameStateComponent { - pub status: u32, // 0=Playing, 1=Won, 2=Lost - pub revealed_count: u32, - pub entity_id: u32, -} - -impl GameStateComponent { - pub fn new(entity_id: u32) -> Self { - Self { - status: STATUS_PLAYING, - revealed_count: 0, - entity_id, - } - } -} - -impl ComponentTrait for GameStateComponent { - fn component_type() -> Symbol { - symbol_short!("gstate") - } - - fn serialize(&self, env: &Env) -> Bytes { - let mut bytes = Bytes::new(env); - bytes.append(&Bytes::from_array(env, &self.entity_id.to_be_bytes())); - bytes.append(&Bytes::from_array(env, &self.status.to_be_bytes())); - bytes.append(&Bytes::from_array(env, &self.revealed_count.to_be_bytes())); - bytes - } - - fn deserialize(_env: &Env, data: &Bytes) -> Option { - if data.len() < 12 { - return None; - } - let entity_id = u32::from_be_bytes([ - data.get(0).unwrap(), - data.get(1).unwrap(), - data.get(2).unwrap(), - data.get(3).unwrap(), - ]); - let status = u32::from_be_bytes([ - data.get(4).unwrap(), - data.get(5).unwrap(), - data.get(6).unwrap(), - data.get(7).unwrap(), - ]); - let revealed_count = u32::from_be_bytes([ - data.get(8).unwrap(), - data.get(9).unwrap(), - data.get(10).unwrap(), - data.get(11).unwrap(), - ]); - Some(Self { - status, - revealed_count, - entity_id, - }) - } -} - -/// ECS World State -#[contracttype] -#[derive(Clone, Debug)] -pub struct ECSWorldState { - pub board: BoardComponent, - pub mine_layout: MineLayoutComponent, - pub game_state: GameStateComponent, - pub next_entity_id: u32, -} - -/// External game state for API consumers -#[contracttype] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct GameState { - pub rows: u32, - pub cols: u32, - pub total_mines: u32, - pub status: u32, - pub revealed_count: u32, - pub safe_cells_remaining: u32, -} +mod components; +mod systems; +#[cfg(test)] +mod tests; -/// Reveal result -#[contracttype] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct RevealResult { - pub success: bool, - pub is_mine: bool, - pub adjacent_mines: u32, - pub message: Symbol, -} +use components::{ + BoardComponent, ECSWorldState, GameState, GameStateComponent, MineLayoutComponent, + RevealResult, VisibleCellState, CELL_MINE, COLS, MINES, ROWS, STATUS_LOST, STATUS_PLAYING, + WORLD_KEY, +}; +use systems::{completion_system, place_mines_deterministic}; -/// Visible cell state for querying -#[contracttype] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct VisibleCellState { - pub is_revealed: bool, - pub is_mine: bool, - pub adjacent_mines: u32, -} +#[cfg(test)] +use components::CELL_HIDDEN; -const WORLD_KEY: Symbol = symbol_short!("WORLD"); +use soroban_sdk::{contract, contractimpl, symbol_short, Env, Vec}; #[contract] pub struct MinesweeperContract; @@ -359,7 +32,7 @@ impl MinesweeperContract { // Create mine layout and place mines deterministically let mut mine_layout = MineLayoutComponent::new(&env, next_entity_id); - Self::place_mines_deterministic(&mut mine_layout, &env); + place_mines_deterministic(&mut mine_layout, &env); next_entity_id += 1; // Create game state @@ -436,7 +109,7 @@ impl MinesweeperContract { world_state.game_state.revealed_count += 1; // Check for win condition - Self::completion_system(&mut world_state); + completion_system(&mut world_state); env.storage().instance().set(&WORLD_KEY, &world_state); @@ -517,42 +190,6 @@ impl MinesweeperContract { Self::init_game(env) } - /// Place mines in a deterministic pattern (for proof-friendly implementation) - /// Uses a fixed pattern that can be verified - fn place_mines_deterministic(mine_layout: &mut MineLayoutComponent, env: &Env) { - // Deterministic mine placement - fixed positions for verifiability - // Using a scattered pattern across the 9x9 grid - let mine_positions = [ - (1, 1), - (1, 5), - (2, 7), - (3, 3), - (3, 8), - (4, 6), - (5, 2), - (5, 4), - (5, 7), - (7, 0), - (7, 5), - ]; - - for (row, col) in mine_positions.iter() { - if *row < ROWS && *col < COLS { - mine_layout.set_mine(env, *row, *col); - } - } - } - - /// Completion System - checks if all safe cells are revealed - fn completion_system(world: &mut ECSWorldState) { - // Total safe cells = total cells - mines - let total_safe = (ROWS * COLS) - MINES; - - if world.game_state.revealed_count >= total_safe { - world.game_state.status = STATUS_WON; - } - } - fn to_game_state(_env: &Env, world: &ECSWorldState) -> GameState { let total_safe = (ROWS * COLS) - MINES; let safe_cells_remaining = total_safe - world.game_state.revealed_count; @@ -567,6 +204,3 @@ impl MinesweeperContract { } } } - -#[cfg(test)] -mod tests; diff --git a/examples/minesweeper/src/systems.rs b/examples/minesweeper/src/systems.rs new file mode 100644 index 00000000..3da4d567 --- /dev/null +++ b/examples/minesweeper/src/systems.rs @@ -0,0 +1,39 @@ +use soroban_sdk::Env; + +use crate::components::{ECSWorldState, MineLayoutComponent, COLS, MINES, ROWS, STATUS_WON}; + +/// Place mines in a deterministic pattern (for proof-friendly implementation) +/// Uses a fixed pattern that can be verified +pub(crate) fn place_mines_deterministic(mine_layout: &mut MineLayoutComponent, env: &Env) { + // Deterministic mine placement - fixed positions for verifiability + // Using a scattered pattern across the 9x9 grid + let mine_positions = [ + (1, 1), + (1, 5), + (2, 7), + (3, 3), + (3, 8), + (4, 6), + (5, 2), + (5, 4), + (5, 7), + (7, 0), + (7, 5), + ]; + + for (row, col) in mine_positions.iter() { + if *row < ROWS && *col < COLS { + mine_layout.set_mine(env, *row, *col); + } + } +} + +/// Completion System - checks if all safe cells are revealed +pub(crate) fn completion_system(world: &mut ECSWorldState) { + // Total safe cells = total cells - mines + let total_safe = (ROWS * COLS) - MINES; + + if world.game_state.revealed_count >= total_safe { + world.game_state.status = STATUS_WON; + } +} diff --git a/examples/reversi/Cargo.lock b/examples/reversi/Cargo.lock new file mode 100644 index 00000000..b70c3e32 --- /dev/null +++ b/examples/reversi/Cargo.lock @@ -0,0 +1,1934 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[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 = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[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.118", +] + +[[package]] +name = "cc" +version = "1.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_eval" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cougr-core" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc489e355664841b5fb4d0b19e014c3be1a9b81cd1cd9f7ad29f02fe54a4135" +dependencies = [ + "soroban-sdk", + "wee_alloc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "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 = "ctor" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dtor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "escape-bytes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" + +[[package]] +name = "ethnum" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if 1.0.4", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[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 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if 1.0.4", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if 1.0.4", + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.118", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[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", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "reversi" +version = "0.1.0" +dependencies = [ + "cougr-core", + "soroban-sdk", +] + +[[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_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.8.22", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7192e3a5551a7aeee90d2110b11b615798e81951fd8c8293c87ea7f88b0168f5" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "soroban-env-common" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc49a80a68fc1005847308e63b9fce39874de731940b1807b721d472de3ff01" +dependencies = [ + "arbitrary", + "crate-git-revision", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr", + "wasmparser", +] + +[[package]] +name = "soroban-env-guest" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2334ba1cfe0a170ab744d96db0b4ca86934de9ff68187ceebc09dc342def55" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43af5d53c57bc2f546e122adc0b1cca6f93942c718977379aa19ddd04f06fcec" +dependencies = [ + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "curve25519-dalek", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "generic-array", + "getrandom", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "p256", + "rand", + "rand_chacha", + "sec1", + "sha2", + "sha3", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", + "static_assertions", + "stellar-strkey 0.0.13", + "wasmparser", +] + +[[package]] +name = "soroban-env-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a989167512e3592d455b1e204d703cfe578a36672a77ed2f9e6f7e1bbfd9cc5c" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr", + "syn 2.0.118", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ca06e6c5029d1285e66219cb387a234224e26969ce8ad2bc2d5017e9395d63b" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common", + "soroban-env-host", + "thiserror", +] + +[[package]] +name = "soroban-sdk" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4502f2e018f238a4c5d3212d7d20ea6abcdc6e58babd63b642b693739db30fd1" +dependencies = [ + "arbitrary", + "bytes-lit", + "crate-git-revision", + "ctor", + "derive_arbitrary", + "ed25519-dalek", + "rand", + "rustc_version", + "serde", + "serde_json", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey 0.0.16", + "visibility", +] + +[[package]] +name = "soroban-sdk-macros" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca03e9cf61d241cb9afdd6ddf41f6c25698b3f566a875e7009ea799b89e2bf0a" +dependencies = [ + "darling 0.20.11", + "heck", + "itertools", + "macro-string", + "proc-macro2", + "quote", + "sha2", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", + "syn 2.0.118", +] + +[[package]] +name = "soroban-spec" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa02e07f507cc27406ae0834db4dcf309b78c4cc8776eb3b2d662d66e8859d25" +dependencies = [ + "base64", + "sha2", + "stellar-xdr", + "thiserror", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6835bb510763ef3fa5405e89036e3c8ea6ef5abe55fc52cfe9ac0e38be9d531c" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2", + "soroban-spec", + "stellar-xdr", + "syn 2.0.118", + "thiserror", +] + +[[package]] +name = "soroban-wasmi" +version = "0.31.1-soroban.20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" +dependencies = [ + "smallvec", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[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.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1832fb50c651ad10f734aaf5d31ca5acdfb197a6ecda64d93fcdb8885af913" +dependencies = [ + "crate-git-revision", + "data-encoding", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084afcb0d458c3d5d5baa2d294b18f881e62cc258ef539d8fdf68be7dbe45520" +dependencies = [ + "crate-git-revision", + "data-encoding", + "heapless", +] + +[[package]] +name = "stellar-xdr" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d20dafed80076b227d4b17c0c508a4bbc4d5e4c3d4c1de7cd42242df4b1eaf" +dependencies = [ + "arbitrary", + "base64", + "cfg_eval", + "crate-git-revision", + "escape-bytes", + "ethnum", + "hex", + "serde", + "serde_with", + "sha2", + "stellar-strkey 0.0.13", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[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.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "time" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.118", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmi_arena" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "wasmparser" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" +dependencies = [ + "indexmap 2.14.0", + "semver", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[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-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/examples/reversi/README.md b/examples/reversi/README.md index 0e6593ba..3d33db97 100644 --- a/examples/reversi/README.md +++ b/examples/reversi/README.md @@ -1,237 +1,88 @@ # Reversi -An on-chain Reversi (Othello) game built with the [Cougr](../../README.md) ECS framework on Stellar Soroban. - -## Overview - -This example implements a complete Reversi (Othello) game as a Soroban smart contract. The focus is on the smart contract logic, showcasing how `cougr-core` simplifies on-chain turn-based game development with isolated systems and typed components. - -## ECS Design - -### Components - -All components derive `Clone + Debug`, annotated with `#[contracttype]` for XDR-serialisation, and implement `ComponentTrait` for Cougr's byte-level storage layer. - -```rust -pub struct BoardComponent { - pub cells: Vec, // 64 elements, row-major; 0=empty, 1=black, 2=white - pub width: u32, // always 8 - pub height: u32, // always 8 -} - -pub struct TurnComponent { - pub current_player: u32, // 1=black, 2=white - pub pass_count: u32, // 0=normal, 1=opponent skipped, 2=both locked → game ends -} - -pub struct GameStatusComponent { - pub status: u32, // 0=active, 1=finished -} - -pub struct ScoreComponent { - pub black_count: u32, - pub white_count: u32, -} -``` - -The entire game state is stored under a single `"WORLD"` key as `ECSWorldState`: - -```rust -pub struct ECSWorldState { - pub board: BoardComponent, - pub turn: TurnComponent, - pub status: GameStatusComponent, - pub score: ScoreComponent, - pub player_one: Address, // plays BLACK (1) - pub player_two: Address, // plays WHITE (2) -} -``` - -### Systems - -Systems run in sequence inside `submit_move`: - -1. **MoveValidationSystem** — rejects occupied cells and moves with no bracketed pieces -2. **FlipResolutionSystem** — places piece and flips all bracketed opponent pieces in 8 directions -3. **ScoringSystem** — recomputes piece counts from board state -4. **TurnSystem + PassSystem** — advances to opponent; auto-skips if opponent has no legal move; signals game end when both have no moves -5. **EndConditionSystem** — sets status=1 when pass_count≥2 or board is full - -## Contract API - -### Functions - -| Function | Description | -|----------|-------------| -| `init_game(player_one, player_two)` | Initialise board; Black moves first | -| `submit_move(player, row, col)` | Place piece; panics on illegal move or wrong turn | -| `get_state() → GameState` | Current player, pass_count state, active/finished | -| `get_board() → BoardState` | Raw cell array (row-major, 0-indexed) | -| `get_score() → ScoreState` | Piece counts and winner (0=ongoing, 1=black, 2=white, 3=draw) | +> **Transitional example**: This example uses an older Cougr pattern and is preserved +> for compatibility reference. For the current recommended approach, see `snake`. -### Return Types - -```rust -pub struct GameState { - pub current_player: u32, // 1=black, 2=white - pub pass_count: u32, // 0, 1, or 2 - pub status: u32, // 0=active, 1=finished -} - -pub struct BoardState { - pub cells: Vec, // 64 values, row-major - pub width: u32, // 8 - pub height: u32, // 8 -} - -pub struct ScoreState { - pub black_count: u32, - pub white_count: u32, - pub winner: u32, // 0=ongoing, 1=black, 2=white, 3=draw -} -``` - -## Rules - -- Black moves first. Players alternate turns. -- A move must flip at least one opponent piece in a straight line (horizontal, vertical, or diagonal). -- If a player has no legal move, their turn is automatically skipped (`pass_count` = 1). -- Game ends when both players have no legal moves (`pass_count` = 2) or the board is full. -- The player with more pieces wins. - -## Implementation Patterns - -### Flip Algorithm +An on-chain Reversi (Othello) game built with the [Cougr](../../README.md) ECS framework on Stellar Soroban. -Reversi's core mechanic: a move is legal only if it brackets at least one opponent piece in a straight line, ending with one of the mover's own pieces. The contract checks all 8 directions using a direction-vector table: +## Purpose and pattern -```rust -const DIRS: [(i32, i32); 8] = [ - (-1, -1), (-1, 0), (-1, 1), // NW N NE - ( 0, -1), ( 0, 1), // W E - ( 1, -1), ( 1, 0), ( 1, 1), // SW S SE -]; -``` +This example demonstrates a turn-based, perfect-information board game where every move +mutates shared board state and can trigger automatic turn-skipping. It showcases Cougr's +`ComponentTrait` pattern for typed, byte-serializable game state — components are plain +Rust structs annotated with `#[contracttype]` that implement `ComponentTrait` directly, +rather than being scanned through `SimpleWorld`/`SimpleQueryBuilder`. This is a lighter-weight +integration suited to a single fixed-shape game state (one board, two players) with no +dynamic entity population. -`flips_in_dir` walks one step at a time from `(row+dr, col+dc)`: -- Counts consecutive opponent pieces. -- Returns the count if it finds one of the player's own pieces at the end (bracket found). -- Returns 0 if it hits an empty cell or the board edge (no bracket). +## Public contract API -`FlipResolutionSystem` then re-walks each direction where `flips_in_dir > 0` and overwrites those cells with the mover's colour. `Vec::set` on a `soroban_sdk::Vec` mutates in place, so the board component is taken by value and returned. +| Function | Parameters | Returns | Description | +|---|---|---|---| +| `init_game` | `player_one: Address`, `player_two: Address` | — | Initializes an 8×8 board with the standard opening position; `player_one` plays Black and moves first. Panics if already initialized. | +| `submit_move` | `player: Address`, `row: u32`, `col: u32` | — | Validates and applies a move (requires `player.require_auth()`), flips bracketed opponent pieces, recomputes score, and advances turn/pass state. Panics on illegal move, wrong turn, or finished game. | +| `get_state` | — | `GameState` | Current player, pass-count state, and active/finished status. | +| `get_board` | — | `BoardState` | Raw 64-cell board array (row-major, 0=empty/1=black/2=white), plus width/height. | +| `get_score` | — | `ScoreState` | Black/white piece counts and winner (0=ongoing, 1=black, 2=white, 3=draw). | -### Pass State Machine +## Architecture overview -`pass_count` is recomputed from scratch every turn — it is not accumulated: +There is no `GameApp` tick loop — `submit_move` runs all systems synchronously in a fixed +pipeline on each invocation: ``` -After every move: - if opponent has legal moves → pass_count = 0 (normal alternation) - elif current has legal moves → pass_count = 1 (opponent auto-passed, current continues) - else → pass_count = 2 (both locked → EndConditionSystem ends game) +submit_move + └─ MoveValidationSystem → rejects occupied cells or moves that flip nothing + └─ FlipResolutionSystem → places the piece, flips bracketed pieces in all 8 directions + └─ ScoringSystem → recomputes black/white piece counts from the board + └─ TurnSystem/PassSystem → advances to the opponent, or auto-passes if they have no legal move + └─ EndConditionSystem → marks the game finished when both players pass or the board is full ``` -This is handled by `TurnSystem` delegating to `PassSystem` when the opponent has no moves. +Each system is a pure function over `components.rs` types (`BoardComponent`, `TurnComponent`, +`GameStatusComponent`, `ScoreComponent`); `lib.rs` owns the single `ECSWorldState` aggregate +and is the only place that touches contract storage. -## Test Coverage +## Storage model -| Category | Tests | What is verified | -|----------|-------|-----------------| -| Initialisation | 3 | Board layout, opening score (2-2), initial turn state | -| Move validation | 2 | Occupied-cell rejection, no-flip rejection | -| Flip mechanics | 3 | Horizontal, vertical, and diagonal flips | -| Score tracking | 1 | Counts update correctly after a flip | -| Turn management | 2 | Turn alternation, wrong-player rejection | -| Pass / sequence | 2 | Normal pass_count=0, multi-move game stays active | -| Re-initialisation | 1 | Second `init_game` call is rejected | -| **Total** | **14** | | +The entire game lives under one instance-storage key (`WORLD_KEY`, `symbol_short!("WORLD")`) +as a single `ECSWorldState` struct bundling the board, turn, status, score, and both player +addresses. Instance storage is used — not persistent or temporary — because there is exactly +one game per contract instance and the state must survive for the lifetime of that instance +with no per-entry TTL management. -## Board Layout +## Main gameplay flow -Cells are stored row-major: `cells[row * 8 + col]`. +1. Deployer calls `init_game(player_one, player_two)`; board is seeded with the four-piece + opening position, Black (`player_one`) to move, status active. +2. Black calls `submit_move(player, row, col)`. The contract checks turn order and move + legality (the move must bracket at least one opponent piece in a straight line). +3. On a legal move: the piece is placed, bracketed opponent pieces flip, score is + recomputed, and turn passes to White — unless White has no legal move, in which case + Black continues and `pass_count` is set to 1. +4. Players alternate `submit_move` calls. If neither player has a legal move + (`pass_count` reaches 2) or the board fills, `EndConditionSystem` sets status to finished. +5. Either player calls `get_score` to read the final piece counts and winner. -``` - 0 1 2 3 4 5 6 7 - 0 . . . . . . . . - 1 . . . . . . . . - 2 . . . . . . . . - 3 . . . W B . . . - 4 . . . B W . . . - 5 . . . . . . . . - 6 . . . . . . . . - 7 . . . . . . . . -``` +## Cougr APIs used -## Development +- `cougr_core::component::ComponentTrait` — gives each component (`BoardComponent`, + `TurnComponent`, `GameStatusComponent`, `ScoreComponent`) a `component_type()` symbol and + byte-level `serialize`/`deserialize`, chosen here because the game has a single fixed set + of components per instance rather than a dynamic entity population that would benefit from + `SimpleWorld`/`SimpleQueryBuilder` scanning. -Requires Rust with `wasm32v1-none` target and [Stellar CLI](https://developers.stellar.org/docs/tools/developer-tools/cli/stellar-cli). +## Build and test commands ```bash -# Run tests cargo test - -# Format check -cargo fmt --check - -# Lint -cargo clippy --all-targets --all-features -- -D warnings - -# Build WASM stellar contract build ``` -## Playing the Game - -After building the WASM (`stellar contract build`), deploy and play on Testnet: - -```bash -# Generate two player identities and fund them via Friendbot -stellar keys generate reversi_black -stellar keys generate reversi_white -stellar keys fund reversi_black --network -stellar keys fund reversi_white --network - -# Deploy the contract -CONTRACT_ID=$(stellar contract deploy \ - --wasm target/wasm32v1-none/release/reversi.wasm \ - --network \ - --source reversi_black) - -# Initialise (reversi_black plays Black, reversi_white plays White) -stellar contract invoke --id $CONTRACT_ID --network --source reversi_black \ - -- init_game \ - --player_one reversi_black \ - --player_two reversi_white - -# Black places at row=3, col=2 (flips (3,3) horizontally) -stellar contract invoke --id $CONTRACT_ID --network --source reversi_black \ - -- submit_move \ - --player reversi_black \ - --row 3 --col 2 - -# Check the board after the move -stellar contract invoke --id $CONTRACT_ID --network --source reversi_black \ - -- get_board -# → {"cells":[0,0,...,1,1,1,0,...,1,2,0,...],"width":8,"height":8} -# (3,2)=1 (3,3)=1 (3,4)=1 — three Black pieces in a row - -# Check whose turn it is -stellar contract invoke --id $CONTRACT_ID --network --source reversi_black \ - -- get_state -# → {"current_player":2,"pass_count":0,"status":0} -# current_player=2 → White's turn, game active - -# Check the score -stellar contract invoke --id $CONTRACT_ID --network --source reversi_black \ - -- get_score -# → {"black_count":4,"white_count":1,"winner":0} -# winner=0 → game ongoing - -# White responds at row=2, col=3 -stellar contract invoke --id $CONTRACT_ID --network --source reversi_white \ - -- submit_move \ - --player reversi_white \ - --row 2 --col 3 -``` +## Known limitations -`submit_move` panics (transaction fails) on an illegal move or wrong turn — the chain rejects it cleanly. `get_score` returns `winner = 0` while the game is active and `1` (Black), `2` (White), or `3` (draw) once it ends. +- Does not use `GameApp`, `ScheduleStage`, or `SimpleWorld` — game logic is invoked directly + from contract entrypoints rather than through a tick-based scheduler, since a turn-based + game with one decision point per call does not need staged scheduling. +- No timeout/forfeit mechanism for an unresponsive player. +- No spectator or replay API beyond the three read-only getters. diff --git a/examples/sudoku/Cargo.lock b/examples/sudoku/Cargo.lock new file mode 100644 index 00000000..0b22d8e5 --- /dev/null +++ b/examples/sudoku/Cargo.lock @@ -0,0 +1,1934 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[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 = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[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.118", +] + +[[package]] +name = "cc" +version = "1.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_eval" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cougr-core" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc489e355664841b5fb4d0b19e014c3be1a9b81cd1cd9f7ad29f02fe54a4135" +dependencies = [ + "soroban-sdk", + "wee_alloc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "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 = "ctor" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dtor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "escape-bytes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" + +[[package]] +name = "ethnum" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if 1.0.4", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[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 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if 1.0.4", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if 1.0.4", + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.118", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[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", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[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_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.8.22", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7192e3a5551a7aeee90d2110b11b615798e81951fd8c8293c87ea7f88b0168f5" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "soroban-env-common" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc49a80a68fc1005847308e63b9fce39874de731940b1807b721d472de3ff01" +dependencies = [ + "arbitrary", + "crate-git-revision", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr", + "wasmparser", +] + +[[package]] +name = "soroban-env-guest" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2334ba1cfe0a170ab744d96db0b4ca86934de9ff68187ceebc09dc342def55" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43af5d53c57bc2f546e122adc0b1cca6f93942c718977379aa19ddd04f06fcec" +dependencies = [ + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "curve25519-dalek", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "generic-array", + "getrandom", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "p256", + "rand", + "rand_chacha", + "sec1", + "sha2", + "sha3", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", + "static_assertions", + "stellar-strkey 0.0.13", + "wasmparser", +] + +[[package]] +name = "soroban-env-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a989167512e3592d455b1e204d703cfe578a36672a77ed2f9e6f7e1bbfd9cc5c" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr", + "syn 2.0.118", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ca06e6c5029d1285e66219cb387a234224e26969ce8ad2bc2d5017e9395d63b" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common", + "soroban-env-host", + "thiserror", +] + +[[package]] +name = "soroban-sdk" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4502f2e018f238a4c5d3212d7d20ea6abcdc6e58babd63b642b693739db30fd1" +dependencies = [ + "arbitrary", + "bytes-lit", + "crate-git-revision", + "ctor", + "derive_arbitrary", + "ed25519-dalek", + "rand", + "rustc_version", + "serde", + "serde_json", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey 0.0.16", + "visibility", +] + +[[package]] +name = "soroban-sdk-macros" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca03e9cf61d241cb9afdd6ddf41f6c25698b3f566a875e7009ea799b89e2bf0a" +dependencies = [ + "darling 0.20.11", + "heck", + "itertools", + "macro-string", + "proc-macro2", + "quote", + "sha2", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", + "syn 2.0.118", +] + +[[package]] +name = "soroban-spec" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa02e07f507cc27406ae0834db4dcf309b78c4cc8776eb3b2d662d66e8859d25" +dependencies = [ + "base64", + "sha2", + "stellar-xdr", + "thiserror", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "25.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6835bb510763ef3fa5405e89036e3c8ea6ef5abe55fc52cfe9ac0e38be9d531c" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2", + "soroban-spec", + "stellar-xdr", + "syn 2.0.118", + "thiserror", +] + +[[package]] +name = "soroban-wasmi" +version = "0.31.1-soroban.20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" +dependencies = [ + "smallvec", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[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.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1832fb50c651ad10f734aaf5d31ca5acdfb197a6ecda64d93fcdb8885af913" +dependencies = [ + "crate-git-revision", + "data-encoding", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084afcb0d458c3d5d5baa2d294b18f881e62cc258ef539d8fdf68be7dbe45520" +dependencies = [ + "crate-git-revision", + "data-encoding", + "heapless", +] + +[[package]] +name = "stellar-xdr" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d20dafed80076b227d4b17c0c508a4bbc4d5e4c3d4c1de7cd42242df4b1eaf" +dependencies = [ + "arbitrary", + "base64", + "cfg_eval", + "crate-git-revision", + "escape-bytes", + "ethnum", + "hex", + "serde", + "serde_with", + "sha2", + "stellar-strkey 0.0.13", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "sudoku" +version = "0.1.0" +dependencies = [ + "cougr-core", + "soroban-sdk", +] + +[[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.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "time" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.118", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmi_arena" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "wasmparser" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" +dependencies = [ + "indexmap 2.14.0", + "semver", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[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-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/examples/sudoku/README.md b/examples/sudoku/README.md index 89ad1b9e..162521fb 100644 --- a/examples/sudoku/README.md +++ b/examples/sudoku/README.md @@ -1,189 +1,88 @@ # Sudoku -An on-chain Sudoku puzzle built with the [Cougr](../../README.md) ECS framework on Stellar Soroban. - -## Overview - -This example implements a 9×9 Sudoku puzzle as a Soroban smart contract. The focus is on strict constraint validation — row, column, and 3×3 block rules are enforced on every move — showcasing how `cougr-core` separates validation logic, board mutation, and completion detection into discrete systems. - -## ECS Design - -### Components - -All components derive `Clone + Debug`, are annotated with `#[contracttype]`, and implement `ComponentTrait`. - -```rust -pub struct BoardComponent { - pub cells: Vec, // 81 elements, row-major; 0=empty, 1–9=value -} - -pub struct FixedCellsComponent { - pub fixed: Vec, // 81 elements; true=immutable (puzzle givens) -} - -pub struct GameStatusComponent { - pub status: u32, // 0=playing, 1=solved -} - -pub struct MoveCountComponent { - pub moves: u32, // number of successful placements -} -``` - -All four components are stored under a single `"WORLD"` key as `ECSWorldState`. - -### Systems - -Systems run in sequence inside `submit_value`: - -1. **InputSystem** — validates that coordinates are in-bounds, the target cell is editable, and the value is 1–9; panics otherwise -2. **PlacementValidationSystem** — checks row, column, and 3×3 block constraints -3. **BoardUpdateSystem** — places the value on the board -4. **EndConditionSystem** — detects puzzle completion (all cells filled, all constraints satisfied) - -## Contract API - -### Functions +> **Transitional example**: This example uses an older Cougr pattern and is preserved +> for compatibility reference. For the current recommended approach, see `snake`. -| Function | Description | -|----------|-------------| -| `init_game(puzzle)` | Load a caller-supplied 81-cell puzzle; status = playing | -| `submit_value(row, col, value)` | Place a digit; panics on any invalid input | -| `get_state() → GameState` | Current status and move count | -| `get_cell(row, col) → CellState` | Value and fixed flag for one cell | -| `is_solved() → bool` | True when puzzle is complete | - -### Return Types +An on-chain Sudoku puzzle built with the [Cougr](../../README.md) ECS framework on Stellar Soroban. -```rust -pub struct GameState { - pub status: u32, // 0=playing, 1=solved - pub moves: u32, -} +## Purpose and pattern -pub struct CellState { - pub value: u32, // 0=empty, 1–9 - pub fixed: bool, // true=immutable -} -``` +This example demonstrates a single-player constraint-validation puzzle where moves are +checked against row/column/3×3-block rules rather than against an opponent. It showcases +Cougr's `ComponentTrait` pattern for typed, byte-serializable game state: each piece of +state (board, fixed-cell mask, status, move counter) is its own component with manual +`serialize`/`deserialize`, demonstrating explicit byte-layout control for a fixed-size grid. -## Rules +## Public contract API -- The board is 9×9, divided into nine 3×3 blocks. -- Fixed cells contain the puzzle's given digits and cannot be overwritten. -- A placement is valid only if the value does not already appear in the same row, column, or 3×3 block. -- The puzzle is solved when all 81 cells are filled and all constraints are satisfied. +| Function | Parameters | Returns | Description | +|---|---|---|---| +| `init_game` | `puzzle: Vec` | — | Seeds an empty 9×9 board from an 81-cell array (0=empty, 1–9=given). Cells with a non-zero starting value become fixed. Panics if already initialized, the length isn't 81, or a value exceeds 9. | +| `submit_value` | `row: u32`, `col: u32`, `value: u32` | — | Validates and places `value` at `(row, col)`. Panics if the puzzle is solved, the cell is fixed/out-of-bounds, the value is outside 1–9, or the placement violates a row/column/block constraint. | +| `get_state` | — | `GameState` | Current status (0=playing, 1=solved) and move count. | +| `get_cell` | `row: u32`, `col: u32` | `CellState` | Current value at the cell and whether it is a fixed given. | +| `is_solved` | — | `bool` | Whether the puzzle is fully and correctly filled. | -## Implementation Patterns +## Architecture overview -### Constraint Validation +`submit_value` runs a fixed validation/update pipeline synchronously on each call — there is +no `GameApp` tick loop: -`placement_validation_system` checks three independent constraints — row, column, and block — and returns `false` on the first violation. Each helper scans only the relevant cells, skipping the target cell itself: - -```rust -fn check_row(cells: &Vec, row: u32, skip_col: u32, value: u32) -> bool { - for c in 0..BOARD_SIZE { - if c != skip_col && get_cell(cells, row, c) == value { - return false; - } - } - true -} ``` - -The block helper derives the top-left corner from integer division: - -```rust -let block_row = (row / 3) * 3; -let block_col = (col / 3) * 3; +submit_value + └─ InputValidationSystem → bounds check, rejects fixed cells, rejects out-of-range values + └─ PlacementValidationSystem → checks row, column, and 3×3 block constraints + └─ BoardUpdateSystem → writes the value into the board component + └─ CompletionSystem → re-validates every row/column/block for full-board completion + └─ EndConditionSystem → sets status=solved when CompletionSystem passes ``` -### Completion Detection - -`end_condition_system` delegates to `completion_system`, which first scans for any empty cell (fast exit), then verifies every row, column, and block using the same `seen[v]` bitmask pattern — `O(81)` total work regardless of board state. +`components.rs` defines the data (`BoardComponent`, `FixedCellsComponent`, +`GameStatusComponent`, `MoveCountComponent`); `systems.rs` holds the pure validation and +update functions; `lib.rs` owns the single `ECSWorldState` aggregate and is the only module +that touches contract storage. -## Test Coverage +## Storage model -| Category | Tests | What is verified | -|----------|-------|-----------------| -| Initialisation | 5 | Game state, fixed values, empty cells, re-init guard, is_solved at start | -| Move validation | 7 | Valid move, fixed-cell rejection, value 0 and 10 rejected, row/col/block conflict | -| Score tracking | 1 | Move counter increments correctly | -| Completion | 2 | Full solution reaches solved, moves rejected after solved | -| **Total** | **15** | | +The full puzzle state lives under one instance-storage key (`WORLD_KEY`, +`symbol_short!("WORLD")`) as a single `ECSWorldState` bundling the board, the fixed-cell +mask, status, and move count. Instance storage is used because there is exactly one puzzle +per contract instance with no per-entry expiry needed; persistent or temporary storage would +add TTL bookkeeping this single-instance puzzle doesn't need. -## Board Layout +## Main gameplay flow -The puzzle is passed to `init_game` as a flat 81-element `Vec` in row-major order. `0` marks an editable cell; `1–9` marks a fixed given. Example fixture (`.` = editable): +1. Deployer calls `init_game(puzzle)` with an 81-cell array representing the starting + grid; non-zero cells are marked fixed and cannot be overwritten. +2. Player calls `submit_value(row, col, value)` for an empty cell. The contract checks the + cell isn't fixed, the value is 1–9, and the placement doesn't repeat a value in the same + row, column, or 3×3 block. +3. On a legal placement, the board updates and the move counter increments. +4. After every placement, `CompletionSystem` re-checks all 9 rows, 9 columns, and 9 blocks. + When the board is completely and correctly filled, status flips to solved. +5. Caller reads `is_solved` or `get_state` to check completion; `get_cell` reads individual + cells (e.g., to render the board off-chain). -``` - 0 1 2 3 4 5 6 7 8 - 0 5 3 . . 7 . . . . - 1 6 . . 1 9 5 . . . - 2 . 9 8 . . . . 6 . - 3 8 . . . 6 . . . 3 - 4 4 . . 8 . 3 . . 1 - 5 7 . . . 2 . . . 6 - 6 . 6 . . . . 2 8 . - 7 . . . 4 1 9 . . 5 - 8 . . . . 8 . . 7 9 -``` +## Cougr APIs used -Cells are stored row-major: `cells[row * 9 + col]`. +- `cougr_core::component::ComponentTrait` — gives each component + (`BoardComponent`, `FixedCellsComponent`, `GameStatusComponent`, `MoveCountComponent`) a + `component_type()` symbol and explicit byte `serialize`/`deserialize`, chosen because the + puzzle has one fixed-shape 81-cell grid rather than a dynamic entity population that would + benefit from `SimpleWorld`/`SimpleQueryBuilder` scanning. -## Development - -Requires Rust with `wasm32v1-none` target and [Stellar CLI](https://developers.stellar.org/docs/tools/developer-tools/cli/stellar-cli). +## Build and test commands ```bash -# Run tests cargo test - -# Format check -cargo fmt --check - -# Lint -cargo clippy --all-targets --all-features -- -D warnings - -# Build WASM stellar contract build ``` -## Playing the Game - -After building the WASM (`stellar contract build`), deploy and play on Testnet: - -```bash -# Generate a player identity and fund via Friendbot -stellar keys generate sudoku_player -stellar keys fund sudoku_player --network - -# Deploy the contract -CONTRACT_ID=$(stellar contract deploy \ - --wasm target/wasm32v1-none/release/sudoku.wasm \ - --network \ - --source sudoku_player) - -# Initialise the puzzle (pass a flat 81-element JSON array; 0=empty, 1–9=fixed) -stellar contract invoke --id $CONTRACT_ID --network --source sudoku_player \ - -- init_game \ - --puzzle '[5,3,0,0,7,0,0,0,0,6,0,0,1,9,5,0,0,0,0,9,8,0,0,0,0,6,0,8,0,0,0,6,0,0,0,3,4,0,0,8,0,3,0,0,1,7,0,0,0,2,0,0,0,6,0,6,0,0,0,0,2,8,0,0,0,0,4,1,9,0,0,5,0,0,0,0,8,0,0,7,9]' - -# Read the initial state -stellar contract invoke --id $CONTRACT_ID --network --source sudoku_player \ - -- get_state - -# Read a cell -stellar contract invoke --id $CONTRACT_ID --network --source sudoku_player \ - -- get_cell --row 0 --col 2 - -# Submit a value -stellar contract invoke --id $CONTRACT_ID --network --source sudoku_player \ - -- submit_value --row 0 --col 2 --value 4 - -# Check if solved -stellar contract invoke --id $CONTRACT_ID --network --source sudoku_player \ - -- is_solved -``` +## Known limitations -`submit_value` panics (transaction fails) on any invalid input — the chain rejects it cleanly. +- Does not use `GameApp`, `ScheduleStage`, or `SimpleWorld` — validation runs directly from + contract entrypoints since a single-player puzzle with one decision point per call has no + need for staged scheduling. +- `init_game` accepts any caller and does not verify the supplied puzzle has a unique + solution; puzzle generation/validation is left to the caller. +- No undo, hint, or partial-progress save beyond the committed board state. diff --git a/examples/tic_tac_toe/.github/workflows/ci.yml b/examples/tic_tac_toe/.github/workflows/ci.yml deleted file mode 100644 index f1fd04d0..00000000 --- a/examples/tic_tac_toe/.github/workflows/ci.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Tic Tac Toe CI - -on: - push: - branches: [main] - paths: - - "examples/tic_tac_toe/**" - pull_request: - branches: [main] - paths: - - "examples/tic_tac_toe/**" - -env: - CARGO_TERM_COLOR: always - -jobs: - build-and-test: - runs-on: ubuntu-latest - defaults: - run: - working-directory: examples/tic_tac_toe - - steps: - - uses: actions/checkout@v4 - - - name: Install Rust - uses: dtolnay/rust-action@stable - - - name: Cache cargo registry - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Build - run: cargo build --verbose - - - name: Run tests - run: cargo test --verbose - - - name: Install Stellar CLI - run: cargo install stellar-cli --locked - - - name: Build WASM - run: stellar contract build - - - name: Upload WASM artifact - uses: actions/upload-artifact@v4 - with: - name: tic_tac_toe_wasm - path: examples/tic_tac_toe/target/tic_tac_toe.wasm diff --git a/examples/tic_tac_toe/README.md b/examples/tic_tac_toe/README.md index 431a4571..49e8bc81 100644 --- a/examples/tic_tac_toe/README.md +++ b/examples/tic_tac_toe/README.md @@ -1,292 +1,127 @@ -# Tic Tac Toe On-Chain Game +# Tic Tac Toe + +> **Transitional example**: This example uses an older Cougr pattern and is preserved +> for compatibility reference. For the current recommended approach, see `snake`. + +An on-chain Tic Tac Toe game built with the [Cougr](../../README.md) ECS framework on +Stellar Soroban. Note: although this example already uses cougr-core's newest macro-based +component pattern (`impl_rich_component!` / `impl_component!` / `impl_soroban_game!`), it is +not on the canonical examples list, so it is marked transitional per the project standard — +see "Cougr APIs used" below for why these specific macros were chosen over the manual +`ComponentTrait` implementations used in other transitional examples like `reversi`. + +## Purpose and pattern + +This example demonstrates a two-player, perfect-information board game with a single shared +entity for all game state. It showcases Cougr's `SimpleWorld` entity/component storage driven +through the macro-generated `RichComponentTrait`/`ComponentTrait` implementations +(`impl_rich_component!`, `impl_component!`) and the `SorobanGame` trait +(`impl_soroban_game!`), which together remove the need to hand-write `serialize`/`deserialize` +or repeat the storage key in every contract function. + +## Public contract API + +| Function | Parameters | Returns | Description | +|---|---|---|---| +| `init_game` | `player_x: Address`, `player_o: Address` | `GameState` | Spawns the single game entity, seeds an empty 9-cell board, and sets X to move first. Overwrites any previous game. | +| `make_move` | `player: Address`, `position: u32` | `MoveResult` | Validates and applies a move at `position` (0–8). Returns `success: false` with a status `message` symbol instead of panicking on illegal input. | +| `get_state` | — | `GameState` | Current board, both player addresses, whose turn it is, move count, and status. | +| `is_valid_move` | `position: u32` | `bool` | Whether `position` is a legal move right now (in range, empty cell, game still in progress). | +| `get_winner` | — | `Option
` | The winning player's address, or `None` if the game is in progress or drawn. | +| `reset_game` | — | `GameState` | Re-initializes the board with the same two players, discarding moves. | + +### Status codes + +`GameState.status`: `0` = in progress, `1` = X wins, `2` = O wins, `3` = draw. + +`MoveResult.message`: `ok`, `invalid` (out of range), `occupied`, `notturn`, `notplay`, +`gameover`. + +## Architecture overview + +``` +src/ +├── lib.rs # #[contract] struct, SorobanGame wiring, #[contractimpl] entrypoints +├── components.rs # Board, Players, TurnState component structs + macro invocations +└── systems.rs # detect_winner: pure win/draw detection over board cells +``` + +`make_move` runs a fixed validation/execution sequence synchronously on each call — there is +no `GameApp` tick loop: + +``` +make_move + └─ load Players, TurnState, Board from the single game entity + └─ validate: game not over, position in range, caller is a player, caller's turn, cell empty + └─ apply the mark, call systems::detect_winner over the updated board + └─ persist updated Board and TurnState, save the world +``` + +`components.rs` owns the three `SimpleWorld` components and the shared `GAME_ENTITY` +constant; `systems.rs` holds the one pure function (`detect_winner`) that contains no +storage access; `lib.rs` owns the `#[contract]` struct, the `SorobanGame` wiring, and is the +only module that loads/saves the `SimpleWorld`. + +## Storage model + +All state lives in **instance storage**, managed transparently by `impl_soroban_game!` +through `SorobanGame::load_world`/`save_world` under a single key (`"ttt_world"`). Within +that one `SimpleWorld`, all three components (`Board`, `Players`, `TurnState`) are attached +to a single fixed entity id (`GAME_ENTITY = 1`), since there is exactly one game per contract +instance and no dynamic entity population to query. Instance storage is appropriate here +because the game state must live for the lifetime of the contract instance with no per-entry +TTL management. + +## Main gameplay flow + +1. Deployer calls `init_game(player_x, player_o)`; a new entity is spawned, the board is + seeded with 9 empty cells, and `TurnState` is set to X's turn, move count 0, status + in-progress. +2. X calls `make_move(player_x, position)`. The contract checks the game isn't over, the + position is in range, the caller is a registered player, and it's their turn. +3. On a legal move, the cell is marked, `systems::detect_winner` re-checks all 8 winning + lines plus the draw condition, and turn state is updated (status, move count, whose turn + is next). +4. Players alternate `make_move` calls until `detect_winner` reports a win (status `1` or + `2`) or a draw (status `3`), after which further moves return `gameover`. +5. Either player can call `get_state` or `get_winner` to read the outcome, or `reset_game` to + start over with the same two players. + +## Cougr APIs used + +- `cougr_core::{impl_rich_component!}` — used for `Board` (holds a `Vec`) and `Players` + (holds two `Address` values). Both fields require Soroban's XDR codec rather than fixed-size + byte packing, so `impl_rich_component!` was chosen to get `RichComponentTrait` for free from + the `#[contracttype]` derive, avoiding a hand-written `serialize`/`deserialize` pair for + `Vec` and `Address` like the one `reversi`'s `components.rs` still carries. +- `cougr_core::{impl_component!}` — used for `TurnState`, which is three fixed-size plain + fields (`bool`, `u32`, `u32`). `impl_component!` generates a compact, fully typed + `ComponentTrait` implementation (byte-packed, not XDR) since there are no `Address`/`Vec` + fields needing the heavier rich-component codec. +- `cougr_core::game::SorobanGame` / `impl_soroban_game!` — generates `load_world`/ + `save_world` for the `#[contract]` struct so every entrypoint can read and persist the + `SimpleWorld` without repeating the instance-storage key (`"ttt_world"`) or its + get/set boilerplate. +- `cougr_core::simple_world::SimpleWorld` — used as the single source of truth for the game's + three components, attached to one fixed entity rather than a dynamic population, since + tic-tac-toe has exactly one game per contract instance. + +This example does not use `GameApp`, `ScheduleStage`, `SimpleQueryBuilder`, `auth`, or +`privacy` — see Known limitations. + +## Build and test commands -A fully functional Tic Tac Toe game implemented as a Soroban smart contract on the Stellar blockchain, demonstrating the **Cougr-Core** ECS (Entity Component System) framework for on-chain gaming. - -## Cougr-Core ECS Integration - -All game components implement `cougr_core::component::ComponentTrait`: - -```rust -impl ComponentTrait for BoardComponent { - fn component_type() -> Symbol { - symbol_short!("board") - } - - fn serialize(&self, env: &Env) -> Bytes { /* ... */ } - fn deserialize(env: &Env, data: &Bytes) -> Option<Self> { /* ... */ } -} - - -## ECS System Pattern -Game logic is organized into discrete systems: -| System | Responsibility | -| ---------------------- | ------------------------------------------------- | -| `validation_system` | Enforces game rules (turn order, valid positions) | -| `execution_system` | Applies moves to the board | -| `win_detection_system` | Checks all 8 winning patterns | -| `turn_system` | Manages turn transitions | - - -# Features -| Feature | Description | -| -------------------- | ------------------------------------------------------ | -| Two-player gameplay | Uses Stellar addresses for player identification | -| Turn-based mechanics | X always goes first, enforced turn order | -| Win detection | All 8 patterns (3 rows, 3 columns, 2 diagonals) | -| Draw detection | Recognizes full board with no winner | -| Move validation | Rejects invalid positions, occupied cells, wrong turns | -| Game reset | Restart with same players | - - -# Prerequisites -| Requirement | Version | -| ----------- | --------------------- | -| Rust | 1.70.0+ | -| Stellar CLI | 25.0.0+ (recommended) | -cargo install stellar-cli - -## Building -# Build for testing -cargo build - -# Build optimized WASM -stellar contract build - -## Testing +```bash cargo test -| Test Category | Count | Coverage | -| -------------- | ------ | ----------------------------------------------- | -| Initialization | 2 | Game setup, state retrieval | -| Valid moves | 3 | X moves, O moves, position validation | -| Invalid moves | 5 | Wrong turn, occupied, out of bounds, non-player | -| Win conditions | 8 | All rows, columns, diagonals | -| Draw | 2 | Full board, post-draw state | -| Game over | 2 | No moves after win/draw | -| Reset | 2 | Mid-game reset, post-win reset | -| State | 3 | Persistence, move counting, winner retrieval | -| **Total** | **33** | **All passing** | - - -## Contract API -# Functions -| Function | Parameters | Returns | Description | -| --------------- | -------------------------------------- | ----------------- | ----------------------- | -| `init_game` | `player_x: Address, player_o: Address` | `GameState` | Initialize new game | -| `make_move` | `player: Address, position: u32` | `MoveResult` | Make a move (0-8) | -| `get_state` | — | `GameState` | Get current state | -| `is_valid_move` | `position: u32` | `bool` | Check if move is valid | -| `get_winner` | — | `Option
` | Get winner's address | -| `reset_game` | — | `GameState` | Reset with same players | - - -## Board Positions - 0 | 1 | 2 ------------ - 3 | 4 | 5 ------------ - 6 | 7 | 8 - - ## Data Structures - # GameState - | Field | Type | Description | -| ------------ | ---------- | -------------------------------------- | -| `cells` | `Vec` | Board state (0=Empty, 1=X, 2=O) | -| `player_x` | `Address` | Player X's address | -| `player_o` | `Address` | Player O's address | -| `is_x_turn` | `bool` | True if X's turn | -| `move_count` | `u32` | Total moves made | -| `status` | `u32` | 0=InProgress, 1=XWins, 2=OWins, 3=Draw | - -# MoveResult -| Field | Type | Description | -| ------------ | ----------- | ---------------------- | -| `success` | `bool` | Whether move succeeded | -| `game_state` | `GameState` | Updated state | -| `message` | `Symbol` | Status code | - -# Error Messages -| Code | Meaning | -| ---------- | -------------------------------- | -| `ok` | Move successful | -| `invalid` | Position out of bounds (not 0-8) | -| `occupied` | Cell already has a mark | -| `notturn` | Not the player's turn | -| `notplay` | Address is not a player | -| `gameover` | Game has already ended | - - -## Architecture -ECSWorldState -├── BoardComponent (entity_id: 0) -│ └── cells: Vec [9 cells] -├── PlayerComponent (entity_id: 1) -│ ├── player_x: Address -│ └── player_o: Address -├── GameStateComponent (entity_id: 2) -│ ├── is_x_turn: bool -│ ├── move_count: u32 -│ └── status: u32 -└── next_entity_id: u32 - -## Deployment -# Deploy to Testnet -# Generate funded account -stellar keys generate deployer --network --fund - -# Build contract stellar contract build +``` -# Deploy -stellar contract deploy \ - --wasm target/tic_tac_toe.wasm \ - --source \ - --network - - # Interact with Deployed Contract - # Initialize a game -stellar contract invoke \ - --id \ - --network \ - -- init_game \ - --player_x \ - --player_o - -# Make a move -stellar contract invoke \ - --id \ - --network \ - -- make_move \ - --player \ - --position 4 - -# Get game state -stellar contract invoke \ - --id \ - --network \ - -- get_state - - - -## Resources - Cougr Repository -Soroban Documentation -Stellar CLI Reference - - ---- - -## 5. Verification Script: `scripts/verify_hygiene.sh` - -```bash -#!/usr/bin/env bash -# -# verify_hygiene.sh — Verify all hygiene standards from #225 are met -# - -set -euo pipefail - -REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" -EXAMPLES_DIR="$REPO_ROOT/examples" -EXIT_CODE=0 - -echo "=== Hygiene Verification (#225) ===" -echo "" - -# --- Check 1: No tracked target/ directories --- -echo "Check 1: No tracked target/ directories" -tracked_targets=$(git -C "$REPO_ROOT" ls-files 'examples/**/target/**' 2>/dev/null || true) -if [ -z "$tracked_targets" ]; then - echo " PASS: No target/ files tracked" -else - echo " FAIL: Tracked target/ files found:" - echo "$tracked_targets" | sed 's/^/ /' - EXIT_CODE=1 -fi -echo "" - -# --- Check 2: No .wasm files tracked --- -echo "Check 2: No tracked .wasm files" -tracked_wasm=$(git -C "$REPO_ROOT" ls-files 'examples/**/*.wasm' 2>/dev/null || true) -if [ -z "$tracked_wasm" ]; then - echo " PASS: No .wasm files tracked" -else - echo " FAIL: Tracked .wasm files found:" - echo "$tracked_wasm" | sed 's/^/ /' - EXIT_CODE=1 -fi -echo "" - -# --- Check 3: No hardcoded contract IDs in READMEs --- -echo "Check 3: No hardcoded contract IDs in README.md files" -contract_ids=$(grep -rE 'C[A-Z2-7]{55}' "$EXAMPLES_DIR"/*/README.md 2>/dev/null || true) -if [ -z "$contract_ids" ]; then - echo " PASS: No hardcoded contract IDs found" -else - echo " FAIL: Hardcoded contract IDs found:" - echo "$contract_ids" | sed 's/^/ /' - EXIT_CODE=1 -fi -echo "" - -# --- Check 4: .gitignore exists in every example --- -echo "Check 4: .gitignore in every example directory" -missing_gitignore=0 -for example_dir in "$EXAMPLES_DIR"/*/; do - if [ ! -f "$example_dir/.gitignore" ]; then - echo " FAIL: Missing .gitignore in $(basename "$example_dir")" - missing_gitignore=$((missing_gitignore + 1)) - EXIT_CODE=1 - fi -done -if [ $missing_gitignore -eq 0 ]; then - echo " PASS: All examples have .gitignore" -fi -echo "" - -# --- Check 5: .gitignore excludes target/ --- -echo "Check 5: .gitignore excludes target/" -missing_target_ignore=0 -for gitignore in "$EXAMPLES_DIR"/*/.gitignore; do - if [ ! -f "$gitignore" ]; then - continue - fi - if ! grep -q "^target/" "$gitignore" && ! grep -q "^/target/" "$gitignore"; then - echo " FAIL: $(dirname "$gitignore")/.gitignore does not exclude target/" - missing_target_ignore=$((missing_target_ignore + 1)) - EXIT_CODE=1 - fi -done -if [ $missing_target_ignore -eq 0 ]; then - echo " PASS: All .gitignore files exclude target/" -fi -echo "" - -# --- Check 6: cargo metadata --no-deps succeeds --- -echo "Check 6: cargo metadata --no-deps succeeds for all examples" -metadata_failed=0 -for example_dir in "$EXAMPLES_DIR"/*/; do - if [ ! -d "$example_dir" ]; then - continue - fi - example_name=$(basename "$example_dir") - if (cd "$example_dir" && cargo metadata --no-deps --format-version 1 >/dev/null 2>&1); then - : - else - echo " FAIL: cargo metadata failed for $example_name" - metadata_failed=$((metadata_failed + 1)) - EXIT_CODE=1 - fi -done -if [ $metadata_failed -eq 0 ]; then - echo " PASS: cargo metadata succeeds for all examples" -fi -echo "" - -# --- Summary --- -if [ $EXIT_CODE -eq 0 ]; then - echo "=== ALL CHECKS PASSED ===" -else - echo "=== SOME CHECKS FAILED ===" -fi +## Known limitations -exit $EXIT_CODE \ No newline at end of file +- Does not use `GameApp` or `ScheduleStage` — validation and win detection run synchronously + inside `make_move` since a turn-based game with one decision point per call has no need for + staged scheduling. +- Does not use `SimpleQueryBuilder` — there is exactly one game entity per contract instance, + so there is no entity population to scan by component type. +- No timeout/forfeit mechanism for an unresponsive player. +- No spectator or replay API beyond the read-only getters. diff --git a/examples/tic_tac_toe/src/components.rs b/examples/tic_tac_toe/src/components.rs new file mode 100644 index 00000000..ea0530c7 --- /dev/null +++ b/examples/tic_tac_toe/src/components.rs @@ -0,0 +1,55 @@ +use cougr_core::{impl_component, impl_rich_component}; +use soroban_sdk::{contracttype, Address, Env, Vec}; + +/// Entity id under which the entire game state is stored. Tic-tac-toe has +/// exactly one game per contract instance, so a single fixed entity id is +/// sufficient instead of a dynamic entity population. +pub const GAME_ENTITY: u32 = 1; + +// ─── Components ─────────────────────────────────────────────────────────────── + +/// Board state: 9 cells where 0 = empty, 1 = X, 2 = O. +/// Uses impl_rich_component! because Vec requires XDR codec. +#[contracttype] +#[derive(Clone, Debug)] +pub struct Board { + pub cells: Vec, +} + +impl_rich_component!(Board, "board"); + +impl Board { + pub fn new(env: &Env) -> Self { + let mut cells = Vec::new(env); + for _ in 0..9u32 { + cells.push_back(0u32); + } + Self { cells } + } +} + +/// Both players' wallet addresses. +/// Uses impl_rich_component! because Address requires XDR codec. +#[contracttype] +#[derive(Clone, Debug)] +pub struct Players { + pub player_x: Address, + pub player_o: Address, +} + +impl_rich_component!(Players, "players"); + +/// Turn and game-over state — plain fixed-size fields; no XDR needed. +#[contracttype] +#[derive(Clone, Debug)] +pub struct TurnState { + pub is_x_turn: bool, + pub move_count: u32, + pub status: u32, // 0 = in progress, 1 = X wins, 2 = O wins, 3 = draw +} + +impl_component!(TurnState, "turnst", Table, { + is_x_turn: bool, + move_count: u32, + status: u32 +}); diff --git a/examples/tic_tac_toe/src/lib.rs b/examples/tic_tac_toe/src/lib.rs index b7b3c8b4..fdea65f2 100644 --- a/examples/tic_tac_toe/src/lib.rs +++ b/examples/tic_tac_toe/src/lib.rs @@ -8,57 +8,17 @@ #![no_std] -use cougr_core::game::SorobanGame; -use cougr_core::{impl_component, impl_rich_component, impl_soroban_game}; -use soroban_sdk::{contract, contractimpl, contracttype, symbol_short, Address, Env, Symbol, Vec}; - -// ─── Components ─────────────────────────────────────────────────────────────── - -/// Board state: 9 cells where 0 = empty, 1 = X, 2 = O. -/// Uses impl_rich_component! because Vec requires XDR codec. -#[contracttype] -#[derive(Clone, Debug)] -pub struct Board { - pub cells: Vec, -} - -impl_rich_component!(Board, "board"); - -impl Board { - fn new(env: &Env) -> Self { - let mut cells = Vec::new(env); - for _ in 0..9u32 { - cells.push_back(0u32); - } - Self { cells } - } -} - -/// Both players' wallet addresses. -/// Uses impl_rich_component! because Address requires XDR codec. -#[contracttype] -#[derive(Clone, Debug)] -pub struct Players { - pub player_x: Address, - pub player_o: Address, -} - -impl_rich_component!(Players, "players"); +mod components; +mod systems; +#[cfg(test)] +mod test; -/// Turn and game-over state — plain fixed-size fields; no XDR needed. -#[contracttype] -#[derive(Clone, Debug)] -pub struct TurnState { - pub is_x_turn: bool, - pub move_count: u32, - pub status: u32, // 0 = in progress, 1 = X wins, 2 = O wins, 3 = draw -} +use components::{Board, Players, TurnState, GAME_ENTITY}; +use systems::detect_winner; -impl_component!(TurnState, "turnst", Table, { - is_x_turn: bool, - move_count: u32, - status: u32 -}); +use cougr_core::game::SorobanGame; +use cougr_core::impl_soroban_game; +use soroban_sdk::{contract, contractimpl, contracttype, symbol_short, Address, Env, Symbol, Vec}; // ─── API return types ───────────────────────────────────────────────────────── @@ -83,10 +43,7 @@ pub struct MoveResult { // ─── Contract ──────────────────────────────────────────────────────────────── -const GAME_ENTITY: u32 = 1; - #[contract] -#[derive(Clone)] pub struct TicTacToeContract; impl_soroban_game!(TicTacToeContract, "ttt_world"); @@ -156,7 +113,7 @@ impl TicTacToeContract { board.cells.set(position, mark); let new_move_count = turn.move_count + 1; - let new_status = Self::detect_winner(&board.cells, new_move_count); + let new_status = detect_winner(&board.cells, new_move_count); let new_is_x_turn = if new_status == 0 { !turn.is_x_turn } else { @@ -268,35 +225,4 @@ impl TicTacToeContract { message: msg, } } - - fn detect_winner(cells: &Vec, move_count: u32) -> u32 { - const LINES: [[u32; 3]; 8] = [ - [0, 1, 2], - [3, 4, 5], - [6, 7, 8], // rows - [0, 3, 6], - [1, 4, 7], - [2, 5, 8], // columns - [0, 4, 8], - [2, 4, 6], // diagonals - ]; - for line in LINES.iter() { - let a = cells.get(line[0]).unwrap_or(0); - let b = cells.get(line[1]).unwrap_or(0); - let c = cells.get(line[2]).unwrap_or(0); - if a != 0 && a == b && b == c { - return a; // 1 = X wins, 2 = O wins - } - } - if move_count >= 9 { - 3 - } else { - 0 - } - } } - -#[cfg(test)] -mod sandbox_tests; -#[cfg(test)] -mod test; diff --git a/examples/tic_tac_toe/src/systems.rs b/examples/tic_tac_toe/src/systems.rs new file mode 100644 index 00000000..26d562eb --- /dev/null +++ b/examples/tic_tac_toe/src/systems.rs @@ -0,0 +1,35 @@ +use soroban_sdk::Vec; + +/// All 8 winning lines on a 3x3 board (3 rows, 3 columns, 2 diagonals), +/// indexed 0-8 left-to-right, top-to-bottom. +const LINES: [[u32; 3]; 8] = [ + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], // rows + [0, 3, 6], + [1, 4, 7], + [2, 5, 8], // columns + [0, 4, 8], + [2, 4, 6], // diagonals +]; + +/// Pure win/draw detection over the current board and move count. +/// +/// Returns `0` if the game is still in progress, `1` if X has won, `2` if O +/// has won, or `3` if the board is full with no winner (draw). Does not +/// touch storage — callers are responsible for persisting the result. +pub fn detect_winner(cells: &Vec, move_count: u32) -> u32 { + for line in LINES.iter() { + let a = cells.get(line[0]).unwrap_or(0); + let b = cells.get(line[1]).unwrap_or(0); + let c = cells.get(line[2]).unwrap_or(0); + if a != 0 && a == b && b == c { + return a; // 1 = X wins, 2 = O wins + } + } + if move_count >= 9 { + 3 + } else { + 0 + } +}