From ba8d7500ed239163bc6274b2553cfddf5d19575d Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:44:31 -0500 Subject: [PATCH 01/28] First pass at soroban init command --- Cargo.lock | 1213 +++++++++++++++++++++++++- cmd/soroban-cli/Cargo.toml | 6 + cmd/soroban-cli/src/commands/init.rs | 105 +++ cmd/soroban-cli/src/commands/mod.rs | 6 + 4 files changed, 1325 insertions(+), 5 deletions(-) create mode 100644 cmd/soroban-cli/src/commands/init.rs diff --git a/Cargo.lock b/Cargo.lock index 5bb334eada..27aff09c4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,6 +104,12 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + [[package]] name = "assert_cmd" version = "2.0.12" @@ -246,6 +252,15 @@ dependencies = [ "serde", ] +[[package]] +name = "btoi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd6407f73a9b8b6162d8a2ef999fe6afd7cc15902ebf42c5cd296addf17e0ad" +dependencies = [ + "num-traits", +] + [[package]] name = "bumpalo" version = "3.14.0" @@ -397,6 +412,12 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +[[package]] +name = "clru" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -466,6 +487,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-channel" version = "0.5.9" @@ -691,6 +721,12 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + [[package]] name = "der" version = "0.7.8" @@ -793,6 +829,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + [[package]] name = "ecdsa" version = "0.16.9" @@ -877,6 +919,27 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enum-as-inner" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -905,6 +968,24 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" +[[package]] +name = "faster-hex" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239f7bfb930f820ab16a9cd95afc26f88264cf6905c960b340a615384aa3338a" +dependencies = [ + "serde", +] + +[[package]] +name = "faster-hex" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" +dependencies = [ + "serde", +] + [[package]] name = "fastrand" version = "2.0.1" @@ -927,6 +1008,28 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.52.0", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "float-cmp" version = "0.9.0" @@ -987,6 +1090,12 @@ version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + [[package]] name = "futures-sink" version = "0.3.29" @@ -1006,10 +1115,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-core", + "futures-io", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -1037,10 +1149,747 @@ dependencies = [ ] [[package]] -name = "gimli" -version = "0.28.1" +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gix" +version = "0.55.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "002667cd1ebb789313d0d0afe3d23b2821cf3b0e91605095f0e6d8751f0ceeea" +dependencies = [ + "gix-actor", + "gix-attributes", + "gix-commitgraph", + "gix-config", + "gix-credentials", + "gix-date", + "gix-diff", + "gix-discover", + "gix-features", + "gix-filter", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-hashtable", + "gix-ignore", + "gix-index", + "gix-lock", + "gix-macros", + "gix-negotiate", + "gix-object", + "gix-odb", + "gix-pack", + "gix-path", + "gix-pathspec", + "gix-prompt", + "gix-protocol", + "gix-ref", + "gix-refspec", + "gix-revision", + "gix-revwalk", + "gix-sec", + "gix-submodule", + "gix-tempfile", + "gix-trace", + "gix-transport", + "gix-traverse", + "gix-url", + "gix-utils", + "gix-validate", + "gix-worktree", + "gix-worktree-state", + "once_cell", + "parking_lot", + "reqwest", + "smallvec", + "thiserror", + "unicode-normalization", +] + +[[package]] +name = "gix-actor" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eadca029ef716b4378f7afb19f7ee101fde9e58ba1f1445971315ac866db417" +dependencies = [ + "bstr", + "btoi", + "gix-date", + "itoa", + "thiserror", + "winnow", +] + +[[package]] +name = "gix-attributes" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f395469d38c76ec47cd1a6c5a53fbc3f13f737b96eaf7535f4e6b367e643381" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-quote", + "gix-trace", + "kstring", + "smallvec", + "thiserror", + "unicode-bom", +] + +[[package]] +name = "gix-bitmap" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b6cd0f246180034ddafac9b00a112f19178135b21eb031b3f79355891f7325" +dependencies = [ + "thiserror", +] + +[[package]] +name = "gix-chunk" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "003ec6deacf68076a0c157271a127e0bb2c031c1a41f7168cbe5d248d9b85c78" +dependencies = [ + "thiserror", +] + +[[package]] +name = "gix-command" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c576cfbf577f72c097b5f88aedea502cd62952bdc1fb3adcab4531d5525a4c7" +dependencies = [ + "bstr", +] + +[[package]] +name = "gix-commitgraph" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a7007ba021f059803afaf6f8a48872422abc20550ac12ede6ddea2936cec36" +dependencies = [ + "bstr", + "gix-chunk", + "gix-features", + "gix-hash", + "memmap2 0.9.3", + "thiserror", +] + +[[package]] +name = "gix-config" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cae98c6b4c66c09379bc35274b172587d6b0ac369a416c39128ad8c6454f9bb" +dependencies = [ + "bstr", + "gix-config-value", + "gix-features", + "gix-glob", + "gix-path", + "gix-ref", + "gix-sec", + "memchr", + "once_cell", + "smallvec", + "thiserror", + "unicode-bom", + "winnow", +] + +[[package]] +name = "gix-config-value" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e0be46f4cf1f8f9e88d0e3eb7b29718aff23889563249f379119bd1ab6910e" +dependencies = [ + "bitflags 2.4.1", + "bstr", + "gix-path", + "libc", + "thiserror", +] + +[[package]] +name = "gix-credentials" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c5c5d74069b842a1861e581027ac6b7ad9ff66f5911c89b9f45484d7ebda6a4" +dependencies = [ + "bstr", + "gix-command", + "gix-config-value", + "gix-path", + "gix-prompt", + "gix-sec", + "gix-url", + "thiserror", +] + +[[package]] +name = "gix-date" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb7f3dfb72bebe3449b5e642be64e3c6ccbe9821c8b8f19f487cf5bfbbf4067e" +dependencies = [ + "bstr", + "itoa", + "thiserror", + "time", +] + +[[package]] +name = "gix-diff" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "931394f69fb8c9ed6afc0aae3487bd869e936339bcc13ed8884472af072e0554" +dependencies = [ + "gix-hash", + "gix-object", + "thiserror", +] + +[[package]] +name = "gix-discover" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a45d5cf0321178883e38705ab2b098f625d609a7d4c391b33ac952eff2c490f2" +dependencies = [ + "bstr", + "dunce", + "gix-hash", + "gix-path", + "gix-ref", + "gix-sec", + "thiserror", +] + +[[package]] +name = "gix-features" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d46a4a5c6bb5bebec9c0d18b65ada20e6517dbd7cf855b87dd4bbdce3a771b2" +dependencies = [ + "bytes", + "crc32fast", + "flate2", + "gix-hash", + "gix-trace", + "libc", + "once_cell", + "prodash", + "sha1_smol", + "thiserror", + "walkdir", +] + +[[package]] +name = "gix-filter" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92f674d3fdb6b1987b04521ec9a5b7be8650671f2c4bbd17c3c81e2a364242ff" +dependencies = [ + "bstr", + "encoding_rs", + "gix-attributes", + "gix-command", + "gix-hash", + "gix-object", + "gix-packetline-blocking", + "gix-path", + "gix-quote", + "gix-trace", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-fs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20e86eb040f5776a5ade092282e51cdcad398adb77d948b88d17583c2ae4e107" +dependencies = [ + "gix-features", +] + +[[package]] +name = "gix-glob" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db19298c5eeea2961e5b3bf190767a2d1f09b8802aeb5f258e42276350aff19" +dependencies = [ + "bitflags 2.4.1", + "bstr", + "gix-features", + "gix-path", +] + +[[package]] +name = "gix-hash" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8cf8c2266f63e582b7eb206799b63aa5fa68ee510ad349f637dfe2d0653de0" +dependencies = [ + "faster-hex 0.9.0", + "thiserror", +] + +[[package]] +name = "gix-hashtable" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb61880816d7ec4f0b20606b498147d480860ddd9133ba542628df2f548d3ca" +dependencies = [ + "gix-hash", + "hashbrown 0.14.3", + "parking_lot", +] + +[[package]] +name = "gix-ignore" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a215cc8cf21645bca131fcf6329d3ebd46299c47dbbe27df71bb1ca9e328b879" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "unicode-bom", +] + +[[package]] +name = "gix-index" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83a4fcc121b2f2e109088f677f89f85e7a8ebf39e8e6659c0ae54d4283b1650" +dependencies = [ + "bitflags 2.4.1", + "bstr", + "btoi", + "filetime", + "gix-bitmap", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-traverse", + "itoa", + "memmap2 0.7.1", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-lock" +version = "11.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e5c65e6a29830a435664891ced3f3c1af010f14900226019590ee0971a22f37" +dependencies = [ + "gix-tempfile", + "gix-utils", + "thiserror", +] + +[[package]] +name = "gix-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75e7ab728059f595f6ddc1ad8771b8d6a231971ae493d9d5948ecad366ee8bb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "gix-negotiate" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a5cdcf491ecc9ce39dcc227216c540355fe0024ae7c38e94557752ca5ebb67f" +dependencies = [ + "bitflags 2.4.1", + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-object", + "gix-revwalk", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-object" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740f2a44267f58770a1cb3a3d01d14e67b089c7136c48d4bddbb3cfd2bf86a51" +dependencies = [ + "bstr", + "btoi", + "gix-actor", + "gix-date", + "gix-features", + "gix-hash", + "gix-validate", + "itoa", + "smallvec", + "thiserror", + "winnow", +] + +[[package]] +name = "gix-odb" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8630b56cb80d8fa684d383dad006a66401ee8314e12fbf0e566ddad8c115143b" +dependencies = [ + "arc-swap", + "gix-date", + "gix-features", + "gix-hash", + "gix-object", + "gix-pack", + "gix-path", + "gix-quote", + "parking_lot", + "tempfile", + "thiserror", +] + +[[package]] +name = "gix-pack" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1431ba2e30deff1405920693d54ab231c88d7c240dd6ccc936ee223d8f8697c3" +dependencies = [ + "clru", + "gix-chunk", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-path", + "gix-tempfile", + "memmap2 0.7.1", + "parking_lot", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-packetline" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a8384b1e964151aff0d5632dd9b191059d07dff358b96bd940f1b452600d7ab" +dependencies = [ + "bstr", + "faster-hex 0.8.1", + "thiserror", +] + +[[package]] +name = "gix-packetline-blocking" +version = "0.16.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8395f7501c84d6a1fe902035fdfd8cd86d89e2dd6be0200ec1a72fd3c92d39" +dependencies = [ + "bstr", + "faster-hex 0.8.1", + "thiserror", +] + +[[package]] +name = "gix-path" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dd0998ab245f33d40ca2267e58d542fe54185ebd1dc41923346cf28d179fb6" +dependencies = [ + "bstr", + "gix-trace", + "home", + "once_cell", + "thiserror", +] + +[[package]] +name = "gix-pathspec" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dbbb92f75a38ef043c8bb830b339b38d0698d7f3746968b5fcbade7a880494d" +dependencies = [ + "bitflags 2.4.1", + "bstr", + "gix-attributes", + "gix-config-value", + "gix-glob", + "gix-path", + "thiserror", +] + +[[package]] +name = "gix-prompt" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c9a913769516f5e9d937afac206fb76428e3d7238e538845842887fda584678" +dependencies = [ + "gix-command", + "gix-config-value", + "parking_lot", + "rustix", + "thiserror", +] + +[[package]] +name = "gix-protocol" +version = "0.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391e3feabdfa5f90dad6673ce59e3291ac28901b2ff248d86c5a7fbde0391e0e" +dependencies = [ + "bstr", + "btoi", + "gix-credentials", + "gix-date", + "gix-features", + "gix-hash", + "gix-transport", + "maybe-async", + "thiserror", + "winnow", +] + +[[package]] +name = "gix-quote" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7dc10303d73a960d10fb82f81188b036ac3e6b11b5795b20b1a60b51d1321f" +dependencies = [ + "bstr", + "btoi", + "thiserror", +] + +[[package]] +name = "gix-ref" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec2f6d07ac88d2fb8007ee3fa3e801856fb9d82e7366ec0ca332eb2c9d74a52" +dependencies = [ + "gix-actor", + "gix-date", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-validate", + "memmap2 0.7.1", + "thiserror", + "winnow", +] + +[[package]] +name = "gix-refspec" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb0974cc41dbdb43a180c7f67aa481e1c1e160fcfa8f4a55291fd1126c1a6e7" +dependencies = [ + "bstr", + "gix-hash", + "gix-revision", + "gix-validate", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-revision" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ca97ac73459a7f3766aa4a5638a6e37d56d4c7962bc1986fbaf4883d0772588" +dependencies = [ + "bstr", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-revwalk", + "gix-trace", + "thiserror", +] + +[[package]] +name = "gix-revwalk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a16d8c892e4cd676d86f0265bf9d40cefd73d8d94f86b213b8b77d50e77efae0" +dependencies = [ + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-sec" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f6dce0c6683e2219e8169aac4b1c29e89540a8262fef7056b31d80d969408c" +dependencies = [ + "bitflags 2.4.1", + "gix-path", + "libc", + "windows", +] + +[[package]] +name = "gix-submodule" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bba78c8d12aa24370178453ec3a472ff08dfaa657d116229f57f2c9cd469a1c2" +dependencies = [ + "bstr", + "gix-config", + "gix-path", + "gix-pathspec", + "gix-refspec", + "gix-url", + "thiserror", +] + +[[package]] +name = "gix-tempfile" +version = "11.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388dd29114a86ec69b28d1e26d6d63a662300ecf61ab3f4cc578f7d7dc9e7e23" +dependencies = [ + "gix-fs", + "libc", + "once_cell", + "parking_lot", + "tempfile", +] + +[[package]] +name = "gix-trace" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8e1127ede0475b58f4fe9c0aaa0d9bb0bad2af90bbd93ccd307c8632b863d89" + +[[package]] +name = "gix-transport" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f209a93364e24f20319751bc11092272e2f3fe82bb72592b2822679cf5be752" +dependencies = [ + "base64 0.21.5", + "bstr", + "gix-command", + "gix-credentials", + "gix-features", + "gix-packetline", + "gix-quote", + "gix-sec", + "gix-url", + "reqwest", + "thiserror", +] + +[[package]] +name = "gix-traverse" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14d050ec7d4e1bb76abf0636cf4104fb915b70e54e3ced9a4427c999100ff38a" +dependencies = [ + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-revwalk", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-url" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c427a1a11ccfa53a4a2da47d9442c2241deee63a154bc15cc14b8312fbc4005" +dependencies = [ + "bstr", + "gix-features", + "gix-path", + "home", + "thiserror", + "url", +] + +[[package]] +name = "gix-utils" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de6225e2de30b6e9bca2d9f1cc4731640fcef0fb3cabddceee366e7e85d3e94f" +dependencies = [ + "fastrand", +] + +[[package]] +name = "gix-validate" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac7cc36f496bd5d96cdca0f9289bb684480725d40db60f48194aa7723b883854" +dependencies = [ + "bstr", + "thiserror", +] + +[[package]] +name = "gix-worktree" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddaf79e721dba64fe726a42f297a3c8ed42e55cdc0d81ca68452f2def3c2d7fd" +dependencies = [ + "bstr", + "gix-attributes", + "gix-features", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-ignore", + "gix-index", + "gix-object", + "gix-path", +] + +[[package]] +name = "gix-worktree-state" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "34a2fcccdcaf3c71c00a03df31c9aa459d444cabbec4ed9ca1fa64e43406bed4" +dependencies = [ + "bstr", + "gix-features", + "gix-filter", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-index", + "gix-object", + "gix-path", + "gix-worktree", + "io-close", + "thiserror", +] [[package]] name = "glob" @@ -1169,6 +2018,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + [[package]] name = "http" version = "0.2.11" @@ -1267,7 +2127,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.51.1", ] [[package]] @@ -1285,6 +2145,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.5.0" @@ -1359,6 +2229,34 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" +[[package]] +name = "io-close" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2 0.5.5", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "itertools" version = "0.10.5" @@ -1477,6 +2375,15 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "kstring" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" +dependencies = [ + "static_assertions", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1515,6 +2422,12 @@ dependencies = [ "cc", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.4.12" @@ -1537,6 +2450,21 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matchers" version = "0.1.0" @@ -1546,12 +2474,41 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "maybe-async" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "memmap2" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45fd3a57831bf88bc63f8cebc0cf956116276e97fef3966103e96416209f7c92" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.9.0" @@ -1561,6 +2518,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1666,6 +2629,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + [[package]] name = "object" version = "0.32.1" @@ -1947,6 +2919,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prodash" +version = "26.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "794b5bf8e2d19b53dcdcec3e4bba628e20f5b6062503ba89281fa7037dd7bbcf" + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "1.0.33" @@ -2056,6 +3040,57 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "reqwest" +version = "0.11.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +dependencies = [ + "base64 0.21.5", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-rustls", + "tower-service", + "trust-dns-resolver", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -2330,6 +3365,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_with" version = "3.4.0" @@ -2359,6 +3406,12 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.9.9" @@ -2494,6 +3547,7 @@ dependencies = [ name = "soroban-cli" version = "20.1.1" dependencies = [ + "anyhow", "assert_cmd", "assert_fs", "base64 0.21.5", @@ -2508,6 +3562,7 @@ dependencies = [ "dotenvy", "ed25519-dalek 2.0.0", "ethnum", + "gix", "heck", "hex", "http", @@ -2541,6 +3596,7 @@ dependencies = [ "stellar-strkey 0.0.7", "stellar-xdr", "strsim", + "tempfile", "termcolor", "termcolor_output", "thiserror", @@ -2938,6 +3994,27 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "temp-dir" version = "0.1.11" @@ -3062,6 +4139,8 @@ checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", + "libc", + "num_threads", "powerfmt", "serde", "time-core", @@ -3291,6 +4370,52 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "trust-dns-proto" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3119112651c157f4488931a01e586aa459736e9d6046d3bd9105ffb69352d374" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand", + "smallvec", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a3e6c3aff1718b3c73e395d1f35202ba2ffa847c6a62eea0db8fb4cfe30be6" +dependencies = [ + "cfg-if", + "futures-util", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tracing", + "trust-dns-proto", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -3309,6 +4434,12 @@ version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +[[package]] +name = "unicode-bom" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -3343,7 +4474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", ] @@ -3430,6 +4561,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.89" @@ -3542,6 +4685,22 @@ dependencies = [ "indexmap-nostd", ] +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" + [[package]] name = "which" version = "4.4.2" @@ -3555,6 +4714,12 @@ dependencies = [ "rustix", ] +[[package]] +name = "widestring" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" + [[package]] name = "winapi" version = "0.3.9" @@ -3586,6 +4751,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets 0.52.0", +] + [[package]] name = "windows-core" version = "0.51.1" @@ -3595,6 +4770,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -3727,6 +4911,25 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "winnow" +version = "0.5.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 972e832a70..318bffe228 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -93,6 +93,12 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] } cargo_metadata = "0.15.4" pathdiff = "0.2.1" dotenvy = "0.15.7" +gix = { version = "0.55.2", default-features = false, features = [ + "blocking-http-transport-reqwest-rust-tls", + "worktree-mutation", +] } +anyhow = "1.0.75" +tempfile = "3.8.1" # For hyper-tls [target.'cfg(unix)'.dependencies] openssl = { version = "0.10.55", features = ["vendored"] } diff --git a/cmd/soroban-cli/src/commands/init.rs b/cmd/soroban-cli/src/commands/init.rs new file mode 100644 index 0000000000..c7364bb9d9 --- /dev/null +++ b/cmd/soroban-cli/src/commands/init.rs @@ -0,0 +1,105 @@ +use std::path::Path; +use std::{fs, io}; + +use clap::Parser; +use std::num::NonZeroU32; +use std::sync::atomic::AtomicBool; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + pub project_path: String, +} +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Failed to create the project directory: {0}")] + CreateDirError(#[from] io::Error), + + #[error("Failed to clone the template repository: {0}")] + CloneError(#[from] gix::clone::Error), + + #[error("Failed to fetch the template repository: {0}")] + FetchError(#[from] gix::clone::fetch::Error), + + #[error("Failed to checkout the template repository: {0}")] + CheckoutError(#[from] gix::clone::checkout::main_worktree::Error), +} + +const TEMPLATE_URL: &str = "https://github.com/AhaLabs/soroban-tutorial-project.git"; + +impl Cmd { + #[allow(clippy::unused_self)] + pub fn run(&self) -> Result<(), Error> { + println!("Creating a new soroban project at {}", self.project_path); + let project_path = Path::new(&self.project_path); + + init(project_path, TEMPLATE_URL) + } +} + +fn init(project_path: &Path, template_url: &str) -> Result<(), Error> { + let temp_dir = tempfile::tempdir()?; + clone_repo(template_url, temp_dir.path())?; + std::fs::create_dir_all(project_path)?; + copy_contents(temp_dir.path(), project_path)?; + Ok(()) +} + +fn clone_repo(from_url: &str, to_path: &Path) -> Result<(), Error> { + let mut fetch = gix::clone::PrepareFetch::new( + from_url, + to_path, + gix::create::Kind::WithWorktree, + gix::create::Options { + destination_must_be_empty: false, + fs_capabilities: None, + }, + gix::open::Options::isolated(), + )? + .with_shallow(gix::remote::fetch::Shallow::DepthAtRemote( + NonZeroU32::new(1).unwrap(), + )); + + let (mut prepare, _outcome) = + fetch.fetch_then_checkout(gix::progress::Discard, &AtomicBool::new(false))?; + + let (_repo, _outcome) = + prepare.main_worktree(gix::progress::Discard, &AtomicBool::new(false))?; + + Ok(()) +} + +fn copy_contents(from: &Path, to: &Path) -> Result<(), Error> { + for entry in fs::read_dir(from)? { + let entry = entry?; + let entry_name = entry.file_name().to_string_lossy().to_string(); + let path = entry.path(); + let file_name = path.file_name().unwrap(); + let new_path = to.join(file_name); + if path.is_dir() { + if entry_name == ".git" { + continue; + } + std::fs::create_dir_all(&new_path)?; + copy_contents(&path, &new_path)?; + } else { + std::fs::copy(&path, &new_path)?; + } + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_init() { + let temp_dir = tempfile::tempdir().unwrap(); + let project_dir = temp_dir.path().join("project"); + init(project_dir.as_path(), TEMPLATE_URL).unwrap(); + + assert!(project_dir.as_path().join("README.md").exists()); + temp_dir.close().unwrap() + } +} diff --git a/cmd/soroban-cli/src/commands/mod.rs b/cmd/soroban-cli/src/commands/mod.rs index 952869af3b..9bba173b52 100644 --- a/cmd/soroban-cli/src/commands/mod.rs +++ b/cmd/soroban-cli/src/commands/mod.rs @@ -8,6 +8,7 @@ pub mod contract; pub mod events; pub mod global; pub mod keys; +pub mod init; pub mod lab; pub mod network; pub mod plugin; @@ -94,6 +95,7 @@ impl Root { Cmd::Completion(completion) => completion.run(), Cmd::Contract(contract) => contract.run(&self.global_args).await?, Cmd::Events(events) => events.run().await?, + Cmd::Init(init) => init.run()?, Cmd::Lab(lab) => lab.run().await?, Cmd::Network(network) => network.run()?, Cmd::Version(version) => version.run(), @@ -128,6 +130,8 @@ pub enum Cmd { /// Create and manage identities including keys and addresses #[command(subcommand)] Keys(keys::Cmd), + /// Initialize a new Soroban project + Init(init::Cmd), /// Experiment with early features and expert tools #[command(subcommand)] Lab(lab::Cmd), @@ -148,6 +152,8 @@ pub enum Error { #[error(transparent)] Keys(#[from] keys::Error), #[error(transparent)] + Init(#[from] init::Error), + #[error(transparent)] Lab(#[from] lab::Error), #[error(transparent)] Config(#[from] config::Error), From dde6b00858864ef51efd41f96f37d3babf25f320 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 20 Dec 2023 16:15:56 -0500 Subject: [PATCH 02/28] Add --with-contract to allow user to initialize the project with a sample contract --- cmd/soroban-cli/src/commands/init.rs | 134 +++++++++++++++++++++++++-- 1 file changed, 124 insertions(+), 10 deletions(-) diff --git a/cmd/soroban-cli/src/commands/init.rs b/cmd/soroban-cli/src/commands/init.rs index c7364bb9d9..ab16a6e1d7 100644 --- a/cmd/soroban-cli/src/commands/init.rs +++ b/cmd/soroban-cli/src/commands/init.rs @@ -5,14 +5,27 @@ use clap::Parser; use std::num::NonZeroU32; use std::sync::atomic::AtomicBool; +#[derive(Clone, Debug, PartialEq, clap::ValueEnum)] +pub enum ExampleContract { + HelloWorld, + Account, + Alloc, + None, +} + #[derive(Parser, Debug, Clone)] #[group(skip)] pub struct Cmd { pub project_path: String, + + /// optional flag to specify the initial soroban example contracts to include + #[arg(short, long, num_args = 1..=6, default_value = "none")] + pub with_contract: Vec, } + #[derive(thiserror::Error, Debug)] pub enum Error { - #[error("Failed to create the project directory: {0}")] + #[error("Failed to create directory: {0}")] CreateDirError(#[from] io::Error), #[error("Failed to clone the template repository: {0}")] @@ -26,6 +39,7 @@ pub enum Error { } const TEMPLATE_URL: &str = "https://github.com/AhaLabs/soroban-tutorial-project.git"; +const SOROBAN_EXAMPLES_URL: &str = "https://github.com/stellar/soroban-examples.git"; impl Cmd { #[allow(clippy::unused_self)] @@ -33,18 +47,71 @@ impl Cmd { println!("Creating a new soroban project at {}", self.project_path); let project_path = Path::new(&self.project_path); - init(project_path, TEMPLATE_URL) + init(project_path, TEMPLATE_URL, &self.with_contract)?; + + Ok(()) } } -fn init(project_path: &Path, template_url: &str) -> Result<(), Error> { - let temp_dir = tempfile::tempdir()?; - clone_repo(template_url, temp_dir.path())?; +fn init( + project_path: &Path, + template_url: &str, + with_contracts: &[ExampleContract], +) -> Result<(), Error> { + // create a template temp dir to clone the template repo into + let template_dir = tempfile::tempdir()?; + + // clone the template repo into the temp dir + clone_repo(template_url, template_dir.path())?; + + // create the project directory and copy the template contents into it std::fs::create_dir_all(project_path)?; - copy_contents(temp_dir.path(), project_path)?; + copy_contents(template_dir.path(), project_path)?; + + // if there are with-contract flags, include the example contracts + if include_example_contracts(with_contracts) { + println!("Including example contracts: {:?}", with_contracts); + + // create an examples temp dir in the temp dir + let examples_dir = tempfile::tempdir()?; + + // clone the soroban-examples repo into temp dir + clone_repo(SOROBAN_EXAMPLES_URL, examples_dir.path())?; + + // copy the example contracts into the project + copy_example_contracts(examples_dir.path(), project_path, with_contracts)?; + } + + Ok(()) +} + +fn copy_example_contracts( + from: &Path, + to: &Path, + contracts: &[ExampleContract], +) -> Result<(), Error> { + let project_contracts_path = to.join("contracts"); + for contract in contracts { + let contract_dir = match contract { + ExampleContract::HelloWorld => Path::new("hello-world"), + ExampleContract::Alloc => Path::new("alloc"), + ExampleContract::Account => Path::new("account"), + ExampleContract::None => continue, + }; + + let from_contract_path = from.join(contract_dir); + let to_contract_path = project_contracts_path.join(contract_dir); + std::fs::create_dir_all(&to_contract_path)?; + + copy_contents(&from_contract_path, &to_contract_path)? + } Ok(()) } +fn include_example_contracts(contracts: &[ExampleContract]) -> bool { + !(contracts.len() == 1 && contracts[0] == ExampleContract::None) +} + fn clone_repo(from_url: &str, to_path: &Path) -> Result<(), Error> { let mut fetch = gix::clone::PrepareFetch::new( from_url, @@ -70,16 +137,19 @@ fn clone_repo(from_url: &str, to_path: &Path) -> Result<(), Error> { } fn copy_contents(from: &Path, to: &Path) -> Result<(), Error> { + let default_entries_to_exclude = vec![".git", ".github", "Makefile", "Cargo.lock"]; for entry in fs::read_dir(from)? { let entry = entry?; let entry_name = entry.file_name().to_string_lossy().to_string(); let path = entry.path(); let file_name = path.file_name().unwrap(); let new_path = to.join(file_name); + + if default_entries_to_exclude.contains(&entry_name.as_str()) { + continue; + } + if path.is_dir() { - if entry_name == ".git" { - continue; - } std::fs::create_dir_all(&new_path)?; copy_contents(&path, &new_path)?; } else { @@ -97,9 +167,53 @@ mod tests { fn test_init() { let temp_dir = tempfile::tempdir().unwrap(); let project_dir = temp_dir.path().join("project"); - init(project_dir.as_path(), TEMPLATE_URL).unwrap(); + let with_contracts = vec![ExampleContract::None]; + init(project_dir.as_path(), TEMPLATE_URL, &with_contracts).unwrap(); + + assert!(project_dir.as_path().join("README.md").exists()); + assert!(project_dir.as_path().join("contracts").exists()); + + // check that it does not include certain template files + assert!(!project_dir.as_path().join(".git").exists()); + assert!(!project_dir.as_path().join(".github").exists()); + assert!(!project_dir.as_path().join("Cargo.lock").exists()); + + temp_dir.close().unwrap() + } + + #[test] + fn test_include_contract() { + let temp_dir = tempfile::tempdir().unwrap(); + let project_dir = temp_dir.path().join("project"); + let with_contracts = vec![ExampleContract::Alloc]; + init(project_dir.as_path(), TEMPLATE_URL, &with_contracts).unwrap(); assert!(project_dir.as_path().join("README.md").exists()); + assert!(project_dir + .as_path() + .join("contracts") + .join("alloc") + .exists()); + + // check that it does not include certain template files + assert!(!project_dir.as_path().join(".git").exists()); + assert!(!project_dir.as_path().join(".github").exists()); + assert!(!project_dir.as_path().join("Cargo.lock").exists()); + + // check that it does not include certain contract files + assert!(!project_dir + .as_path() + .join("contracts") + .join("alloc") + .join("Makefile") + .exists()); + assert!(!project_dir + .as_path() + .join("contracts") + .join("alloc") + .join("Cargo.lock") + .exists()); + temp_dir.close().unwrap() } } From e2339a3492e07541683a81847cf244cd16733f19 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Thu, 21 Dec 2023 10:40:05 -0500 Subject: [PATCH 03/28] Cleanup --- cmd/soroban-cli/Cargo.toml | 1 + cmd/soroban-cli/src/commands/init.rs | 140 +++++++++++++++++++++++---- 2 files changed, 121 insertions(+), 20 deletions(-) diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 318bffe228..f38b33e2b7 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -99,6 +99,7 @@ gix = { version = "0.55.2", default-features = false, features = [ ] } anyhow = "1.0.75" tempfile = "3.8.1" +toml_edit = "0.21.0" # For hyper-tls [target.'cfg(unix)'.dependencies] openssl = { version = "0.10.55", features = ["vendored"] } diff --git a/cmd/soroban-cli/src/commands/init.rs b/cmd/soroban-cli/src/commands/init.rs index ab16a6e1d7..b588a4f8ce 100644 --- a/cmd/soroban-cli/src/commands/init.rs +++ b/cmd/soroban-cli/src/commands/init.rs @@ -1,25 +1,74 @@ +use std::fs::read_to_string; use std::path::Path; use std::{fs, io}; use clap::Parser; use std::num::NonZeroU32; use std::sync::atomic::AtomicBool; +use toml_edit::{value, Document, TomlError}; #[derive(Clone, Debug, PartialEq, clap::ValueEnum)] pub enum ExampleContract { - HelloWorld, Account, Alloc, + AtomicMultiswap, + AtomicSwap, + Auth, + CrossContract, + CustomTypes, + DeepContractAuth, + Deployer, + Errors, + Events, + Fuzzing, + HelloWorld, + Increment, + LiquidityPool, + Logging, + SimpleAccount, + SingleOffer, + Timelock, + Token, + UpgradeableContract, None, } +impl ToString for ExampleContract { + fn to_string(&self) -> String { + match self { + ExampleContract::Account => String::from("account"), + ExampleContract::Alloc => String::from("alloc"), + ExampleContract::AtomicMultiswap => String::from("atomic_multiswap"), + ExampleContract::AtomicSwap => String::from("atomic_swap"), + ExampleContract::Auth => String::from("auth"), + ExampleContract::CrossContract => String::from("cross_contract"), + ExampleContract::CustomTypes => String::from("custom_types"), + ExampleContract::DeepContractAuth => String::from("deep_contract_auth"), + ExampleContract::Deployer => String::from("deployer"), + ExampleContract::Errors => String::from("errors"), + ExampleContract::Events => String::from("events"), + ExampleContract::Fuzzing => String::from("fuzzing"), + ExampleContract::HelloWorld => String::from("hello_world"), + ExampleContract::Increment => String::from("increment"), + ExampleContract::LiquidityPool => String::from("liquidity_pool"), + ExampleContract::Logging => String::from("logging"), + ExampleContract::SimpleAccount => String::from("simple_account"), + ExampleContract::SingleOffer => String::from("single_offer"), + ExampleContract::Timelock => String::from("timelock"), + ExampleContract::Token => String::from("token"), + ExampleContract::UpgradeableContract => String::from("upgradeable_contract"), + ExampleContract::None => String::from("none"), + } + } +} + #[derive(Parser, Debug, Clone)] #[group(skip)] pub struct Cmd { pub project_path: String, /// optional flag to specify the initial soroban example contracts to include - #[arg(short, long, num_args = 1..=6, default_value = "none")] + #[arg(short, long, num_args = 1..=20, default_value = "none")] pub with_contract: Vec, } @@ -36,6 +85,9 @@ pub enum Error { #[error("Failed to checkout the template repository: {0}")] CheckoutError(#[from] gix::clone::checkout::main_worktree::Error), + + #[error("Failed to parse Cargo.toml: {0}")] + TomlParseError(#[from] TomlError), } const TEMPLATE_URL: &str = "https://github.com/AhaLabs/soroban-tutorial-project.git"; @@ -92,19 +144,29 @@ fn copy_example_contracts( ) -> Result<(), Error> { let project_contracts_path = to.join("contracts"); for contract in contracts { - let contract_dir = match contract { - ExampleContract::HelloWorld => Path::new("hello-world"), - ExampleContract::Alloc => Path::new("alloc"), - ExampleContract::Account => Path::new("account"), - ExampleContract::None => continue, - }; - - let from_contract_path = from.join(contract_dir); - let to_contract_path = project_contracts_path.join(contract_dir); + let contract_as_string = contract.to_string(); + let contract_path = Path::new(&contract_as_string); + let from_contract_path = from.join(contract_path); + let to_contract_path = project_contracts_path.join(contract_path); std::fs::create_dir_all(&to_contract_path)?; - copy_contents(&from_contract_path, &to_contract_path)? + copy_contents(&from_contract_path, &to_contract_path)?; + edit_cargo_file(&to_contract_path)?; } + + Ok(()) +} + +fn edit_cargo_file(contract_path: &Path) -> Result<(), Error> { + let cargo_path = contract_path.join("Cargo.toml"); + let cargo_toml_str = read_to_string(&cargo_path)?; + let mut doc = cargo_toml_str.parse::().unwrap(); + + doc["dependencies"]["soroban-sdk"] = value("{ workspace = true }"); + doc["dev_dependencies"]["soroban-sdk"] = value("{ workspace = true }"); + + std::fs::write(&cargo_path, doc.to_string())?; + Ok(()) } @@ -137,15 +199,15 @@ fn clone_repo(from_url: &str, to_path: &Path) -> Result<(), Error> { } fn copy_contents(from: &Path, to: &Path) -> Result<(), Error> { - let default_entries_to_exclude = vec![".git", ".github", "Makefile", "Cargo.lock"]; + let contents_to_exclude_from_copy = + vec![".git", ".github", "Makefile", "Cargo.lock", ".vscode"]; for entry in fs::read_dir(from)? { let entry = entry?; - let entry_name = entry.file_name().to_string_lossy().to_string(); let path = entry.path(); - let file_name = path.file_name().unwrap(); - let new_path = to.join(file_name); + let entry_name = entry.file_name().to_string_lossy().to_string(); + let new_path = to.join(&entry_name); - if default_entries_to_exclude.contains(&entry_name.as_str()) { + if contents_to_exclude_from_copy.contains(&entry_name.as_str()) { continue; } @@ -156,11 +218,14 @@ fn copy_contents(from: &Path, to: &Path) -> Result<(), Error> { std::fs::copy(&path, &new_path)?; } } + Ok(()) } #[cfg(test)] mod tests { + use std::fs::read_to_string; + use super::*; #[test] @@ -172,17 +237,19 @@ mod tests { assert!(project_dir.as_path().join("README.md").exists()); assert!(project_dir.as_path().join("contracts").exists()); + assert!(project_dir.as_path().join("Cargo.toml").exists()); - // check that it does not include certain template files + // check that it does not include certain template files and directories assert!(!project_dir.as_path().join(".git").exists()); assert!(!project_dir.as_path().join(".github").exists()); assert!(!project_dir.as_path().join("Cargo.lock").exists()); + assert!(!project_dir.as_path().join(".vscode").exists()); temp_dir.close().unwrap() } #[test] - fn test_include_contract() { + fn test_including_example_contract() { let temp_dir = tempfile::tempdir().unwrap(); let project_dir = temp_dir.path().join("project"); let with_contracts = vec![ExampleContract::Alloc]; @@ -195,10 +262,11 @@ mod tests { .join("alloc") .exists()); - // check that it does not include certain template files + // check that it does not include certain template files and directories assert!(!project_dir.as_path().join(".git").exists()); assert!(!project_dir.as_path().join(".github").exists()); assert!(!project_dir.as_path().join("Cargo.lock").exists()); + assert!(!project_dir.as_path().join(".vscode").exists()); // check that it does not include certain contract files assert!(!project_dir @@ -214,6 +282,38 @@ mod tests { .join("Cargo.lock") .exists()); + // check that the contract's Cargo.toml file uses the workspace for dependencies + let contract_cargo_path = project_dir + .as_path() + .join("contracts") + .join("alloc") + .join("Cargo.toml"); + let cargo_toml_str = read_to_string(contract_cargo_path).unwrap(); + println!("{}", cargo_toml_str); + + assert!(cargo_toml_str.contains("soroban-sdk = \"{ workspace = true }\"")); + + temp_dir.close().unwrap() + } + + #[test] + fn test_including_multiple_example_contracts() { + let temp_dir = tempfile::tempdir().unwrap(); + let project_dir = temp_dir.path().join("project"); + let with_contracts = vec![ExampleContract::Account, ExampleContract::AtomicSwap]; + init(project_dir.as_path(), TEMPLATE_URL, &with_contracts).unwrap(); + + assert!(project_dir + .as_path() + .join("contracts") + .join("account") + .exists()); + assert!(project_dir + .as_path() + .join("contracts") + .join("atomic_swap") + .exists()); + temp_dir.close().unwrap() } } From 7e873a07c99b6006fc5218250d1913bb0394fd20 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 3 Jan 2024 12:24:32 -0500 Subject: [PATCH 04/28] Update workspace inline table in Cargo.toml for included contracts --- cmd/soroban-cli/src/commands/init.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/cmd/soroban-cli/src/commands/init.rs b/cmd/soroban-cli/src/commands/init.rs index b588a4f8ce..eb364da350 100644 --- a/cmd/soroban-cli/src/commands/init.rs +++ b/cmd/soroban-cli/src/commands/init.rs @@ -5,7 +5,7 @@ use std::{fs, io}; use clap::Parser; use std::num::NonZeroU32; use std::sync::atomic::AtomicBool; -use toml_edit::{value, Document, TomlError}; +use toml_edit::{Document, Formatted, InlineTable, TomlError, Value}; #[derive(Clone, Debug, PartialEq, clap::ValueEnum)] pub enum ExampleContract { @@ -162,8 +162,13 @@ fn edit_cargo_file(contract_path: &Path) -> Result<(), Error> { let cargo_toml_str = read_to_string(&cargo_path)?; let mut doc = cargo_toml_str.parse::().unwrap(); - doc["dependencies"]["soroban-sdk"] = value("{ workspace = true }"); - doc["dev_dependencies"]["soroban-sdk"] = value("{ workspace = true }"); + let mut workspace_table = InlineTable::new(); + workspace_table.insert("workspace", Value::Boolean(Formatted::new(true))); + + doc["dependencies"]["soroban-sdk"] = + toml_edit::Item::Value(Value::InlineTable(workspace_table.clone())); + doc["dev_dependencies"]["soroban-sdk"] = + toml_edit::Item::Value(Value::InlineTable(workspace_table)); std::fs::write(&cargo_path, doc.to_string())?; @@ -291,7 +296,7 @@ mod tests { let cargo_toml_str = read_to_string(contract_cargo_path).unwrap(); println!("{}", cargo_toml_str); - assert!(cargo_toml_str.contains("soroban-sdk = \"{ workspace = true }\"")); + assert!(cargo_toml_str.contains("soroban-sdk = { workspace = true }")); temp_dir.close().unwrap() } From f7c5eb2adb2f869c4500fddb42f0b5e2bc879df3 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 3 Jan 2024 12:31:52 -0500 Subject: [PATCH 05/28] Cleanup --- cmd/soroban-cli/src/commands/init.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/soroban-cli/src/commands/init.rs b/cmd/soroban-cli/src/commands/init.rs index eb364da350..881217dbc5 100644 --- a/cmd/soroban-cli/src/commands/init.rs +++ b/cmd/soroban-cli/src/commands/init.rs @@ -67,14 +67,14 @@ impl ToString for ExampleContract { pub struct Cmd { pub project_path: String, - /// optional flag to specify the initial soroban example contracts to include + /// optional flag to specify soroban example contracts to include #[arg(short, long, num_args = 1..=20, default_value = "none")] pub with_contract: Vec, } #[derive(thiserror::Error, Debug)] pub enum Error { - #[error("Failed to create directory: {0}")] + #[error("Io error: {0}")] CreateDirError(#[from] io::Error), #[error("Failed to clone the template repository: {0}")] @@ -254,7 +254,7 @@ mod tests { } #[test] - fn test_including_example_contract() { + fn test_init_including_example_contract() { let temp_dir = tempfile::tempdir().unwrap(); let project_dir = temp_dir.path().join("project"); let with_contracts = vec![ExampleContract::Alloc]; @@ -302,7 +302,7 @@ mod tests { } #[test] - fn test_including_multiple_example_contracts() { + fn test_init_including_multiple_example_contracts() { let temp_dir = tempfile::tempdir().unwrap(); let project_dir = temp_dir.path().join("project"); let with_contracts = vec![ExampleContract::Account, ExampleContract::AtomicSwap]; From 6824ded09214b0411e5cc557538d08b31b66d8e7 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 3 Jan 2024 16:32:05 -0500 Subject: [PATCH 06/28] Update the template repo url --- cmd/soroban-cli/src/commands/init.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/commands/init.rs b/cmd/soroban-cli/src/commands/init.rs index 881217dbc5..4f69d13db0 100644 --- a/cmd/soroban-cli/src/commands/init.rs +++ b/cmd/soroban-cli/src/commands/init.rs @@ -90,7 +90,7 @@ pub enum Error { TomlParseError(#[from] TomlError), } -const TEMPLATE_URL: &str = "https://github.com/AhaLabs/soroban-tutorial-project.git"; +const TEMPLATE_URL: &str = "https://github.com/AhaLabs/soroban-init-template.git"; const SOROBAN_EXAMPLES_URL: &str = "https://github.com/stellar/soroban-examples.git"; impl Cmd { From fa504d520f0d34b73842f0bcce0a4b5e6033e664 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 3 Jan 2024 16:32:50 -0500 Subject: [PATCH 07/28] Remove profiles from non-root packages --- cmd/soroban-cli/src/commands/init.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/soroban-cli/src/commands/init.rs b/cmd/soroban-cli/src/commands/init.rs index 4f69d13db0..2c6c4fd83f 100644 --- a/cmd/soroban-cli/src/commands/init.rs +++ b/cmd/soroban-cli/src/commands/init.rs @@ -170,6 +170,8 @@ fn edit_cargo_file(contract_path: &Path) -> Result<(), Error> { doc["dev_dependencies"]["soroban-sdk"] = toml_edit::Item::Value(Value::InlineTable(workspace_table)); + doc.remove("profile"); + std::fs::write(&cargo_path, doc.to_string())?; Ok(()) From 09408d0895a626ea75cbca69c78c67c16be1e5ab Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 3 Jan 2024 17:02:16 -0500 Subject: [PATCH 08/28] Check in Cargo.lock updates --- Cargo.lock | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 27aff09c4f..6e56c40a5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3602,6 +3602,7 @@ dependencies = [ "thiserror", "tokio", "toml", + "toml_edit", "tracing", "tracing-appender", "tracing-subscriber", @@ -4269,6 +4270,23 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.4.13" From 394fcd604a6f0f5dac89ad7f40e1fe26b03a3051 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Thu, 4 Jan 2024 12:36:37 -0500 Subject: [PATCH 09/28] Fix clippy errors --- cmd/soroban-cli/src/commands/init.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/cmd/soroban-cli/src/commands/init.rs b/cmd/soroban-cli/src/commands/init.rs index 2c6c4fd83f..5ad1baf614 100644 --- a/cmd/soroban-cli/src/commands/init.rs +++ b/cmd/soroban-cli/src/commands/init.rs @@ -77,11 +77,13 @@ pub enum Error { #[error("Io error: {0}")] CreateDirError(#[from] io::Error), - #[error("Failed to clone the template repository: {0}")] - CloneError(#[from] gix::clone::Error), + // the gix::clone::Error is too large to include in the error enum as is, so we wrap it in a Box + #[error("Failed to clone the template repository")] + CloneError(#[from] Box), + // the gix::clone::fetch::Error is too large to include in the error enum as is, so we wrap it in a Box #[error("Failed to fetch the template repository: {0}")] - FetchError(#[from] gix::clone::fetch::Error), + FetchError(#[from] Box), #[error("Failed to checkout the template repository: {0}")] CheckoutError(#[from] gix::clone::checkout::main_worktree::Error), @@ -122,7 +124,7 @@ fn init( // if there are with-contract flags, include the example contracts if include_example_contracts(with_contracts) { - println!("Including example contracts: {:?}", with_contracts); + println!("Including example contracts: {with_contracts:?}"); // create an examples temp dir in the temp dir let examples_dir = tempfile::tempdir()?; @@ -191,13 +193,15 @@ fn clone_repo(from_url: &str, to_path: &Path) -> Result<(), Error> { fs_capabilities: None, }, gix::open::Options::isolated(), - )? + ) + .map_err(Box::new)? .with_shallow(gix::remote::fetch::Shallow::DepthAtRemote( NonZeroU32::new(1).unwrap(), )); - let (mut prepare, _outcome) = - fetch.fetch_then_checkout(gix::progress::Discard, &AtomicBool::new(false))?; + let (mut prepare, _outcome) = fetch + .fetch_then_checkout(gix::progress::Discard, &AtomicBool::new(false)) + .map_err(Box::new)?; let (_repo, _outcome) = prepare.main_worktree(gix::progress::Discard, &AtomicBool::new(false))?; @@ -206,8 +210,7 @@ fn clone_repo(from_url: &str, to_path: &Path) -> Result<(), Error> { } fn copy_contents(from: &Path, to: &Path) -> Result<(), Error> { - let contents_to_exclude_from_copy = - vec![".git", ".github", "Makefile", "Cargo.lock", ".vscode"]; + let contents_to_exclude_from_copy = [".git", ".github", "Makefile", "Cargo.lock", ".vscode"]; for entry in fs::read_dir(from)? { let entry = entry?; let path = entry.path(); From 5124c118b0d0a276f0c81bb4b1a124035b46b657 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Thu, 4 Jan 2024 12:57:16 -0500 Subject: [PATCH 10/28] Small tweaks for clarity --- cmd/soroban-cli/src/commands/init.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/soroban-cli/src/commands/init.rs b/cmd/soroban-cli/src/commands/init.rs index 5ad1baf614..e4950d1982 100644 --- a/cmd/soroban-cli/src/commands/init.rs +++ b/cmd/soroban-cli/src/commands/init.rs @@ -153,13 +153,13 @@ fn copy_example_contracts( std::fs::create_dir_all(&to_contract_path)?; copy_contents(&from_contract_path, &to_contract_path)?; - edit_cargo_file(&to_contract_path)?; + edit_contract_cargo_file(&to_contract_path)?; } Ok(()) } -fn edit_cargo_file(contract_path: &Path) -> Result<(), Error> { +fn edit_contract_cargo_file(contract_path: &Path) -> Result<(), Error> { let cargo_path = contract_path.join("Cargo.toml"); let cargo_toml_str = read_to_string(&cargo_path)?; let mut doc = cargo_toml_str.parse::().unwrap(); From 3e5d0a3ead5be86d5a0b5a4eb80a59d8ad297580 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Thu, 4 Jan 2024 13:06:55 -0500 Subject: [PATCH 11/28] Update docs --- docs/soroban-cli-full-docs.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/soroban-cli-full-docs.md b/docs/soroban-cli-full-docs.md index 3546594c61..0b41fae0d6 100644 --- a/docs/soroban-cli-full-docs.md +++ b/docs/soroban-cli-full-docs.md @@ -49,6 +49,7 @@ This document contains the help content for the `soroban` command-line program. * [`soroban keys ls`↴](#soroban-keys-ls) * [`soroban keys rm`↴](#soroban-keys-rm) * [`soroban keys show`↴](#soroban-keys-show) +* [`soroban init`↴](#soroban-init) * [`soroban lab`↴](#soroban-lab) * [`soroban lab token`↴](#soroban-lab-token) * [`soroban lab token wrap`↴](#soroban-lab-token-wrap) @@ -102,6 +103,7 @@ Full CLI reference: https://github.com/stellar/soroban-tools/tree/main/docs/soro * `contract` — Tools for smart contract developers * `events` — Watch the network for contract events * `keys` — Create and manage identities including keys and addresses +* `init` — Initialize a new Soroban project * `lab` — Experiment with early features and expert tools * `network` — Start and configure networks * `version` — Print version information @@ -1032,6 +1034,26 @@ Given an identity return its private key * `--config-dir ` — Location of config directory, default is "." +## `soroban init` + +Initialize a new Soroban project + +**Usage:** `soroban init [OPTIONS] ` + +###### **Arguments:** + +* `` + +###### **Options:** + +* `-w`, `--with-contract ` — optional flag to specify soroban example contracts to include + + Default value: `none` + + Possible values: `account`, `alloc`, `atomic-multiswap`, `atomic-swap`, `auth`, `cross-contract`, `custom-types`, `deep-contract-auth`, `deployer`, `errors`, `events`, `fuzzing`, `hello-world`, `increment`, `liquidity-pool`, `logging`, `simple-account`, `single-offer`, `timelock`, `token`, `upgradeable-contract`, `none` + + + ## `soroban lab` From a47e32ee1c0b0d80d78011564ec75c8ca601cad4 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Thu, 11 Jan 2024 10:11:18 -0500 Subject: [PATCH 12/28] Update --with-contract flag to --with-example Co-authored-by: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> --- cmd/soroban-cli/src/commands/init.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/cmd/soroban-cli/src/commands/init.rs b/cmd/soroban-cli/src/commands/init.rs index e4950d1982..7ea70d4ae2 100644 --- a/cmd/soroban-cli/src/commands/init.rs +++ b/cmd/soroban-cli/src/commands/init.rs @@ -69,7 +69,7 @@ pub struct Cmd { /// optional flag to specify soroban example contracts to include #[arg(short, long, num_args = 1..=20, default_value = "none")] - pub with_contract: Vec, + pub with_example: Vec, } #[derive(thiserror::Error, Debug)] @@ -101,7 +101,7 @@ impl Cmd { println!("Creating a new soroban project at {}", self.project_path); let project_path = Path::new(&self.project_path); - init(project_path, TEMPLATE_URL, &self.with_contract)?; + init(project_path, TEMPLATE_URL, &self.with_example)?; Ok(()) } @@ -110,7 +110,7 @@ impl Cmd { fn init( project_path: &Path, template_url: &str, - with_contracts: &[ExampleContract], + with_examples: &[ExampleContract], ) -> Result<(), Error> { // create a template temp dir to clone the template repo into let template_dir = tempfile::tempdir()?; @@ -123,8 +123,8 @@ fn init( copy_contents(template_dir.path(), project_path)?; // if there are with-contract flags, include the example contracts - if include_example_contracts(with_contracts) { - println!("Including example contracts: {with_contracts:?}"); + if include_example_contracts(with_examples) { + println!("Including example contracts: {with_examples:?}"); // create an examples temp dir in the temp dir let examples_dir = tempfile::tempdir()?; @@ -133,7 +133,7 @@ fn init( clone_repo(SOROBAN_EXAMPLES_URL, examples_dir.path())?; // copy the example contracts into the project - copy_example_contracts(examples_dir.path(), project_path, with_contracts)?; + copy_example_contracts(examples_dir.path(), project_path, with_examples)?; } Ok(()) @@ -242,8 +242,8 @@ mod tests { fn test_init() { let temp_dir = tempfile::tempdir().unwrap(); let project_dir = temp_dir.path().join("project"); - let with_contracts = vec![ExampleContract::None]; - init(project_dir.as_path(), TEMPLATE_URL, &with_contracts).unwrap(); + let with_examples = vec![ExampleContract::None]; + init(project_dir.as_path(), TEMPLATE_URL, &with_examples).unwrap(); assert!(project_dir.as_path().join("README.md").exists()); assert!(project_dir.as_path().join("contracts").exists()); @@ -262,8 +262,8 @@ mod tests { fn test_init_including_example_contract() { let temp_dir = tempfile::tempdir().unwrap(); let project_dir = temp_dir.path().join("project"); - let with_contracts = vec![ExampleContract::Alloc]; - init(project_dir.as_path(), TEMPLATE_URL, &with_contracts).unwrap(); + let with_examples = vec![ExampleContract::Alloc]; + init(project_dir.as_path(), TEMPLATE_URL, &with_examples).unwrap(); assert!(project_dir.as_path().join("README.md").exists()); assert!(project_dir @@ -310,8 +310,8 @@ mod tests { fn test_init_including_multiple_example_contracts() { let temp_dir = tempfile::tempdir().unwrap(); let project_dir = temp_dir.path().join("project"); - let with_contracts = vec![ExampleContract::Account, ExampleContract::AtomicSwap]; - init(project_dir.as_path(), TEMPLATE_URL, &with_contracts).unwrap(); + let with_examples = vec![ExampleContract::Account, ExampleContract::AtomicSwap]; + init(project_dir.as_path(), TEMPLATE_URL, &with_examples).unwrap(); assert!(project_dir .as_path() From 2a3d68ca36c4e04ac5f372b7b7b3920a8aebd5c0 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Thu, 11 Jan 2024 10:18:22 -0500 Subject: [PATCH 13/28] Remove unused anyhow dependency Co-authored-by: Willem Wyndham --- Cargo.lock | 1 - cmd/soroban-cli/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6e56c40a5c..47bb79166b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3547,7 +3547,6 @@ dependencies = [ name = "soroban-cli" version = "20.1.1" dependencies = [ - "anyhow", "assert_cmd", "assert_fs", "base64 0.21.5", diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index f38b33e2b7..1cfe748eaa 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -97,7 +97,6 @@ gix = { version = "0.55.2", default-features = false, features = [ "blocking-http-transport-reqwest-rust-tls", "worktree-mutation", ] } -anyhow = "1.0.75" tempfile = "3.8.1" toml_edit = "0.21.0" # For hyper-tls From ddec708734073e280fcc3c056fcb2eecee521f85 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Thu, 11 Jan 2024 13:34:08 -0500 Subject: [PATCH 14/28] Print example options as a comma separated list instead of an array --- cmd/soroban-cli/src/commands/init.rs | 55 +++++++++++++++------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/cmd/soroban-cli/src/commands/init.rs b/cmd/soroban-cli/src/commands/init.rs index 7ea70d4ae2..21cf869e69 100644 --- a/cmd/soroban-cli/src/commands/init.rs +++ b/cmd/soroban-cli/src/commands/init.rs @@ -1,8 +1,10 @@ +use core::fmt; use std::fs::read_to_string; use std::path::Path; use std::{fs, io}; use clap::Parser; +use itertools::Itertools; use std::num::NonZeroU32; use std::sync::atomic::AtomicBool; use toml_edit::{Document, Formatted, InlineTable, TomlError, Value}; @@ -33,31 +35,31 @@ pub enum ExampleContract { None, } -impl ToString for ExampleContract { - fn to_string(&self) -> String { +impl fmt::Display for ExampleContract { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ExampleContract::Account => String::from("account"), - ExampleContract::Alloc => String::from("alloc"), - ExampleContract::AtomicMultiswap => String::from("atomic_multiswap"), - ExampleContract::AtomicSwap => String::from("atomic_swap"), - ExampleContract::Auth => String::from("auth"), - ExampleContract::CrossContract => String::from("cross_contract"), - ExampleContract::CustomTypes => String::from("custom_types"), - ExampleContract::DeepContractAuth => String::from("deep_contract_auth"), - ExampleContract::Deployer => String::from("deployer"), - ExampleContract::Errors => String::from("errors"), - ExampleContract::Events => String::from("events"), - ExampleContract::Fuzzing => String::from("fuzzing"), - ExampleContract::HelloWorld => String::from("hello_world"), - ExampleContract::Increment => String::from("increment"), - ExampleContract::LiquidityPool => String::from("liquidity_pool"), - ExampleContract::Logging => String::from("logging"), - ExampleContract::SimpleAccount => String::from("simple_account"), - ExampleContract::SingleOffer => String::from("single_offer"), - ExampleContract::Timelock => String::from("timelock"), - ExampleContract::Token => String::from("token"), - ExampleContract::UpgradeableContract => String::from("upgradeable_contract"), - ExampleContract::None => String::from("none"), + ExampleContract::Account => write!(f, "account"), + ExampleContract::Alloc => write!(f, "alloc"), + ExampleContract::AtomicMultiswap => write!(f, "atomic_multiswap"), + ExampleContract::AtomicSwap => write!(f, "atomic_swap"), + ExampleContract::Auth => write!(f, "auth"), + ExampleContract::CrossContract => write!(f, "cross_contract"), + ExampleContract::CustomTypes => write!(f, "custom_types"), + ExampleContract::DeepContractAuth => write!(f, "deep_contract_auth"), + ExampleContract::Deployer => write!(f, "deployer"), + ExampleContract::Errors => write!(f, "errors"), + ExampleContract::Events => write!(f, "events"), + ExampleContract::Fuzzing => write!(f, "fuzzing"), + ExampleContract::HelloWorld => write!(f, "hello_world"), + ExampleContract::Increment => write!(f, "increment"), + ExampleContract::LiquidityPool => write!(f, "liquidity_pool"), + ExampleContract::Logging => write!(f, "logging"), + ExampleContract::SimpleAccount => write!(f, "simple_account"), + ExampleContract::SingleOffer => write!(f, "single_offer"), + ExampleContract::Timelock => write!(f, "timelock"), + ExampleContract::Token => write!(f, "token"), + ExampleContract::UpgradeableContract => write!(f, "upgradeable_contract"), + ExampleContract::None => write!(f, "none"), } } } @@ -124,7 +126,10 @@ fn init( // if there are with-contract flags, include the example contracts if include_example_contracts(with_examples) { - println!("Including example contracts: {with_examples:?}"); + println!( + "Including example contracts: {}", + with_examples.iter().join(", ") + ); // create an examples temp dir in the temp dir let examples_dir = tempfile::tempdir()?; From 9ba0c2b46b56ee6bafee276d6b4f351c74c34b11 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:52:46 -0500 Subject: [PATCH 15/28] Update after rebasing with main --- cmd/soroban-cli/src/commands/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/commands/mod.rs b/cmd/soroban-cli/src/commands/mod.rs index 9bba173b52..51c7b4d730 100644 --- a/cmd/soroban-cli/src/commands/mod.rs +++ b/cmd/soroban-cli/src/commands/mod.rs @@ -7,8 +7,8 @@ pub mod config; pub mod contract; pub mod events; pub mod global; -pub mod keys; pub mod init; +pub mod keys; pub mod lab; pub mod network; pub mod plugin; From 31fdbf999f092fd0d49d14fc812ca4a4635aabe1 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 12 Jan 2024 11:20:56 -0500 Subject: [PATCH 16/28] Print out info about each file being written or skipped and don't overwrite existing files --- cmd/soroban-cli/src/commands/init.rs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/cmd/soroban-cli/src/commands/init.rs b/cmd/soroban-cli/src/commands/init.rs index 21cf869e69..8cf4cd36b8 100644 --- a/cmd/soroban-cli/src/commands/init.rs +++ b/cmd/soroban-cli/src/commands/init.rs @@ -4,7 +4,6 @@ use std::path::Path; use std::{fs, io}; use clap::Parser; -use itertools::Itertools; use std::num::NonZeroU32; use std::sync::atomic::AtomicBool; use toml_edit::{Document, Formatted, InlineTable, TomlError, Value}; @@ -100,7 +99,7 @@ const SOROBAN_EXAMPLES_URL: &str = "https://github.com/stellar/soroban-examples. impl Cmd { #[allow(clippy::unused_self)] pub fn run(&self) -> Result<(), Error> { - println!("Creating a new soroban project at {}", self.project_path); + println!("ℹ️ Initializing project at {}", self.project_path); let project_path = Path::new(&self.project_path); init(project_path, TEMPLATE_URL, &self.with_example)?; @@ -126,11 +125,6 @@ fn init( // if there are with-contract flags, include the example contracts if include_example_contracts(with_examples) { - println!( - "Including example contracts: {}", - with_examples.iter().join(", ") - ); - // create an examples temp dir in the temp dir let examples_dir = tempfile::tempdir()?; @@ -151,6 +145,7 @@ fn copy_example_contracts( ) -> Result<(), Error> { let project_contracts_path = to.join("contracts"); for contract in contracts { + println!("ℹ️ Initializing example contract: {}", contract); let contract_as_string = contract.to_string(); let contract_path = Path::new(&contract_as_string); let from_contract_path = from.join(contract_path); @@ -230,6 +225,15 @@ fn copy_contents(from: &Path, to: &Path) -> Result<(), Error> { std::fs::create_dir_all(&new_path)?; copy_contents(&path, &new_path)?; } else { + if file_exists(&new_path.to_string_lossy()) { + println!( + "ℹ️ Skipped creating {} as it already exists", + &new_path.to_string_lossy() + ); + continue; + } + + println!("➕ Writing {}", &new_path.to_string_lossy()); std::fs::copy(&path, &new_path)?; } } @@ -237,6 +241,14 @@ fn copy_contents(from: &Path, to: &Path) -> Result<(), Error> { Ok(()) } +fn file_exists(file_path: &str) -> bool { + if let Ok(metadata) = fs::metadata(file_path) { + metadata.is_file() + } else { + false + } +} + #[cfg(test)] mod tests { use std::fs::read_to_string; From 73b0ab7ff0d2c99388f71f7cba102aa1fc209766 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 12 Jan 2024 11:59:33 -0500 Subject: [PATCH 17/28] Move init as subcommand under contract --- cmd/soroban-cli/src/commands/{ => contract}/init.rs | 0 cmd/soroban-cli/src/commands/contract/mod.rs | 9 +++++++++ cmd/soroban-cli/src/commands/mod.rs | 6 ------ 3 files changed, 9 insertions(+), 6 deletions(-) rename cmd/soroban-cli/src/commands/{ => contract}/init.rs (100%) diff --git a/cmd/soroban-cli/src/commands/init.rs b/cmd/soroban-cli/src/commands/contract/init.rs similarity index 100% rename from cmd/soroban-cli/src/commands/init.rs rename to cmd/soroban-cli/src/commands/contract/init.rs diff --git a/cmd/soroban-cli/src/commands/contract/mod.rs b/cmd/soroban-cli/src/commands/contract/mod.rs index 35be97a70b..66541a546e 100644 --- a/cmd/soroban-cli/src/commands/contract/mod.rs +++ b/cmd/soroban-cli/src/commands/contract/mod.rs @@ -5,6 +5,7 @@ pub mod deploy; pub mod extend; pub mod fetch; pub mod id; +pub mod init; pub mod inspect; pub mod install; pub mod invoke; @@ -40,6 +41,9 @@ pub enum Cmd { #[command(subcommand)] Id(id::Cmd), + /// Initialize a Soroban project with an example contract + Init(init::Cmd), + /// Inspect a WASM file listing contract functions, meta, etc Inspect(inspect::Cmd), @@ -87,6 +91,10 @@ pub enum Error { #[error(transparent)] Fetch(#[from] fetch::Error), + + #[error(transparent)] + Init(#[from] init::Error), + #[error(transparent)] Id(#[from] id::Error), @@ -118,6 +126,7 @@ impl Cmd { Cmd::Extend(extend) => extend.run().await?, Cmd::Deploy(deploy) => deploy.run().await?, Cmd::Id(id) => id.run()?, + Cmd::Init(init) => init.run()?, Cmd::Inspect(inspect) => inspect.run()?, Cmd::Install(install) => install.run().await?, Cmd::Invoke(invoke) => invoke.run(global_args).await?, diff --git a/cmd/soroban-cli/src/commands/mod.rs b/cmd/soroban-cli/src/commands/mod.rs index 51c7b4d730..952869af3b 100644 --- a/cmd/soroban-cli/src/commands/mod.rs +++ b/cmd/soroban-cli/src/commands/mod.rs @@ -7,7 +7,6 @@ pub mod config; pub mod contract; pub mod events; pub mod global; -pub mod init; pub mod keys; pub mod lab; pub mod network; @@ -95,7 +94,6 @@ impl Root { Cmd::Completion(completion) => completion.run(), Cmd::Contract(contract) => contract.run(&self.global_args).await?, Cmd::Events(events) => events.run().await?, - Cmd::Init(init) => init.run()?, Cmd::Lab(lab) => lab.run().await?, Cmd::Network(network) => network.run()?, Cmd::Version(version) => version.run(), @@ -130,8 +128,6 @@ pub enum Cmd { /// Create and manage identities including keys and addresses #[command(subcommand)] Keys(keys::Cmd), - /// Initialize a new Soroban project - Init(init::Cmd), /// Experiment with early features and expert tools #[command(subcommand)] Lab(lab::Cmd), @@ -152,8 +148,6 @@ pub enum Error { #[error(transparent)] Keys(#[from] keys::Error), #[error(transparent)] - Init(#[from] init::Error), - #[error(transparent)] Lab(#[from] lab::Error), #[error(transparent)] Config(#[from] config::Error), From d71493f99f8739145ca78908baffb8e92071438c Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 12 Jan 2024 16:32:42 -0500 Subject: [PATCH 18/28] Include contract-init-template code in this repo this allows users to run contract init with out internet connectivity --- cmd/soroban-cli/src/commands/contract/init.rs | 39 +- .../utils/contract-init-template/.gitignore | 9 + .../utils/contract-init-template/Cargo.lock | 1485 +++++++++++++++++ .../utils/contract-init-template/Cargo.toml | 23 + .../utils/contract-init-template/README.md | 3 + .../contracts/hello-soroban/Cargo.toml | 16 + .../contracts/hello-soroban/src/lib.rs | 17 + .../contracts/hello-soroban/src/test.rs | 15 + 8 files changed, 1588 insertions(+), 19 deletions(-) create mode 100644 cmd/soroban-cli/src/utils/contract-init-template/.gitignore create mode 100644 cmd/soroban-cli/src/utils/contract-init-template/Cargo.lock create mode 100644 cmd/soroban-cli/src/utils/contract-init-template/Cargo.toml create mode 100644 cmd/soroban-cli/src/utils/contract-init-template/README.md create mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/Cargo.toml create mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/lib.rs create mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/test.rs diff --git a/cmd/soroban-cli/src/commands/contract/init.rs b/cmd/soroban-cli/src/commands/contract/init.rs index 8cf4cd36b8..10414c3894 100644 --- a/cmd/soroban-cli/src/commands/contract/init.rs +++ b/cmd/soroban-cli/src/commands/contract/init.rs @@ -1,7 +1,7 @@ use core::fmt; use std::fs::read_to_string; use std::path::Path; -use std::{fs, io}; +use std::{env, fs, io}; use clap::Parser; use std::num::NonZeroU32; @@ -93,7 +93,6 @@ pub enum Error { TomlParseError(#[from] TomlError), } -const TEMPLATE_URL: &str = "https://github.com/AhaLabs/soroban-init-template.git"; const SOROBAN_EXAMPLES_URL: &str = "https://github.com/stellar/soroban-examples.git"; impl Cmd { @@ -102,26 +101,21 @@ impl Cmd { println!("ℹ️ Initializing project at {}", self.project_path); let project_path = Path::new(&self.project_path); - init(project_path, TEMPLATE_URL, &self.with_example)?; + init(project_path, &self.with_example)?; Ok(()) } } -fn init( - project_path: &Path, - template_url: &str, - with_examples: &[ExampleContract], -) -> Result<(), Error> { - // create a template temp dir to clone the template repo into - let template_dir = tempfile::tempdir()?; - - // clone the template repo into the temp dir - clone_repo(template_url, template_dir.path())?; +fn init(project_path: &Path, with_examples: &[ExampleContract]) -> Result<(), Error> { + let cli_cmd_root = env!("CARGO_MANIFEST_DIR"); + let template_dir_path = Path::new(cli_cmd_root) + .join("src") + .join("utils") + .join("contract-init-template"); - // create the project directory and copy the template contents into it std::fs::create_dir_all(project_path)?; - copy_contents(template_dir.path(), project_path)?; + copy_contents(template_dir_path.as_path(), project_path)?; // if there are with-contract flags, include the example contracts if include_example_contracts(with_examples) { @@ -210,7 +204,14 @@ fn clone_repo(from_url: &str, to_path: &Path) -> Result<(), Error> { } fn copy_contents(from: &Path, to: &Path) -> Result<(), Error> { - let contents_to_exclude_from_copy = [".git", ".github", "Makefile", "Cargo.lock", ".vscode"]; + let contents_to_exclude_from_copy = [ + ".git", + ".github", + "Makefile", + "Cargo.lock", + ".vscode", + "target", + ]; for entry in fs::read_dir(from)? { let entry = entry?; let path = entry.path(); @@ -260,7 +261,7 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let project_dir = temp_dir.path().join("project"); let with_examples = vec![ExampleContract::None]; - init(project_dir.as_path(), TEMPLATE_URL, &with_examples).unwrap(); + init(project_dir.as_path(), &with_examples).unwrap(); assert!(project_dir.as_path().join("README.md").exists()); assert!(project_dir.as_path().join("contracts").exists()); @@ -280,7 +281,7 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let project_dir = temp_dir.path().join("project"); let with_examples = vec![ExampleContract::Alloc]; - init(project_dir.as_path(), TEMPLATE_URL, &with_examples).unwrap(); + init(project_dir.as_path(), &with_examples).unwrap(); assert!(project_dir.as_path().join("README.md").exists()); assert!(project_dir @@ -328,7 +329,7 @@ mod tests { let temp_dir = tempfile::tempdir().unwrap(); let project_dir = temp_dir.path().join("project"); let with_examples = vec![ExampleContract::Account, ExampleContract::AtomicSwap]; - init(project_dir.as_path(), TEMPLATE_URL, &with_examples).unwrap(); + init(project_dir.as_path(), &with_examples).unwrap(); assert!(project_dir .as_path() diff --git a/cmd/soroban-cli/src/utils/contract-init-template/.gitignore b/cmd/soroban-cli/src/utils/contract-init-template/.gitignore new file mode 100644 index 0000000000..8d9d75cc9c --- /dev/null +++ b/cmd/soroban-cli/src/utils/contract-init-template/.gitignore @@ -0,0 +1,9 @@ +target +.soroban + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store diff --git a/cmd/soroban-cli/src/utils/contract-init-template/Cargo.lock b/cmd/soroban-cli/src/utils/contract-init-template/Cargo.lock new file mode 100644 index 0000000000..7c6ec69aa5 --- /dev/null +++ b/cmd/soroban-cli/src/utils/contract-init-template/Cargo.lock @@ -0,0 +1,1485 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base32" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[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", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets 0.48.5", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +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.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "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", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[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", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[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", + "spki", +] + +[[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.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "zeroize", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[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", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[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 = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "hello-soroban" +version = "0.1.0" +dependencies = [ + "soroban-sdk", +] + +[[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.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "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.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[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.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "platforms" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" + +[[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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +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 = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +dependencies = [ + "base64 0.21.5", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.1.0", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[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 = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "20.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a10c4f54b2f546429395dc76b7cca7d89f49a920bfa39834998fc89ad2ac0c8d" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "soroban-env-common" +version = "20.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "803553ca3023ed9fe53b4cdac9f0603da4ef967823bd58b52ea24d73639e1996" +dependencies = [ + "arbitrary", + "crate-git-revision", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr", +] + +[[package]] +name = "soroban-env-guest" +version = "20.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06b756e50b27df7ed8722f8709833fe88a8ced5e1499e81cfc379d74c8330a93" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "20.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80edcbfca555940b538349bdf4c19b96c1845b7a160d22e528e9096a33c4eb7e" +dependencies = [ + "backtrace", + "curve25519-dalek", + "ed25519-dalek", + "getrandom", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "rand", + "rand_chacha", + "sha2", + "sha3", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", + "static_assertions", + "stellar-strkey", +] + +[[package]] +name = "soroban-env-macros" +version = "20.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7a4be3a2737115035b062be921a86ba78374937e4b0918071c7ea4c531240bb" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr", + "syn", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "20.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900b090cdcd3e81bdb8b6a1712e3b53ae682ca0653c27e1cbddff585e13f0a52" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common", + "soroban-env-host", + "thiserror", +] + +[[package]] +name = "soroban-sdk" +version = "20.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b20b89f388b28eedce486b81edf9b5c4f852bc09a592ebe6ebceda6e2a82a2b" +dependencies = [ + "arbitrary", + "bytes-lit", + "ctor", + "ed25519-dalek", + "rand", + "serde", + "serde_json", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey", +] + +[[package]] +name = "soroban-sdk-macros" +version = "20.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5b36fa6f88360bb9092378056cd860d9a8702027306fbfde701a8040f440531" +dependencies = [ + "crate-git-revision", + "darling", + "itertools", + "proc-macro2", + "quote", + "rustc_version", + "sha2", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", + "syn", +] + +[[package]] +name = "soroban-spec" +version = "20.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "315e7d51ff27ff8044f251c55ed3b21f62c13b220f95382584822637d026f7bd" +dependencies = [ + "base64 0.13.1", + "stellar-xdr", + "thiserror", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "20.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0744c5b6011bfc5f933e26662ff874e75b8d8453c5d54c52d1ffe70db7a01788" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2", + "soroban-spec", + "stellar-xdr", + "syn", + "thiserror", +] + +[[package]] +name = "soroban-wasmi" +version = "0.31.1-soroban.20.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1aaa682a67cbd2173f1d60cb1e7b951d490d7c4e0b7b6f5387cbb952e963c46" +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 = "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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12d2bf45e114117ea91d820a846fd1afbe3ba7d717988fee094ce8227a3bf8bd" +dependencies = [ + "base32", + "crate-git-revision", + "thiserror", +] + +[[package]] +name = "stellar-xdr" +version = "20.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f00a85bd9b1617d4cb7e741733889c9940e6bdeca360db81752b0ef04fe3a5" +dependencies = [ + "arbitrary", + "base64 0.13.1", + "crate-git-revision", + "escape-bytes", + "hex", + "serde", + "serde_with", + "stellar-strkey", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +dependencies = [ + "time-core", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "wasmi_arena" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "401c1f35e413fac1846d4843745589d9ec678977ab35a384db8ae7830525d468" + +[[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.88.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8cf7dd82407fe68161bedcd57fde15596f32ebf6e9b3bdbf3ae1da20e38e5e" +dependencies = [ + "indexmap 1.9.3", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/cmd/soroban-cli/src/utils/contract-init-template/Cargo.toml b/cmd/soroban-cli/src/utils/contract-init-template/Cargo.toml new file mode 100644 index 0000000000..560d83f23a --- /dev/null +++ b/cmd/soroban-cli/src/utils/contract-init-template/Cargo.toml @@ -0,0 +1,23 @@ +[workspace] +resolver = "2" +members = [ + "contracts/*", +] + +[workspace.dependencies] +soroban-sdk = "20.0.0" + +[profile.release] +opt-level = "z" +overflow-checks = true +debug = 0 +strip = "symbols" +debug-assertions = false +panic = "abort" +codegen-units = 1 +lto = true + +# For more information about this profile see https://soroban.stellar.org/docs/basic-tutorials/logging#cargotoml-profile +[profile.release-with-logs] +inherits = "release" +debug-assertions = true diff --git a/cmd/soroban-cli/src/utils/contract-init-template/README.md b/cmd/soroban-cli/src/utils/contract-init-template/README.md new file mode 100644 index 0000000000..d78ed28e53 --- /dev/null +++ b/cmd/soroban-cli/src/utils/contract-init-template/README.md @@ -0,0 +1,3 @@ +# Soroban Init Template + +This repository can be used as a template for creating a Soroban project. You can either clone this repository directly, or soon will be able to use the `soroban init` command from the soroban CLI. This first iteration just creates a bare-bones Rust project setup as a Rust workspace, but in the future it will also include options for creating an frontend that is able to interact with contracts in the `contracts` directory as well. diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/Cargo.toml b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/Cargo.toml new file mode 100644 index 0000000000..7385883288 --- /dev/null +++ b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "hello-soroban" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +soroban-sdk = { workspace = true } + +[dev_dependencies] +soroban-sdk = { workspace = true, features = ["testutils"] } + +[features] +testutils = ["soroban-sdk/testutils"] diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/lib.rs b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/lib.rs new file mode 100644 index 0000000000..9e6c88e158 --- /dev/null +++ b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/lib.rs @@ -0,0 +1,17 @@ +#![no_std] +use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Symbol, Vec}; + +#[contract] +pub struct Contract; + +#[contractimpl] +impl Contract { + /// Say Hello to someone or something. + /// Returns a length-2 vector/array containing 'Hello' and then the value passed as `to`. + pub fn hello(env: Env, to: Symbol) -> Vec { + vec![&env, symbol_short!("Hello"), to] + } +} + +#[cfg(test)] +mod test; diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/test.rs b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/test.rs new file mode 100644 index 0000000000..d302e9c963 --- /dev/null +++ b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/test.rs @@ -0,0 +1,15 @@ +use crate::{Contract, ContractClient}; +use soroban_sdk::{symbol_short, vec, Env}; + +#[test] +fn hello() { + let env = Env::default(); + let contract_id = env.register_contract(None, Contract); + let client = ContractClient::new(&env, &contract_id); + + let words = client.hello(&symbol_short!("Dev")); + assert_eq!( + words, + vec![&env, symbol_short!("Hello"), symbol_short!("Dev"),] + ); +} From 26beb4da067732b165fd7ea0399a4b09024eeec5 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 12 Jan 2024 16:37:30 -0500 Subject: [PATCH 19/28] Reorder helper methods --- cmd/soroban-cli/src/commands/contract/init.rs | 142 +++++++++--------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/init.rs b/cmd/soroban-cli/src/commands/contract/init.rs index 10414c3894..388d07b5b9 100644 --- a/cmd/soroban-cli/src/commands/contract/init.rs +++ b/cmd/soroban-cli/src/commands/contract/init.rs @@ -132,77 +132,6 @@ fn init(project_path: &Path, with_examples: &[ExampleContract]) -> Result<(), Er Ok(()) } -fn copy_example_contracts( - from: &Path, - to: &Path, - contracts: &[ExampleContract], -) -> Result<(), Error> { - let project_contracts_path = to.join("contracts"); - for contract in contracts { - println!("ℹ️ Initializing example contract: {}", contract); - let contract_as_string = contract.to_string(); - let contract_path = Path::new(&contract_as_string); - let from_contract_path = from.join(contract_path); - let to_contract_path = project_contracts_path.join(contract_path); - std::fs::create_dir_all(&to_contract_path)?; - - copy_contents(&from_contract_path, &to_contract_path)?; - edit_contract_cargo_file(&to_contract_path)?; - } - - Ok(()) -} - -fn edit_contract_cargo_file(contract_path: &Path) -> Result<(), Error> { - let cargo_path = contract_path.join("Cargo.toml"); - let cargo_toml_str = read_to_string(&cargo_path)?; - let mut doc = cargo_toml_str.parse::().unwrap(); - - let mut workspace_table = InlineTable::new(); - workspace_table.insert("workspace", Value::Boolean(Formatted::new(true))); - - doc["dependencies"]["soroban-sdk"] = - toml_edit::Item::Value(Value::InlineTable(workspace_table.clone())); - doc["dev_dependencies"]["soroban-sdk"] = - toml_edit::Item::Value(Value::InlineTable(workspace_table)); - - doc.remove("profile"); - - std::fs::write(&cargo_path, doc.to_string())?; - - Ok(()) -} - -fn include_example_contracts(contracts: &[ExampleContract]) -> bool { - !(contracts.len() == 1 && contracts[0] == ExampleContract::None) -} - -fn clone_repo(from_url: &str, to_path: &Path) -> Result<(), Error> { - let mut fetch = gix::clone::PrepareFetch::new( - from_url, - to_path, - gix::create::Kind::WithWorktree, - gix::create::Options { - destination_must_be_empty: false, - fs_capabilities: None, - }, - gix::open::Options::isolated(), - ) - .map_err(Box::new)? - .with_shallow(gix::remote::fetch::Shallow::DepthAtRemote( - NonZeroU32::new(1).unwrap(), - )); - - let (mut prepare, _outcome) = fetch - .fetch_then_checkout(gix::progress::Discard, &AtomicBool::new(false)) - .map_err(Box::new)?; - - let (_repo, _outcome) = - prepare.main_worktree(gix::progress::Discard, &AtomicBool::new(false))?; - - Ok(()) -} - fn copy_contents(from: &Path, to: &Path) -> Result<(), Error> { let contents_to_exclude_from_copy = [ ".git", @@ -250,6 +179,77 @@ fn file_exists(file_path: &str) -> bool { } } +fn include_example_contracts(contracts: &[ExampleContract]) -> bool { + !(contracts.len() == 1 && contracts[0] == ExampleContract::None) +} + +fn clone_repo(from_url: &str, to_path: &Path) -> Result<(), Error> { + let mut fetch = gix::clone::PrepareFetch::new( + from_url, + to_path, + gix::create::Kind::WithWorktree, + gix::create::Options { + destination_must_be_empty: false, + fs_capabilities: None, + }, + gix::open::Options::isolated(), + ) + .map_err(Box::new)? + .with_shallow(gix::remote::fetch::Shallow::DepthAtRemote( + NonZeroU32::new(1).unwrap(), + )); + + let (mut prepare, _outcome) = fetch + .fetch_then_checkout(gix::progress::Discard, &AtomicBool::new(false)) + .map_err(Box::new)?; + + let (_repo, _outcome) = + prepare.main_worktree(gix::progress::Discard, &AtomicBool::new(false))?; + + Ok(()) +} + +fn copy_example_contracts( + from: &Path, + to: &Path, + contracts: &[ExampleContract], +) -> Result<(), Error> { + let project_contracts_path = to.join("contracts"); + for contract in contracts { + println!("ℹ️ Initializing example contract: {}", contract); + let contract_as_string = contract.to_string(); + let contract_path = Path::new(&contract_as_string); + let from_contract_path = from.join(contract_path); + let to_contract_path = project_contracts_path.join(contract_path); + std::fs::create_dir_all(&to_contract_path)?; + + copy_contents(&from_contract_path, &to_contract_path)?; + edit_contract_cargo_file(&to_contract_path)?; + } + + Ok(()) +} + +fn edit_contract_cargo_file(contract_path: &Path) -> Result<(), Error> { + let cargo_path = contract_path.join("Cargo.toml"); + let cargo_toml_str = read_to_string(&cargo_path)?; + let mut doc = cargo_toml_str.parse::().unwrap(); + + let mut workspace_table = InlineTable::new(); + workspace_table.insert("workspace", Value::Boolean(Formatted::new(true))); + + doc["dependencies"]["soroban-sdk"] = + toml_edit::Item::Value(Value::InlineTable(workspace_table.clone())); + doc["dev_dependencies"]["soroban-sdk"] = + toml_edit::Item::Value(Value::InlineTable(workspace_table)); + + doc.remove("profile"); + + std::fs::write(&cargo_path, doc.to_string())?; + + Ok(()) +} + #[cfg(test)] mod tests { use std::fs::read_to_string; From 4daf40b6ad29f49325675e253345fc1e8030987e Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 12 Jan 2024 16:42:34 -0500 Subject: [PATCH 20/28] Replace hello-soroban with increment as the default example contract --- .../contracts/hello-soroban/Cargo.toml | 16 --------- .../contracts/hello-soroban/src/lib.rs | 17 ---------- .../contracts/hello-soroban/src/test.rs | 15 -------- .../contracts/increment/Cargo.toml | 31 +++++++++++++++++ .../contracts/increment/src/lib.rs | 34 +++++++++++++++++++ .../contracts/increment/src/test.rs | 19 +++++++++++ 6 files changed, 84 insertions(+), 48 deletions(-) delete mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/Cargo.toml delete mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/lib.rs delete mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/test.rs create mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/Cargo.toml create mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/lib.rs create mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/test.rs diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/Cargo.toml b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/Cargo.toml deleted file mode 100644 index 7385883288..0000000000 --- a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "hello-soroban" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -soroban-sdk = { workspace = true } - -[dev_dependencies] -soroban-sdk = { workspace = true, features = ["testutils"] } - -[features] -testutils = ["soroban-sdk/testutils"] diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/lib.rs b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/lib.rs deleted file mode 100644 index 9e6c88e158..0000000000 --- a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![no_std] -use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Symbol, Vec}; - -#[contract] -pub struct Contract; - -#[contractimpl] -impl Contract { - /// Say Hello to someone or something. - /// Returns a length-2 vector/array containing 'Hello' and then the value passed as `to`. - pub fn hello(env: Env, to: Symbol) -> Vec { - vec![&env, symbol_short!("Hello"), to] - } -} - -#[cfg(test)] -mod test; diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/test.rs b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/test.rs deleted file mode 100644 index d302e9c963..0000000000 --- a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello-soroban/src/test.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::{Contract, ContractClient}; -use soroban_sdk::{symbol_short, vec, Env}; - -#[test] -fn hello() { - let env = Env::default(); - let contract_id = env.register_contract(None, Contract); - let client = ContractClient::new(&env, &contract_id); - - let words = client.hello(&symbol_short!("Dev")); - assert_eq!( - words, - vec![&env, symbol_short!("Hello"), symbol_short!("Dev"),] - ); -} diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/Cargo.toml b/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/Cargo.toml new file mode 100644 index 0000000000..bf2dc444a2 --- /dev/null +++ b/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "soroban-increment-contract" +version = "0.0.0" +authors = ["Stellar Development Foundation "] +license = "Apache-2.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] +doctest = false + +[dependencies] +soroban-sdk = { version = "20.0.0" } + +[dev_dependencies] +soroban-sdk = { version = "20.0.0", features = ["testutils"] } + +[profile.release] +opt-level = "z" +overflow-checks = true +debug = 0 +strip = "symbols" +debug-assertions = false +panic = "abort" +codegen-units = 1 +lto = true + +[profile.release-with-logs] +inherits = "release" +debug-assertions = true diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/lib.rs b/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/lib.rs new file mode 100644 index 0000000000..0a0d0f82d6 --- /dev/null +++ b/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/lib.rs @@ -0,0 +1,34 @@ +#![no_std] +use soroban_sdk::{contract, contractimpl, log, symbol_short, Env, Symbol}; + +const COUNTER: Symbol = symbol_short!("COUNTER"); + +#[contract] +pub struct IncrementContract; + +#[contractimpl] +impl IncrementContract { + /// Increment increments an internal counter, and returns the value. + pub fn increment(env: Env) -> u32 { + // Get the current count. + let mut count: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0); // If no value set, assume 0. + log!(&env, "count: {}", count); + + // Increment the count. + count += 1; + + // Save the count. + env.storage().instance().set(&COUNTER, &count); + + // The contract instance will be bumped to have a lifetime of at least 100 ledgers if the current expiration lifetime at most 50. + // If the lifetime is already more than 100 ledgers, this is a no-op. Otherwise, + // the lifetime is extended to 100 ledgers. This lifetime bump includes the contract + // instance itself and all entries in storage().instance(), i.e, COUNTER. + env.storage().instance().extend_ttl(50, 100); + + // Return the count to the caller. + count + } +} + +mod test; diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/test.rs b/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/test.rs new file mode 100644 index 0000000000..29ec9dcb5d --- /dev/null +++ b/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/test.rs @@ -0,0 +1,19 @@ +#![cfg(test)] + +use super::{IncrementContract, IncrementContractClient}; +use soroban_sdk::{testutils::Logs, Env}; + +extern crate std; + +#[test] +fn test() { + let env = Env::default(); + let contract_id = env.register_contract(None, IncrementContract); + let client = IncrementContractClient::new(&env, &contract_id); + + assert_eq!(client.increment(), 1); + assert_eq!(client.increment(), 2); + assert_eq!(client.increment(), 3); + + std::println!("{}", env.logs().all().join("\n")); +} From 0130df15df4b8d03fad69c07cf734961c85b83a7 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 12 Jan 2024 16:46:24 -0500 Subject: [PATCH 21/28] Clippy --- cmd/soroban-cli/src/commands/contract/init.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/commands/contract/init.rs b/cmd/soroban-cli/src/commands/contract/init.rs index 388d07b5b9..6fe764d602 100644 --- a/cmd/soroban-cli/src/commands/contract/init.rs +++ b/cmd/soroban-cli/src/commands/contract/init.rs @@ -216,7 +216,7 @@ fn copy_example_contracts( ) -> Result<(), Error> { let project_contracts_path = to.join("contracts"); for contract in contracts { - println!("ℹ️ Initializing example contract: {}", contract); + println!("ℹ️ Initializing example contract: {contract}"); let contract_as_string = contract.to_string(); let contract_path = Path::new(&contract_as_string); let from_contract_path = from.join(contract_path); From 3367c271cdb58a8a59a3aadb2676feba505bf23f Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 12 Jan 2024 17:08:09 -0500 Subject: [PATCH 22/28] Add generated docs --- docs/soroban-cli-full-docs.md | 45 ++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/docs/soroban-cli-full-docs.md b/docs/soroban-cli-full-docs.md index 0b41fae0d6..fd7b0a631e 100644 --- a/docs/soroban-cli-full-docs.md +++ b/docs/soroban-cli-full-docs.md @@ -34,6 +34,7 @@ This document contains the help content for the `soroban` command-line program. * [`soroban contract id`↴](#soroban-contract-id) * [`soroban contract id asset`↴](#soroban-contract-id-asset) * [`soroban contract id wasm`↴](#soroban-contract-id-wasm) +* [`soroban contract init`↴](#soroban-contract-init) * [`soroban contract inspect`↴](#soroban-contract-inspect) * [`soroban contract install`↴](#soroban-contract-install) * [`soroban contract invoke`↴](#soroban-contract-invoke) @@ -49,7 +50,6 @@ This document contains the help content for the `soroban` command-line program. * [`soroban keys ls`↴](#soroban-keys-ls) * [`soroban keys rm`↴](#soroban-keys-rm) * [`soroban keys show`↴](#soroban-keys-show) -* [`soroban init`↴](#soroban-init) * [`soroban lab`↴](#soroban-lab) * [`soroban lab token`↴](#soroban-lab-token) * [`soroban lab token wrap`↴](#soroban-lab-token-wrap) @@ -103,7 +103,6 @@ Full CLI reference: https://github.com/stellar/soroban-tools/tree/main/docs/soro * `contract` — Tools for smart contract developers * `events` — Watch the network for contract events * `keys` — Create and manage identities including keys and addresses -* `init` — Initialize a new Soroban project * `lab` — Experiment with early features and expert tools * `network` — Start and configure networks * `version` — Print version information @@ -386,6 +385,7 @@ Tools for smart contract developers * `deploy` — Deploy a wasm contract * `fetch` — Fetch a contract's Wasm binary * `id` — Generate the contract id for a given contract or asset +* `init` — Initialize a Soroban project with an example contract * `inspect` — Inspect a WASM file listing contract functions, meta, etc * `install` — Install a WASM file to the ledger without creating a contract instance * `invoke` — Invoke a contract function @@ -668,6 +668,27 @@ Deploy normal Wasm Contract +## `soroban contract init` + +Initialize a Soroban project with an example contract + +**Usage:** `soroban contract init [OPTIONS] ` + +###### **Arguments:** + +* `` + +###### **Options:** + +* `-w`, `--with-example ` — optional flag to specify soroban example contracts to include + + Default value: `none` + + Possible values: `account`, `alloc`, `atomic-multiswap`, `atomic-swap`, `auth`, `cross-contract`, `custom-types`, `deep-contract-auth`, `deployer`, `errors`, `events`, `fuzzing`, `hello-world`, `increment`, `liquidity-pool`, `logging`, `simple-account`, `single-offer`, `timelock`, `token`, `upgradeable-contract`, `none` + + + + ## `soroban contract inspect` Inspect a WASM file listing contract functions, meta, etc @@ -1034,26 +1055,6 @@ Given an identity return its private key * `--config-dir ` — Location of config directory, default is "." -## `soroban init` - -Initialize a new Soroban project - -**Usage:** `soroban init [OPTIONS] ` - -###### **Arguments:** - -* `` - -###### **Options:** - -* `-w`, `--with-contract ` — optional flag to specify soroban example contracts to include - - Default value: `none` - - Possible values: `account`, `alloc`, `atomic-multiswap`, `atomic-swap`, `auth`, `cross-contract`, `custom-types`, `deep-contract-auth`, `deployer`, `errors`, `events`, `fuzzing`, `hello-world`, `increment`, `liquidity-pool`, `logging`, `simple-account`, `single-offer`, `timelock`, `token`, `upgradeable-contract`, `none` - - - ## `soroban lab` From 409cf35dc3d106b05b9ca15bc5d23ddcb0a253bc Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:28:15 -0500 Subject: [PATCH 23/28] Cleanup tests --- cmd/soroban-cli/src/commands/contract/init.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/init.rs b/cmd/soroban-cli/src/commands/contract/init.rs index 6fe764d602..cb223a725b 100644 --- a/cmd/soroban-cli/src/commands/contract/init.rs +++ b/cmd/soroban-cli/src/commands/contract/init.rs @@ -273,7 +273,7 @@ mod tests { assert!(!project_dir.as_path().join("Cargo.lock").exists()); assert!(!project_dir.as_path().join(".vscode").exists()); - temp_dir.close().unwrap() + temp_dir.close().unwrap(); } #[test] @@ -317,11 +317,10 @@ mod tests { .join("alloc") .join("Cargo.toml"); let cargo_toml_str = read_to_string(contract_cargo_path).unwrap(); - println!("{}", cargo_toml_str); assert!(cargo_toml_str.contains("soroban-sdk = { workspace = true }")); - temp_dir.close().unwrap() + temp_dir.close().unwrap(); } #[test] @@ -342,6 +341,6 @@ mod tests { .join("atomic_swap") .exists()); - temp_dir.close().unwrap() + temp_dir.close().unwrap(); } } From 4f7eac47456d8724a9f0ce86770c4b34415eac83 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 19 Jan 2024 11:09:11 -0500 Subject: [PATCH 24/28] Use hello-world instead of increment as default contract --- .../contracts/hello_world/Cargo.toml | 18 ++++++++++ .../contracts/hello_world/src/lib.rs | 14 ++++++++ .../contracts/hello_world/src/test.rs | 17 ++++++++++ .../contracts/increment/Cargo.toml | 31 ----------------- .../contracts/increment/src/lib.rs | 34 ------------------- .../contracts/increment/src/test.rs | 19 ----------- 6 files changed, 49 insertions(+), 84 deletions(-) create mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/Cargo.toml create mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/lib.rs create mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/test.rs delete mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/Cargo.toml delete mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/lib.rs delete mode 100644 cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/test.rs diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/Cargo.toml b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/Cargo.toml new file mode 100644 index 0000000000..3353ae18c8 --- /dev/null +++ b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "soroban-hello-world-contract" +version = "0.0.0" +authors = ["Stellar Development Foundation "] +license = "Apache-2.0" +edition = "2021" +rust-version = "1.74.0" +publish = false + +[lib] +crate-type = ["cdylib"] +doctest = false + +[dependencies] +soroban-sdk = { workspace = true } + +[dev_dependencies] +soroban-sdk = { workspace = true } diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/lib.rs b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/lib.rs new file mode 100644 index 0000000000..fb23e50556 --- /dev/null +++ b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/lib.rs @@ -0,0 +1,14 @@ +#![no_std] +use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Symbol, Vec}; + +#[contract] +pub struct HelloContract; + +#[contractimpl] +impl HelloContract { + pub fn hello(env: Env, to: Symbol) -> Vec { + vec![&env, symbol_short!("Hello"), to] + } +} + +mod test; diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/test.rs b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/test.rs new file mode 100644 index 0000000000..e72c6bb9fa --- /dev/null +++ b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/test.rs @@ -0,0 +1,17 @@ +#![cfg(test)] + +use super::*; +use soroban_sdk::{symbol_short, vec, Env}; + +#[test] +fn test() { + let env = Env::default(); + let contract_id = env.register_contract(None, HelloContract); + let client = HelloContractClient::new(&env, &contract_id); + + let words = client.hello(&symbol_short!("Dev")); + assert_eq!( + words, + vec![&env, symbol_short!("Hello"), symbol_short!("Dev"),] + ); +} diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/Cargo.toml b/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/Cargo.toml deleted file mode 100644 index bf2dc444a2..0000000000 --- a/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "soroban-increment-contract" -version = "0.0.0" -authors = ["Stellar Development Foundation "] -license = "Apache-2.0" -edition = "2021" -publish = false - -[lib] -crate-type = ["cdylib"] -doctest = false - -[dependencies] -soroban-sdk = { version = "20.0.0" } - -[dev_dependencies] -soroban-sdk = { version = "20.0.0", features = ["testutils"] } - -[profile.release] -opt-level = "z" -overflow-checks = true -debug = 0 -strip = "symbols" -debug-assertions = false -panic = "abort" -codegen-units = 1 -lto = true - -[profile.release-with-logs] -inherits = "release" -debug-assertions = true diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/lib.rs b/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/lib.rs deleted file mode 100644 index 0a0d0f82d6..0000000000 --- a/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/lib.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![no_std] -use soroban_sdk::{contract, contractimpl, log, symbol_short, Env, Symbol}; - -const COUNTER: Symbol = symbol_short!("COUNTER"); - -#[contract] -pub struct IncrementContract; - -#[contractimpl] -impl IncrementContract { - /// Increment increments an internal counter, and returns the value. - pub fn increment(env: Env) -> u32 { - // Get the current count. - let mut count: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0); // If no value set, assume 0. - log!(&env, "count: {}", count); - - // Increment the count. - count += 1; - - // Save the count. - env.storage().instance().set(&COUNTER, &count); - - // The contract instance will be bumped to have a lifetime of at least 100 ledgers if the current expiration lifetime at most 50. - // If the lifetime is already more than 100 ledgers, this is a no-op. Otherwise, - // the lifetime is extended to 100 ledgers. This lifetime bump includes the contract - // instance itself and all entries in storage().instance(), i.e, COUNTER. - env.storage().instance().extend_ttl(50, 100); - - // Return the count to the caller. - count - } -} - -mod test; diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/test.rs b/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/test.rs deleted file mode 100644 index 29ec9dcb5d..0000000000 --- a/cmd/soroban-cli/src/utils/contract-init-template/contracts/increment/src/test.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![cfg(test)] - -use super::{IncrementContract, IncrementContractClient}; -use soroban_sdk::{testutils::Logs, Env}; - -extern crate std; - -#[test] -fn test() { - let env = Env::default(); - let contract_id = env.register_contract(None, IncrementContract); - let client = IncrementContractClient::new(&env, &contract_id); - - assert_eq!(client.increment(), 1); - assert_eq!(client.increment(), 2); - assert_eq!(client.increment(), 3); - - std::println!("{}", env.logs().all().join("\n")); -} From c2a2f229f9a5fc00edeccd4b77583d3b0c5e577d Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 19 Jan 2024 11:55:41 -0500 Subject: [PATCH 25/28] Remove hello-world from example options --- cmd/soroban-cli/src/commands/contract/init.rs | 10 +++------- docs/soroban-cli-full-docs.md | 6 ++---- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/init.rs b/cmd/soroban-cli/src/commands/contract/init.rs index cb223a725b..4bed47b96a 100644 --- a/cmd/soroban-cli/src/commands/contract/init.rs +++ b/cmd/soroban-cli/src/commands/contract/init.rs @@ -22,7 +22,6 @@ pub enum ExampleContract { Errors, Events, Fuzzing, - HelloWorld, Increment, LiquidityPool, Logging, @@ -31,7 +30,6 @@ pub enum ExampleContract { Timelock, Token, UpgradeableContract, - None, } impl fmt::Display for ExampleContract { @@ -49,7 +47,6 @@ impl fmt::Display for ExampleContract { ExampleContract::Errors => write!(f, "errors"), ExampleContract::Events => write!(f, "events"), ExampleContract::Fuzzing => write!(f, "fuzzing"), - ExampleContract::HelloWorld => write!(f, "hello_world"), ExampleContract::Increment => write!(f, "increment"), ExampleContract::LiquidityPool => write!(f, "liquidity_pool"), ExampleContract::Logging => write!(f, "logging"), @@ -58,7 +55,6 @@ impl fmt::Display for ExampleContract { ExampleContract::Timelock => write!(f, "timelock"), ExampleContract::Token => write!(f, "token"), ExampleContract::UpgradeableContract => write!(f, "upgradeable_contract"), - ExampleContract::None => write!(f, "none"), } } } @@ -68,8 +64,8 @@ impl fmt::Display for ExampleContract { pub struct Cmd { pub project_path: String, - /// optional flag to specify soroban example contracts to include - #[arg(short, long, num_args = 1..=20, default_value = "none")] + /// An optional flag to specify Soroban example contracts to include. A hello-world contract will be included by default. + #[arg(short, long, num_args = 1..=20)] pub with_example: Vec, } @@ -180,7 +176,7 @@ fn file_exists(file_path: &str) -> bool { } fn include_example_contracts(contracts: &[ExampleContract]) -> bool { - !(contracts.len() == 1 && contracts[0] == ExampleContract::None) + contracts.len() >= 1 } fn clone_repo(from_url: &str, to_path: &Path) -> Result<(), Error> { diff --git a/docs/soroban-cli-full-docs.md b/docs/soroban-cli-full-docs.md index fd7b0a631e..34ab0caa23 100644 --- a/docs/soroban-cli-full-docs.md +++ b/docs/soroban-cli-full-docs.md @@ -680,11 +680,9 @@ Initialize a Soroban project with an example contract ###### **Options:** -* `-w`, `--with-example ` — optional flag to specify soroban example contracts to include +* `-w`, `--with-example ` — An optional flag to specify Soroban example contracts to include. A hello-world contract will be included by default - Default value: `none` - - Possible values: `account`, `alloc`, `atomic-multiswap`, `atomic-swap`, `auth`, `cross-contract`, `custom-types`, `deep-contract-auth`, `deployer`, `errors`, `events`, `fuzzing`, `hello-world`, `increment`, `liquidity-pool`, `logging`, `simple-account`, `single-offer`, `timelock`, `token`, `upgradeable-contract`, `none` + Possible values: `account`, `alloc`, `atomic-multiswap`, `atomic-swap`, `auth`, `cross-contract`, `custom-types`, `deep-contract-auth`, `deployer`, `errors`, `events`, `fuzzing`, `increment`, `liquidity-pool`, `logging`, `simple-account`, `single-offer`, `timelock`, `token`, `upgradeable-contract` From 3753d7286609db289f95f85c739d579377c8bcff Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 19 Jan 2024 16:12:03 -0500 Subject: [PATCH 26/28] Clean up tests --- cmd/soroban-cli/src/commands/contract/init.rs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/init.rs b/cmd/soroban-cli/src/commands/contract/init.rs index 4bed47b96a..435ea20e21 100644 --- a/cmd/soroban-cli/src/commands/contract/init.rs +++ b/cmd/soroban-cli/src/commands/contract/init.rs @@ -176,7 +176,7 @@ fn file_exists(file_path: &str) -> bool { } fn include_example_contracts(contracts: &[ExampleContract]) -> bool { - contracts.len() >= 1 + !contracts.is_empty() } fn clone_repo(from_url: &str, to_path: &Path) -> Result<(), Error> { @@ -256,13 +256,28 @@ mod tests { fn test_init() { let temp_dir = tempfile::tempdir().unwrap(); let project_dir = temp_dir.path().join("project"); - let with_examples = vec![ExampleContract::None]; + let with_examples = vec![]; init(project_dir.as_path(), &with_examples).unwrap(); assert!(project_dir.as_path().join("README.md").exists()); assert!(project_dir.as_path().join("contracts").exists()); assert!(project_dir.as_path().join("Cargo.toml").exists()); + // check that it includes the default hello-world contract + assert!(project_dir + .as_path() + .join("contracts") + .join("hello_world") + .exists()); + // check that the contract's Cargo.toml file uses the workspace for dependencies + let contract_cargo_path = project_dir + .as_path() + .join("contracts") + .join("hello_world") + .join("Cargo.toml"); + let cargo_toml_str = read_to_string(contract_cargo_path).unwrap(); + assert!(cargo_toml_str.contains("soroban-sdk = { workspace = true }")); + // check that it does not include certain template files and directories assert!(!project_dir.as_path().join(".git").exists()); assert!(!project_dir.as_path().join(".github").exists()); @@ -313,7 +328,6 @@ mod tests { .join("alloc") .join("Cargo.toml"); let cargo_toml_str = read_to_string(contract_cargo_path).unwrap(); - assert!(cargo_toml_str.contains("soroban-sdk = { workspace = true }")); temp_dir.close().unwrap(); From b6671e2d02fef7063a9364cc8af17ef3152f6c20 Mon Sep 17 00:00:00 2001 From: Aditya Vyas Date: Thu, 25 Jan 2024 12:11:01 -0500 Subject: [PATCH 27/28] soroban-rpc: Remove panics from internal codebase (#1167) * Remove panic - 1 * Remove panic - 2 * Remove panic - 3 * Remove panic - 4 * Small changes - 1 * undo changes in Get() func * undo changes - 2 * undo changes - 3 * add test for append error --- .../internal/config/config_option.go | 4 +- .../internal/config/config_option_test.go | 11 ++ cmd/soroban-rpc/internal/config/log_format.go | 12 +- cmd/soroban-rpc/internal/config/options.go | 2 +- .../ledgerbucketwindow/ledgerbucketwindow.go | 6 +- .../ledgerbucketwindow_test.go | 123 +++++++----------- .../internal/transactions/transactions.go | 5 +- 7 files changed, 73 insertions(+), 90 deletions(-) diff --git a/cmd/soroban-rpc/internal/config/config_option.go b/cmd/soroban-rpc/internal/config/config_option.go index 86eab8e7b4..3c8ca87a4e 100644 --- a/cmd/soroban-rpc/internal/config/config_option.go +++ b/cmd/soroban-rpc/internal/config/config_option.go @@ -88,8 +88,6 @@ func (o *ConfigOption) setValue(i interface{}) (err error) { if o.CustomSetValue != nil { return o.CustomSetValue(o, i) } - // it's unfortunate that Set below panics when it cannot set the value.. - // we'll want to catch this so that we can alert the user nicely. defer func() { if recoverRes := recover(); recoverRes != nil { var ok bool @@ -101,7 +99,7 @@ func (o *ConfigOption) setValue(i interface{}) (err error) { } }() parser := func(option *ConfigOption, i interface{}) error { - panic(fmt.Sprintf("no parser for flag %s", o.Name)) + return errors.Errorf("no parser for flag %s", o.Name) } switch o.ConfigKey.(type) { case *bool: diff --git a/cmd/soroban-rpc/internal/config/config_option_test.go b/cmd/soroban-rpc/internal/config/config_option_test.go index 831c886560..a6309cb3e9 100644 --- a/cmd/soroban-rpc/internal/config/config_option_test.go +++ b/cmd/soroban-rpc/internal/config/config_option_test.go @@ -4,6 +4,7 @@ import ( "fmt" "math" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -115,6 +116,16 @@ func TestUnassignableField(t *testing.T) { require.Contains(t, err.Error(), co.Name) } +func TestNoParserForFlag(t *testing.T) { + var co ConfigOption + var invalidKey []time.Duration + co.Name = "mykey" + co.ConfigKey = &invalidKey + err := co.setValue("abc") + require.Error(t, err) + require.Contains(t, err.Error(), "no parser for flag mykey") +} + func TestSetValue(t *testing.T) { var b bool var i int diff --git a/cmd/soroban-rpc/internal/config/log_format.go b/cmd/soroban-rpc/internal/config/log_format.go index 076e43e68b..1ab4c7fc64 100644 --- a/cmd/soroban-rpc/internal/config/log_format.go +++ b/cmd/soroban-rpc/internal/config/log_format.go @@ -1,6 +1,8 @@ package config -import "fmt" +import ( + "fmt" +) type LogFormat int @@ -47,13 +49,13 @@ func (f *LogFormat) UnmarshalTOML(i interface{}) error { } } -func (f LogFormat) String() string { +func (f LogFormat) String() (string, error) { switch f { case LogFormatText: - return "text" + return "text", nil case LogFormatJSON: - return "json" + return "json", nil default: - panic(fmt.Sprintf("unknown log format: %d", f)) + return "", fmt.Errorf("unknown log format: %d", f) } } diff --git a/cmd/soroban-rpc/internal/config/options.go b/cmd/soroban-rpc/internal/config/options.go index cecfb2e7d9..d38dd7cd5b 100644 --- a/cmd/soroban-rpc/internal/config/options.go +++ b/cmd/soroban-rpc/internal/config/options.go @@ -130,7 +130,7 @@ func (cfg *Config) options() ConfigOptions { return nil }, MarshalTOML: func(option *ConfigOption) (interface{}, error) { - return cfg.LogFormat.String(), nil + return cfg.LogFormat.String() }, }, { diff --git a/cmd/soroban-rpc/internal/ledgerbucketwindow/ledgerbucketwindow.go b/cmd/soroban-rpc/internal/ledgerbucketwindow/ledgerbucketwindow.go index 0d447e7163..8234b60765 100644 --- a/cmd/soroban-rpc/internal/ledgerbucketwindow/ledgerbucketwindow.go +++ b/cmd/soroban-rpc/internal/ledgerbucketwindow/ledgerbucketwindow.go @@ -35,12 +35,12 @@ func NewLedgerBucketWindow[T any](retentionWindow uint32) *LedgerBucketWindow[T] } // Append adds a new bucket to the window. If the window is full a bucket will be evicted and returned. -func (w *LedgerBucketWindow[T]) Append(bucket LedgerBucket[T]) *LedgerBucket[T] { +func (w *LedgerBucketWindow[T]) Append(bucket LedgerBucket[T]) (*LedgerBucket[T], error) { length := w.Len() if length > 0 { expectedLedgerSequence := w.buckets[w.start].LedgerSeq + length if expectedLedgerSequence != bucket.LedgerSeq { - panic(fmt.Errorf("ledgers not contiguous: expected ledger sequence %v but received %v", expectedLedgerSequence, bucket.LedgerSeq)) + return &LedgerBucket[T]{}, fmt.Errorf("error appending ledgers: ledgers not contiguous: expected ledger sequence %v but received %v", expectedLedgerSequence, bucket.LedgerSeq) } } @@ -57,7 +57,7 @@ func (w *LedgerBucketWindow[T]) Append(bucket LedgerBucket[T]) *LedgerBucket[T] w.start = (w.start + 1) % length } - return evicted + return evicted, nil } // Len returns the length (number of buckets in the window) diff --git a/cmd/soroban-rpc/internal/ledgerbucketwindow/ledgerbucketwindow_test.go b/cmd/soroban-rpc/internal/ledgerbucketwindow/ledgerbucketwindow_test.go index b472af0b45..2e50ed6d53 100644 --- a/cmd/soroban-rpc/internal/ledgerbucketwindow/ledgerbucketwindow_test.go +++ b/cmd/soroban-rpc/internal/ledgerbucketwindow/ledgerbucketwindow_test.go @@ -18,118 +18,87 @@ func TestAppend(t *testing.T) { m := NewLedgerBucketWindow[uint32](3) require.Equal(t, uint32(0), m.Len()) - // test appending first bucket of events - evicted := m.Append(bucket(5)) + // Test appending first bucket of events + evicted, err := m.Append(bucket(5)) + require.NoError(t, err) require.Nil(t, evicted) require.Equal(t, uint32(1), m.Len()) require.Equal(t, bucket(5), *m.Get(0)) - // the next bucket must follow the previous bucket (ledger 5) - - require.PanicsWithError( - t, "ledgers not contiguous: expected ledger sequence 6 but received 10", - func() { - m.Append(LedgerBucket[uint32]{ - LedgerSeq: 10, - LedgerCloseTimestamp: 100, - BucketContent: 10, - }) - }, - ) - require.PanicsWithError( - t, "ledgers not contiguous: expected ledger sequence 6 but received 4", - func() { - m.Append(LedgerBucket[uint32]{ - LedgerSeq: 4, - LedgerCloseTimestamp: 100, - BucketContent: 4, - }) - }, - ) - require.PanicsWithError( - t, "ledgers not contiguous: expected ledger sequence 6 but received 5", - func() { - m.Append(LedgerBucket[uint32]{ - LedgerSeq: 5, - LedgerCloseTimestamp: 100, - BucketContent: 5, - }) - }, - ) + // The next bucket must follow the previous bucket (ledger 5) + _, err = m.Append(LedgerBucket[uint32]{ + LedgerSeq: 10, + LedgerCloseTimestamp: 100, + BucketContent: 10, + }) + require.Errorf(t, err, "ledgers not contiguous: expected ledger sequence 6 but received 10") + + _, err = m.Append(LedgerBucket[uint32]{ + LedgerSeq: 4, + LedgerCloseTimestamp: 100, + BucketContent: 4, + }) + require.Errorf(t, err, "ledgers not contiguous: expected ledger sequence 6 but received 4") + // check that none of the calls above modified our buckets require.Equal(t, uint32(1), m.Len()) require.Equal(t, bucket(5), *m.Get(0)) - // append ledger 6 bucket, now we have two buckets filled - evicted = m.Append(bucket(6)) + // Append ledger 6 bucket, now we have two buckets filled + evicted, err = m.Append(bucket(6)) + require.NoError(t, err) require.Nil(t, evicted) require.Equal(t, uint32(2), m.Len()) require.Equal(t, bucket(5), *m.Get(0)) require.Equal(t, bucket(6), *m.Get(1)) - // the next bucket of events must follow the previous bucket (ledger 6) - require.PanicsWithError( - t, "ledgers not contiguous: expected ledger sequence 7 but received 10", - func() { - m.Append(LedgerBucket[uint32]{ - LedgerSeq: 10, - LedgerCloseTimestamp: 100, - BucketContent: 10, - }) - }, - ) - require.PanicsWithError( - t, "ledgers not contiguous: expected ledger sequence 7 but received 4", - func() { - m.Append(LedgerBucket[uint32]{ - LedgerSeq: 4, - LedgerCloseTimestamp: 100, - BucketContent: 4, - }) - }, - ) - require.PanicsWithError( - t, "ledgers not contiguous: expected ledger sequence 7 but received 5", - func() { - m.Append(LedgerBucket[uint32]{ - LedgerSeq: 5, - LedgerCloseTimestamp: 100, - BucketContent: 5, - }) - }, - ) - - // append ledger 7, now we have all three buckets filled - evicted = m.Append(bucket(7)) - require.Nil(t, evicted) + // Append ledger 7, now we have all three buckets filled + evicted, err = m.Append(bucket(7)) + require.NoError(t, err) require.Nil(t, evicted) require.Equal(t, uint32(3), m.Len()) require.Equal(t, bucket(5), *m.Get(0)) require.Equal(t, bucket(6), *m.Get(1)) require.Equal(t, bucket(7), *m.Get(2)) - // append ledger 8, but all buckets are full, so we need to evict ledger 5 - evicted = m.Append(bucket(8)) + // Append ledger 8, but all buckets are full, so we need to evict ledger 5 + evicted, err = m.Append(bucket(8)) + require.NoError(t, err) require.Equal(t, bucket(5), *evicted) require.Equal(t, uint32(3), m.Len()) require.Equal(t, bucket(6), *m.Get(0)) require.Equal(t, bucket(7), *m.Get(1)) require.Equal(t, bucket(8), *m.Get(2)) - // append ledger 9 events, but all buckets are full, so we need to evict ledger 6 - evicted = m.Append(bucket(9)) + // Append ledger 9 events, but all buckets are full, so we need to evict ledger 6 + evicted, err = m.Append(bucket(9)) + require.NoError(t, err) require.Equal(t, bucket(6), *evicted) require.Equal(t, uint32(3), m.Len()) require.Equal(t, bucket(7), *m.Get(0)) require.Equal(t, bucket(8), *m.Get(1)) require.Equal(t, bucket(9), *m.Get(2)) - // append ledger 10, but all buckets are full, so we need to evict ledger 7. + // Append ledger 10, but all buckets are full, so we need to evict ledger 7. // The start index must have wrapped around - evicted = m.Append(bucket(10)) + evicted, err = m.Append(bucket(10)) + require.NoError(t, err) require.Equal(t, bucket(7), *evicted) require.Equal(t, uint32(3), m.Len()) require.Equal(t, bucket(8), *m.Get(0)) require.Equal(t, bucket(9), *m.Get(1)) require.Equal(t, bucket(10), *m.Get(2)) } + +func TestAppendError(t *testing.T) { + m := NewLedgerBucketWindow[uint32](3) + require.Equal(t, uint32(0), m.Len()) + + evicted, err := m.Append(bucket(5)) + require.NoError(t, err) + require.Nil(t, evicted) + + evicted, err = m.Append(bucket(1)) + require.Error(t, err) + require.Contains(t, err.Error(), "error appending ledgers: ledgers not contiguous: expected ledger sequence 6 but received 1") +} diff --git a/cmd/soroban-rpc/internal/transactions/transactions.go b/cmd/soroban-rpc/internal/transactions/transactions.go index 8d58a035a1..5ed719ade6 100644 --- a/cmd/soroban-rpc/internal/transactions/transactions.go +++ b/cmd/soroban-rpc/internal/transactions/transactions.go @@ -122,7 +122,10 @@ func (m *MemoryStore) IngestTransactions(ledgerCloseMeta xdr.LedgerCloseMeta) er m.lock.Lock() defer m.lock.Unlock() - evicted := m.transactionsByLedger.Append(bucket) + evicted, err := m.transactionsByLedger.Append(bucket) + if err != nil { + return err + } if evicted != nil { // garbage-collect evicted entries for _, evictedTxHash := range evicted.BucketContent { From 433cd44dbc897c85fadf899448acbb31da62256d Mon Sep 17 00:00:00 2001 From: Alfonso Acosta Date: Wed, 31 Jan 2024 11:05:42 +0100 Subject: [PATCH 28/28] rpc: Reduce event memory footprint (#1183) 1. Get rid of the in-memory operation index (since it was always set to zero anyways) 2. Keep events serialized while in memory (it saves quite a bit of space due to the inneficient representation of unions in golang). --- cmd/soroban-rpc/internal/events/cursor.go | 2 + cmd/soroban-rpc/internal/events/events.go | 40 +++++---- .../internal/events/events_test.go | 82 +++++++++---------- 3 files changed, 61 insertions(+), 63 deletions(-) diff --git a/cmd/soroban-rpc/internal/events/cursor.go b/cmd/soroban-rpc/internal/events/cursor.go index 9f37b51303..3fbfbecb85 100644 --- a/cmd/soroban-rpc/internal/events/cursor.go +++ b/cmd/soroban-rpc/internal/events/cursor.go @@ -20,6 +20,8 @@ type Cursor struct { // Tx is the index of the transaction within the ledger which emitted the event. Tx uint32 // Op is the index of the operation within the transaction which emitted the event. + // Note: Currently, there is no use for it (events are transaction-wide and not operation-specific) + // but we keep it in order to make the API future-proof. Op uint32 // Event is the index of the event within in the operation which emitted the event. Event uint32 diff --git a/cmd/soroban-rpc/internal/events/events.go b/cmd/soroban-rpc/internal/events/events.go index 0c5fdc8397..e2c9030bf1 100644 --- a/cmd/soroban-rpc/internal/events/events.go +++ b/cmd/soroban-rpc/internal/events/events.go @@ -15,24 +15,16 @@ import ( "github.com/stellar/soroban-tools/cmd/soroban-rpc/internal/ledgerbucketwindow" ) -type bucket struct { - ledgerSeq uint32 - ledgerCloseTimestamp int64 - events []event -} - type event struct { - contents xdr.DiagnosticEvent - txIndex uint32 - opIndex uint32 - eventIndex uint32 + diagnosticEventXDR []byte + txIndex uint32 + eventIndex uint32 } func (e event) cursor(ledgerSeq uint32) Cursor { return Cursor{ Ledger: ledgerSeq, Tx: e.txIndex, - Op: e.opIndex, Event: e.eventIndex, } } @@ -129,7 +121,12 @@ func (m *MemoryStore) Scan(eventRange Range, f func(xdr.DiagnosticEvent, Cursor, if eventRange.End.Cmp(cur) <= 0 { return lastLedgerInWindow, nil } - if !f(event.contents, cur, timestamp) { + var diagnosticEvent xdr.DiagnosticEvent + err := xdr.SafeUnmarshal(event.diagnosticEventXDR, &diagnosticEvent) + if err != nil { + return 0, err + } + if !f(diagnosticEvent, cur, timestamp) { return lastLedgerInWindow, nil } } @@ -201,7 +198,9 @@ func (m *MemoryStore) IngestEvents(ledgerCloseMeta xdr.LedgerCloseMeta) error { BucketContent: events, } m.lock.Lock() - m.eventsByLedger.Append(bucket) + if _, err = m.eventsByLedger.Append(bucket); err != nil { + return err + } m.lock.Unlock() m.eventsDurationMetric.With(prometheus.Labels{"operation": "ingest"}). Observe(time.Since(startTime).Seconds()) @@ -241,15 +240,14 @@ func readEvents(networkPassphrase string, ledgerCloseMeta xdr.LedgerCloseMeta) ( return nil, err } for index, e := range txEvents { + diagnosticEventXDR, err := e.MarshalBinary() + if err != nil { + return nil, err + } events = append(events, event{ - contents: e, - txIndex: tx.Index, - // NOTE: we cannot really index by operation since all events - // are provided as part of the transaction. However, - // that shouldn't matter in practice since a transaction - // can only contain a single Host Function Invocation. - opIndex: 0, - eventIndex: uint32(index), + diagnosticEventXDR: diagnosticEventXDR, + txIndex: tx.Index, + eventIndex: uint32(index), }) } } diff --git a/cmd/soroban-rpc/internal/events/events_test.go b/cmd/soroban-rpc/internal/events/events_test.go index 9f6a3fe0dc..fc4d3c6b95 100644 --- a/cmd/soroban-rpc/internal/events/events_test.go +++ b/cmd/soroban-rpc/internal/events/events_test.go @@ -1,6 +1,7 @@ package events import ( + "bytes" "testing" "github.com/stellar/go/xdr" @@ -13,24 +14,24 @@ import ( var ( ledger5CloseTime = ledgerCloseTime(5) ledger5Events = []event{ - newEvent(1, 0, 0, 100), - newEvent(1, 0, 1, 200), - newEvent(2, 0, 0, 300), - newEvent(2, 1, 0, 400), + newEvent(1, 0, 100), + newEvent(1, 1, 200), + newEvent(2, 0, 300), + newEvent(2, 1, 400), } ledger6CloseTime = ledgerCloseTime(6) ledger6Events []event = nil ledger7CloseTime = ledgerCloseTime(7) ledger7Events = []event{ - newEvent(1, 0, 0, 500), + newEvent(1, 0, 500), } ledger8CloseTime = ledgerCloseTime(8) ledger8Events = []event{ - newEvent(1, 0, 0, 600), - newEvent(2, 0, 0, 700), - newEvent(2, 0, 1, 800), - newEvent(2, 0, 2, 900), - newEvent(2, 1, 0, 1000), + newEvent(1, 0, 600), + newEvent(2, 0, 700), + newEvent(2, 1, 800), + newEvent(2, 2, 900), + newEvent(2, 3, 1000), } ) @@ -38,43 +39,39 @@ func ledgerCloseTime(seq uint32) int64 { return int64(seq)*25 + 100 } -func newEvent(txIndex, opIndex, eventIndex, val uint32) event { +func newEvent(txIndex, eventIndex, val uint32) event { v := xdr.Uint32(val) - return event{ - contents: xdr.DiagnosticEvent{ - InSuccessfulContractCall: true, - Event: xdr.ContractEvent{ - Type: xdr.ContractEventTypeSystem, - Body: xdr.ContractEventBody{ - V: 0, - V0: &xdr.ContractEventV0{ - Data: xdr.ScVal{ - Type: xdr.ScValTypeScvU32, - U32: &v, - }, + + e := xdr.DiagnosticEvent{ + InSuccessfulContractCall: true, + Event: xdr.ContractEvent{ + Type: xdr.ContractEventTypeSystem, + Body: xdr.ContractEventBody{ + V: 0, + V0: &xdr.ContractEventV0{ + Data: xdr.ScVal{ + Type: xdr.ScValTypeScvU32, + U32: &v, }, }, }, }, - txIndex: txIndex, - opIndex: opIndex, - eventIndex: eventIndex, } -} - -func mustMarshal(e xdr.DiagnosticEvent) string { - result, err := xdr.MarshalBase64(e) + diagnosticEventXDR, err := e.MarshalBinary() if err != nil { panic(err) } - return result + return event{ + diagnosticEventXDR: diagnosticEventXDR, + txIndex: txIndex, + eventIndex: eventIndex, + } } func (e event) equals(other event) bool { return e.txIndex == other.txIndex && - e.opIndex == other.opIndex && e.eventIndex == other.eventIndex && - mustMarshal(e.contents) == mustMarshal(other.contents) + bytes.Equal(e.diagnosticEventXDR, other.diagnosticEventXDR) } func eventsAreEqual(t *testing.T, a, b []event) { @@ -291,7 +288,7 @@ func TestScan(t *testing.T) { }, { Range{ - Start: Cursor{Ledger: 5, Tx: 1, Op: 2}, + Start: Cursor{Ledger: 5, Tx: 2}, ClampStart: false, End: Cursor{Ledger: 9}, ClampEnd: false, @@ -327,7 +324,7 @@ func TestScan(t *testing.T) { }, { Range{ - Start: Cursor{Ledger: 8, Tx: 2, Op: 1, Event: 0}, + Start: Cursor{Ledger: 8, Tx: 2, Event: 3}, ClampStart: false, End: MaxCursor, ClampEnd: true, @@ -336,7 +333,7 @@ func TestScan(t *testing.T) { }, { Range{ - Start: Cursor{Ledger: 8, Tx: 2, Op: 1, Event: 0}, + Start: Cursor{Ledger: 8, Tx: 2, Event: 3}, ClampStart: false, End: Cursor{Ledger: 9}, ClampEnd: false, @@ -354,9 +351,9 @@ func TestScan(t *testing.T) { }, { Range{ - Start: Cursor{Ledger: 5, Tx: 1, Op: 2}, + Start: Cursor{Ledger: 5, Tx: 2}, ClampStart: false, - End: Cursor{Ledger: 8, Tx: 1, Op: 4}, + End: Cursor{Ledger: 8, Tx: 2}, ClampEnd: false, }, concat(ledger5Events[2:], ledger6Events, ledger7Events, ledger8Events[:1]), @@ -367,11 +364,12 @@ func TestScan(t *testing.T) { iterateAll := true f := func(contractEvent xdr.DiagnosticEvent, cursor Cursor, ledgerCloseTimestamp int64) bool { require.Equal(t, ledgerCloseTime(cursor.Ledger), ledgerCloseTimestamp) + diagnosticEventXDR, err := contractEvent.MarshalBinary() + require.NoError(t, err) events = append(events, event{ - contents: contractEvent, - txIndex: cursor.Tx, - opIndex: cursor.Op, - eventIndex: cursor.Event, + diagnosticEventXDR: diagnosticEventXDR, + txIndex: cursor.Tx, + eventIndex: cursor.Event, }) return iterateAll }