diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 08641b9ad..c008cc7b4 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -27,17 +27,15 @@ jobs: crate: - name: stellar-cli binary: stellar - - name: soroban-cli - binary: soroban sys: - os: ubuntu-20.04 # Use 20.04 to get an older version of glibc for increased compat target: x86_64-unknown-linux-gnu - os: ubuntu-20.04 # Use 20.04 to get an older version of glibc for increased compat target: aarch64-unknown-linux-gnu - os: macos-14 - target: x86_64-apple-darwin - - os: macos-12 target: aarch64-apple-darwin + - os: macos-14 + target: x86_64-apple-darwin - os: windows-latest target: x86_64-pc-windows-msvc ext: .exe @@ -46,41 +44,50 @@ jobs: - uses: actions/checkout@v4 - run: rustup update - run: rustup target add ${{ matrix.sys.target }} + - if: matrix.sys.target == 'aarch64-unknown-linux-gnu' run: sudo apt-get update && sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libudev-dev + - name: Setup vars run: | version="$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "stellar-cli") | .version')" echo "VERSION=${version}" >> $GITHUB_ENV echo "NAME=${{ matrix.crate.name }}-${version}-${{ matrix.sys.target }}" >> $GITHUB_ENV + - name: Package (release only) if: github.event_name == 'release' run: cargo package --no-verify --package ${{ matrix.crate.name }} + - name: Package Extract (release only) if: github.event_name == 'release' run: | cd target/package tar xvfz ${{ matrix.crate.name }}-$VERSION.crate echo "BUILD_WORKING_DIR=target/package/${{ matrix.crate.name }}-$VERSION" >> $GITHUB_ENV + - name: Build env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc working-directory: ${{ env.BUILD_WORKING_DIR }} run: cargo build --target-dir="$GITHUB_WORKSPACE/target" --package ${{ matrix.crate.name }} --features opt --release --target ${{ matrix.sys.target }} + - name: Build provenance for attestation (release only) if: github.event_name == 'release' uses: actions/attest-build-provenance@v1 with: subject-path: target/${{ matrix.sys.target }}/release/${{ matrix.crate.binary }}${{ matrix.sys.ext }} + - name: Compress run: | cd target/${{ matrix.sys.target }}/release tar czvf $NAME.tar.gz ${{ matrix.crate.binary }}${{ matrix.sys.ext }} + - name: Upload to Artifacts uses: actions/upload-artifact@v4 with: - name: ${{ env.NAME }} + name: ${{ env.NAME }}.tar.gz path: 'target/${{ matrix.sys.target }}/release/${{ env.NAME }}.tar.gz' + - name: Upload to Release (release only) if: github.event_name == 'release' uses: actions/github-script@v7 @@ -94,3 +101,61 @@ jobs: name: '${{ env.NAME }}.tar.gz', data: fs.readFileSync('target/${{ matrix.sys.target }}/release/${{ env.NAME }}.tar.gz'), }); + + installer: + needs: build + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup vars + run: | + version="$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "stellar-cli") | .version')" + installer_basename="stellar-cli-installer-${version}-x86_64-pc-windows-msvc" + echo "VERSION=${version}" >> $GITHUB_ENV + echo "STELLAR_CLI_INSTALLER_BASENAME=${installer_basename}" >> $GITHUB_ENV + echo "STELLAR_CLI_INSTALLER=${installer_basename}.exe" >> $GITHUB_ENV + echo "ARTIFACT_NAME=stellar-cli-${version}-x86_64-pc-windows-msvc.tar.gz" >> $GITHUB_ENV + + - name: Download Artifact + uses: actions/download-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }} + + - name: Uncompress Artifact + run: tar xvf ${{ env.ARTIFACT_NAME }} + + - name: Build Installer + shell: powershell + run: | + $Env:Path += ";C:\Users\$Env:UserName\AppData\Local\Programs\Inno Setup 6" + $Env:STELLAR_CLI_VERSION = "${{ env.VERSION }}" + ISCC.exe installer.iss + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.STELLAR_CLI_INSTALLER }} + path: Output/stellar-installer.exe + + - name: Build provenance for attestation (release only) + if: github.event_name == 'release' + uses: actions/attest-build-provenance@v1 + with: + subject-path: ${{ env.STELLAR_CLI_INSTALLER }} + + - name: Upload to Release (release only) + if: github.event_name == 'release' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + await github.rest.repos.uploadReleaseAsset({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: ${{ github.event.release.id }}, + name: '${{ env.STELLAR_CLI_INSTALLER }}', + data: fs.readFileSync('Output/stellar-installer.exe'), + }); + diff --git a/.github/workflows/rpc-tests.yml b/.github/workflows/rpc-tests.yml index 769cd21f7..75b6d7760 100644 --- a/.github/workflows/rpc-tests.yml +++ b/.github/workflows/rpc-tests.yml @@ -1,4 +1,3 @@ - name: RPC Tests on: push: @@ -15,32 +14,32 @@ jobs: runs-on: ubuntu-22.04 services: rpc: - image: stellar/quickstart:v438-testing + image: stellar/quickstart:testing ports: - 8000:8000 env: ENABLE_LOGS: true ENABLE_SOROBAN_DIAGNOSTIC_EVENTS: true NETWORK: local + PROTOCOL_VERSION: 22 options: >- --health-cmd "curl --no-progress-meter --fail-with-body -X POST \"http://localhost:8000/soroban/rpc\" -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":8675309,\"method\":\"getNetwork\"}' && curl --no-progress-meter \"http://localhost:8000/friendbot\" | grep '\"invalid_field\": \"addr\"'" --health-interval 10s --health-timeout 5s --health-retries 50 steps: - - uses: actions/checkout@v4 - - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - run: rustup update - - run: cargo build - - run: rustup target add wasm32-unknown-unknown - - run: make build-test-wasms - - run: SOROBAN_PORT=8000 cargo test --features it --package soroban-test --test it -- integration - + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - run: rustup update + - run: cargo build + - run: rustup target add wasm32-unknown-unknown + - run: make build-test-wasms + - run: SOROBAN_PORT=8000 cargo test --features it --package soroban-test --test it -- integration diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 048015009..595e6e817 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -17,7 +17,7 @@ jobs: complete: if: always() - needs: [fmt, check-generated-full-help-docs, build-and-test, publish-dry-run] + needs: [fmt, cargo-deny, check-generated-full-help-docs, build-and-test, publish-dry-run] runs-on: ubuntu-latest steps: - if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') @@ -30,6 +30,19 @@ jobs: - run: rustup update - run: cargo fmt --all --check + cargo-deny: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + check: [advisories, bans, licenses, sources] + continue-on-error: ${{ matrix.check == 'advisories' || matrix.check == 'bans' || matrix.check == 'licenses' }} + steps: + - uses: actions/checkout@v3 + - uses: EmbarkStudios/cargo-deny-action@b01e7a8cfb1f496c52d77361e84c1840d8246393 + with: + command: check ${{ matrix.check }} + check-generated-full-help-docs: runs-on: ubuntu-latest-16-cores steps: @@ -44,10 +57,10 @@ jobs: fail-fast: false matrix: rust: [msrv, latest] - include: + sys: - os: ubuntu-latest-16-cores target: x86_64-unknown-linux-gnu - - os: ubuntu-latest-16-cores + - os: ubuntu-jammy-16-cores-arm64 target: aarch64-unknown-linux-gnu - os: macos-latest target: x86_64-apple-darwin @@ -55,7 +68,7 @@ jobs: target: aarch64-apple-darwin - os: windows-latest-8-cores target: x86_64-pc-windows-msvc - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.sys.os }} env: CI_TESTS: true steps: @@ -74,18 +87,15 @@ jobs: run: echo RUSTFLAGS='-Dwarnings -Dclippy::all -Dclippy::pedantic' >> $GITHUB_ENV - run: rustup update - run: cargo version - - run: rustup target add ${{ matrix.target }} + - run: rustup target add ${{ matrix.sys.target }} - run: rustup target add wasm32-unknown-unknown - - if: matrix.target == 'aarch64-unknown-linux-gnu' - run: sudo apt-get update && sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu - - run: cargo clippy --all-targets --target ${{ matrix.target }} - - run: make build-test - - if: startsWith(matrix.target, 'x86_64') - # specify directories explicitly (otherwise it will fail with missing symbols) - run: | - for I in cmd/soroban-cli cmd/crates/* cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world ; do - cargo test --target ${{ matrix.target }} --manifest-path $I/Cargo.toml - done + - if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libudev-dev + - run: cargo clippy --all-targets --target ${{ matrix.sys.target }} + - run: make test + env: + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc + CARGO_BUILD_TARGET: ${{ matrix.sys.target }} publish-dry-run: if: github.event_name == 'push' || startsWith(github.head_ref, 'release/') @@ -97,10 +107,11 @@ jobs: target: x86_64-unknown-linux-gnu cargo-hack-feature-options: --feature-powerset additional-deb-packages: libudev-dev - # - os: ubuntu-latest-16-cores - # target: aarch64-unknown-linux-gnu - # cargo-hack-feature-options: --feature-powerset - # additional-deb-packages: libudev-dev libssl-dev + # TODO: add back ARM support + #- os: ubuntu-latest-16-cores + # target: aarch64-unknown-linux-gnu + # cargo-hack-feature-options: --feature-powerset + # additional-deb-packages: libudev-dev libssl-dev - os: macos-latest target: x86_64-apple-darwin cargo-hack-feature-options: --feature-powerset diff --git a/.gitignore b/.gitignore index 7723bb84b..b7150ae5d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ test_snapshots .vscode/settings.json .idea local.sh +.stellar diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 21b38392b..7c54767c6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ changes quickly. ## Setting up development environment -There are 2 ways to being developing stellar-cli: +There are 2 ways to begin developing stellar-cli: ### Installing all required dependencies diff --git a/Cargo.lock b/Cargo.lock index 55ebf62a2..89cb82cdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -241,12 +241,6 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - [[package]] name = "ascii-canvas" version = "3.0.0" @@ -276,7 +270,7 @@ dependencies = [ "bstr", "doc-comment", "libc", - "predicates 3.1.2", + "predicates", "predicates-core", "predicates-tree", "wait-timeout", @@ -291,7 +285,7 @@ dependencies = [ "anstyle", "doc-comment", "globwalk", - "predicates 3.1.2", + "predicates", "predicates-core", "predicates-tree", "tempfile", @@ -727,6 +721,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "byteorder" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" + [[package]] name = "byteorder" version = "1.5.0" @@ -848,7 +848,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", ] [[package]] @@ -944,17 +944,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crate-git-revision" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f998aef136a4e7833b0e4f0fc0939a59c40140b28e0ffbf524ad84fb2cc568c8" -dependencies = [ - "serde", - "serde_derive", - "serde_json", -] - [[package]] name = "crate-git-revision" version = "0.0.6" @@ -1022,7 +1011,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "subtle", "zeroize", ] @@ -1078,19 +1067,6 @@ dependencies = [ "syn 2.0.77", ] -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -1182,7 +1158,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.11.1", + "strsim", "syn 2.0.77", ] @@ -1284,16 +1260,7 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" dependencies = [ - "dirs-sys 0.4.1", -] - -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys 0.3.7", + "dirs-sys", ] [[package]] @@ -1302,7 +1269,7 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys 0.4.1", + "dirs-sys", ] [[package]] @@ -1315,17 +1282,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "dirs-sys" version = "0.4.1" @@ -1394,16 +1350,7 @@ dependencies = [ "digest 0.10.7", "elliptic-curve", "rfc6979", - "signature 2.1.0", -] - -[[package]] -name = "ed25519" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" -dependencies = [ - "signature 1.6.4", + "signature", ] [[package]] @@ -1413,19 +1360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8", - "signature 2.1.0", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" -dependencies = [ - "curve25519-dalek 3.2.0", - "ed25519 1.5.3", - "sha2 0.9.9", - "zeroize", + "signature", ] [[package]] @@ -1434,9 +1369,9 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ - "curve25519-dalek 4.1.3", - "ed25519 2.2.3", - "rand_core 0.6.4", + "curve25519-dalek", + "ed25519", + "rand_core", "serde", "sha2 0.10.8", "subtle", @@ -1461,7 +1396,7 @@ dependencies = [ "ff", "generic-array", "group", - "rand_core 0.6.4", + "rand_core", "sec1", "subtle", "zeroize", @@ -1607,7 +1542,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -1880,7 +1815,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -1942,9 +1877,6 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] [[package]] name = "heck" @@ -2308,15 +2240,18 @@ dependencies = [ [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", - "hyper 0.14.30", + "http-body-util", + "hyper 1.4.1", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", ] [[package]] @@ -2692,6 +2627,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + [[package]] name = "ledger-apdu" version = "0.10.0" @@ -2719,7 +2660,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45ba81a1f5f24396b37211478aff7fbcd605dd4544df8dbed07b9da3c2057aee" dependencies = [ - "byteorder", + "byteorder 1.5.0", "cfg-if", "hex", "hidapi", @@ -2884,12 +2825,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "multi-stash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685a9ac4b61f4e728e1d2c6a7844609c16527aeb5e6c865915c08e619c16410f" - [[package]] name = "native-tls" version = "0.2.12" @@ -3341,20 +3276,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -[[package]] -name = "predicates" -version = "2.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" -dependencies = [ - "difflib", - "float-cmp", - "itertools 0.10.5", - "normalize-line-endings", - "predicates-core", - "regex", -] - [[package]] name = "predicates" version = "3.1.2" @@ -3363,7 +3284,10 @@ checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" dependencies = [ "anstyle", "difflib", + "float-cmp", + "normalize-line-endings", "predicates-core", + "regex", ] [[package]] @@ -3491,7 +3415,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -3501,15 +3425,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" - [[package]] name = "rand_core" version = "0.6.4" @@ -3583,46 +3501,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - [[package]] name = "reqwest" version = "0.12.7" @@ -3642,11 +3520,13 @@ dependencies = [ "http-body-util", "hyper 1.4.1", "hyper-rustls 0.27.3", + "hyper-tls", "hyper-util", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -3658,8 +3538,10 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper", + "system-configuration", "tokio", + "tokio-native-tls", "tokio-rustls 0.26.0", "tokio-util", "tower-service", @@ -3782,9 +3664,9 @@ checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] @@ -4281,6 +4163,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-escape" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" + [[package]] name = "shlex" version = "1.3.0" @@ -4296,12 +4184,6 @@ dependencies = [ "libc", ] -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" - [[package]] name = "signature" version = "2.1.0" @@ -4309,7 +4191,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" dependencies = [ "digest 0.10.7", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -4333,24 +4215,13 @@ dependencies = [ "autocfg", ] -[[package]] -name = "slip10" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28724a6e6f70b0cb115c580891483da6f3aa99e6a353598303a57f89d23aa6bc" -dependencies = [ - "ed25519-dalek 1.0.1", - "hmac 0.9.0", - "sha2 0.9.9", -] - [[package]] name = "slipped10" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a45443e66aa5d96db5e02d17db056e1ca970232a4fe73e1f9bc1816d68f4e98" dependencies = [ - "ed25519-dalek 2.1.1", + "ed25519-dalek", "hmac 0.9.0", "sha2 0.9.9", ] @@ -4405,21 +4276,9 @@ dependencies = [ [[package]] name = "soroban-builtin-sdk-macros" -version = "21.2.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f57a68ef8777e28e274de0f3a88ad9a5a41d9a2eb461b4dd800b086f0e83b80" -dependencies = [ - "itertools 0.11.0", - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "soroban-builtin-sdk-macros" -version = "22.0.0-rc.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4c8668199d95e3061cd42e1b96a91451c656a238a607fa53f96f0a3fdcf5f3" +checksum = "c45d2492cd44f05cc79eeb857985f153f12a4423ce51b4b746b5925024c473b1" dependencies = [ "itertools 0.10.5", "proc-macro2", @@ -4429,7 +4288,7 @@ dependencies = [ [[package]] name = "soroban-cli" -version = "21.5.0" +version = "22.0.0" dependencies = [ "assert_cmd", "assert_fs", @@ -4443,12 +4302,11 @@ dependencies = [ "clap", "clap-markdown", "clap_complete", - "crate-git-revision 0.0.4", + "crate-git-revision", "csv", "directories", - "dirs 4.0.0", "dotenvy", - "ed25519-dalek 2.1.1", + "ed25519-dalek", "ethnum", "flate2", "fqdn", @@ -4467,10 +4325,10 @@ dependencies = [ "open", "pathdiff", "phf", - "predicates 2.1.5", + "predicates", "rand", "regex", - "reqwest 0.12.7", + "reqwest", "rpassword", "rust-embed", "semver", @@ -4479,18 +4337,19 @@ dependencies = [ "serde-aux", "serde_json", "sha2 0.10.8", + "shell-escape", "shlex", - "soroban-ledger-snapshot 22.0.0-rc.1.1", - "soroban-sdk 22.0.0-rc.1.1", - "soroban-spec 22.0.0-rc.1.1", + "soroban-ledger-snapshot", + "soroban-sdk", + "soroban-spec", "soroban-spec-json", - "soroban-spec-rust 22.0.0-rc.1.1", + "soroban-spec-rust", "soroban-spec-tools", "soroban-spec-typescript", "stellar-rpc-client", "stellar-strkey 0.0.11", - "stellar-xdr 22.0.0-rc.1.1", - "strsim 0.10.0", + "stellar-xdr", + "strsim", "strum 0.17.1", "strum_macros 0.17.1", "tempfile", @@ -4499,122 +4358,62 @@ dependencies = [ "thiserror", "tokio", "tokio-util", - "toml 0.5.11", - "toml_edit 0.21.1", + "toml", + "toml_edit", "tracing", "tracing-appender", "tracing-subscriber", "ulid", "url", "walkdir", + "wasm-gen", "wasm-opt", - "wasmparser 0.90.0", + "wasmparser", "which", ] [[package]] name = "soroban-env-common" -version = "21.2.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1c89463835fe6da996318156d39f424b4f167c725ec692e5a7a2d4e694b3d" +checksum = "39b6d2ec8955243394278e1fae88be3b367fcfed9cf74e5044799a90786a8642" dependencies = [ "arbitrary", - "crate-git-revision 0.0.6", + "crate-git-revision", "ethnum", "num-derive", "num-traits", "serde", - "soroban-env-macros 21.2.1", - "soroban-wasmi 0.31.1-soroban.20.0.1", - "static_assertions", - "stellar-xdr 21.2.0", - "wasmparser 0.116.1", -] - -[[package]] -name = "soroban-env-common" -version = "22.0.0-rc.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdf1d66133d6b29e2834acea79decb57c47c71aa01885cae2b9ad621d67525c" -dependencies = [ - "crate-git-revision 0.0.6", - "ethnum", - "num-derive", - "num-traits", - "serde", - "soroban-env-macros 22.0.0-rc.1.1", - "soroban-wasmi 0.36.1-soroban.22.0.0", - "static_assertions", - "stellar-xdr 22.0.0-rc.1.1", - "wasmparser 0.116.1", -] - -[[package]] -name = "soroban-env-guest" -version = "21.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bfb2536811045d5cd0c656a324cbe9ce4467eb734c7946b74410d90dea5d0ce" -dependencies = [ - "soroban-env-common 21.2.1", + "soroban-env-macros", + "soroban-wasmi", "static_assertions", + "stellar-xdr", + "wasmparser", ] [[package]] name = "soroban-env-guest" -version = "22.0.0-rc.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9610ac8a4a900e6f35b2ed171bc325c7e9883929f5e9da758e85f1226dd284" -dependencies = [ - "soroban-env-common 22.0.0-rc.1.1", - "static_assertions", -] - -[[package]] -name = "soroban-env-host" -version = "21.2.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b7a32c28f281c423189f1298960194f0e0fc4eeb72378028171e556d8cd6160" +checksum = "4002fc582cd20cc9b9fbb73959bc5d6b5b15feda11485cbfab0c28e78ecbab3e" dependencies = [ - "backtrace", - "curve25519-dalek 4.1.3", - "ecdsa", - "ed25519-dalek 2.1.1", - "elliptic-curve", - "generic-array", - "getrandom", - "hex-literal", - "hmac 0.12.1", - "k256", - "num-derive", - "num-integer", - "num-traits", - "p256", - "rand", - "rand_chacha", - "sec1", - "sha2 0.10.8", - "sha3", - "soroban-builtin-sdk-macros 21.2.1", - "soroban-env-common 21.2.1", - "soroban-wasmi 0.31.1-soroban.20.0.1", + "soroban-env-common", "static_assertions", - "stellar-strkey 0.0.8", - "wasmparser 0.116.1", ] [[package]] name = "soroban-env-host" -version = "22.0.0-rc.1.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c695d22888ede1f98c016a4a690be307817d133be0e0f32a25fd6e53bb6c929" +checksum = "8cb9be0260d39a648db0d33e1c6f8f494ec0c4f5be2b8a0a4e15ed4b7c6a92b0" dependencies = [ "ark-bls12-381", "ark-ec", "ark-ff", "ark-serialize", - "curve25519-dalek 4.1.3", + "curve25519-dalek", "ecdsa", - "ed25519-dalek 2.1.1", + "ed25519-dalek", "elliptic-curve", "generic-array", "getrandom", @@ -4630,244 +4429,151 @@ dependencies = [ "sec1", "sha2 0.10.8", "sha3", - "soroban-builtin-sdk-macros 22.0.0-rc.1.1", - "soroban-env-common 22.0.0-rc.1.1", - "soroban-wasmi 0.36.1-soroban.22.0.0", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", "static_assertions", "stellar-strkey 0.0.9", - "wasmparser 0.116.1", -] - -[[package]] -name = "soroban-env-macros" -version = "21.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "242926fe5e0d922f12d3796cd7cd02dd824e5ef1caa088f45fce20b618309f64" -dependencies = [ - "itertools 0.11.0", - "proc-macro2", - "quote", - "serde", - "serde_json", - "stellar-xdr 21.2.0", - "syn 2.0.77", + "wasmparser", ] [[package]] name = "soroban-env-macros" -version = "22.0.0-rc.1.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d926d0daa3ba798cd70ce962ea10012e630d75088352369a6d248b2644dd7a2c" +checksum = "a328297a568ae98999fdb06902e3362dfd8a2bfa9abea40beaeb7dc93a402fe7" dependencies = [ "itertools 0.10.5", "proc-macro2", "quote", "serde", "serde_json", - "stellar-xdr 22.0.0-rc.1.1", + "stellar-xdr", "syn 2.0.77", ] [[package]] name = "soroban-hello" -version = "21.5.0" +version = "0.0.0" [[package]] name = "soroban-ledger-snapshot" -version = "21.7.3" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84589856911dfd6731695c9b51c858aed6d4540118c0a1e5c4c858ea13bc744c" +checksum = "56375490f176006a636db0e50c2269c55626e0ff7222711bb78d77028376fe0d" dependencies = [ "serde", "serde_json", "serde_with", - "soroban-env-common 21.2.1", - "soroban-env-host 21.2.1", - "thiserror", -] - -[[package]] -name = "soroban-ledger-snapshot" -version = "22.0.0-rc.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07ca63290730b803f0637e00994f803d2f24cc6383ac8680724050d07690a89" -dependencies = [ - "serde", - "serde_json", - "serde_with", - "soroban-env-common 22.0.0-rc.1.1", - "soroban-env-host 22.0.0-rc.1.1", + "soroban-env-common", + "soroban-env-host", "thiserror", ] [[package]] name = "soroban-sdk" -version = "21.7.2" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e39bf9e8ab05579c836e8e5be5f2f4c5ba75e7337ece20e975e82fc3a9d41e" +checksum = "6d063d0df000aaec20105aab3d743660322bc0269934ea95d79fa19aa8792385" dependencies = [ "arbitrary", "bytes-lit", "ctor", - "ed25519-dalek 2.1.1", - "rand", - "serde", - "serde_json", - "soroban-env-guest 21.2.1", - "soroban-env-host 21.2.1", - "soroban-ledger-snapshot 21.7.3", - "soroban-sdk-macros 21.7.3", - "stellar-strkey 0.0.8", -] - -[[package]] -name = "soroban-sdk" -version = "22.0.0-rc.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b715e15357dbdda2fe0626005b75c2253bb3786deec3001d4077462a9dadd3" -dependencies = [ - "bytes-lit", + "ed25519-dalek", "rand", + "rustc_version", "serde", "serde_json", - "soroban-env-guest 22.0.0-rc.1.1", - "soroban-env-host 22.0.0-rc.1.1", - "soroban-ledger-snapshot 22.0.0-rc.1.1", - "soroban-sdk-macros 22.0.0-rc.1.1", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", "stellar-strkey 0.0.9", ] [[package]] name = "soroban-sdk-macros" -version = "21.7.3" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c2173f1aacd56b4405eed71cb2a9694dff99d51ba72d4f0cbc5e4961fdabf4" +checksum = "508c9d819a05109120664aab86c371e1b72c5bea20b1a13158b4ef7948d9f673" dependencies = [ - "crate-git-revision 0.0.6", - "darling", - "itertools 0.11.0", - "proc-macro2", - "quote", - "rustc_version", - "sha2 0.10.8", - "soroban-env-common 21.2.1", - "soroban-spec 21.7.3", - "soroban-spec-rust 21.7.3", - "stellar-xdr 21.2.0", - "syn 2.0.77", -] - -[[package]] -name = "soroban-sdk-macros" -version = "22.0.0-rc.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00d1b8d96ccf53ea9c30d86352756cace630ca5c7e1200fff32ffc793bedd593" -dependencies = [ - "crate-git-revision 0.0.6", + "crate-git-revision", "darling", "itertools 0.10.5", "proc-macro2", "quote", "rustc_version", "sha2 0.10.8", - "soroban-env-common 22.0.0-rc.1.1", - "soroban-spec 22.0.0-rc.1.1", - "soroban-spec-rust 22.0.0-rc.1.1", - "stellar-xdr 22.0.0-rc.1.1", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", "syn 2.0.77", ] [[package]] name = "soroban-spec" -version = "21.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7705bffbcc747c08e81698b87b4a787f8b268c25d88f777160091dc1ee8121cb" -dependencies = [ - "base64 0.13.1", - "stellar-xdr 21.2.0", - "thiserror", - "wasmparser 0.116.1", -] - -[[package]] -name = "soroban-spec" -version = "22.0.0-rc.1.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4603430fd36848da7189e758d7f3fe1d7bbfef024e8aad2230a8be8252a583c1" +checksum = "69001c97783ed3ce197eac2404e7beeabedd16e40e6f0aa210d1bc6a13063c33" dependencies = [ "base64 0.13.1", - "stellar-xdr 22.0.0-rc.1.1", + "stellar-xdr", "thiserror", - "wasmparser 0.116.1", + "wasmparser", ] [[package]] name = "soroban-spec-json" -version = "21.5.0" +version = "22.0.0" dependencies = [ "pretty_assertions", "serde", "serde_derive", "serde_json", "sha2 0.9.9", - "soroban-spec 22.0.0-rc.1.1", - "stellar-xdr 22.0.0-rc.1.1", + "soroban-spec", + "stellar-xdr", "thiserror", ] [[package]] name = "soroban-spec-rust" -version = "21.7.3" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48207ebc8616c2804a17203d1d86c53c3d3c804b682cbab011a135893db1cf78" +checksum = "a45dbf346f91ed23ea63b1c256c522da9e6f0e2db1887b990a8f0f1d842a3093" dependencies = [ "prettyplease", "proc-macro2", "quote", "sha2 0.10.8", - "soroban-spec 21.7.3", - "stellar-xdr 21.2.0", - "syn 2.0.77", - "thiserror", -] - -[[package]] -name = "soroban-spec-rust" -version = "22.0.0-rc.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d5f3fe5b27e0b1c1d9ecdff04c4e31f5cd4d12cdc052d54f0e4995cf637b9c" -dependencies = [ - "prettyplease", - "proc-macro2", - "quote", - "sha2 0.10.8", - "soroban-spec 22.0.0-rc.1.1", - "stellar-xdr 22.0.0-rc.1.1", + "soroban-spec", + "stellar-xdr", "syn 2.0.77", "thiserror", ] [[package]] name = "soroban-spec-tools" -version = "21.5.0" +version = "22.0.0" dependencies = [ "base64 0.21.7", "ethnum", "hex", "itertools 0.10.5", "serde_json", - "soroban-spec 22.0.0-rc.1.1", + "soroban-spec", "stellar-strkey 0.0.11", - "stellar-xdr 22.0.0-rc.1.1", + "stellar-xdr", "thiserror", "tokio", - "wasmparser 0.90.0", + "wasmparser", "which", ] [[package]] name = "soroban-spec-typescript" -version = "21.5.0" +version = "22.0.0" dependencies = [ "base64 0.21.7", "heck 0.4.1", @@ -4879,8 +4585,8 @@ dependencies = [ "serde_derive", "serde_json", "sha2 0.9.9", - "soroban-spec 22.0.0-rc.1.1", - "stellar-xdr 22.0.0-rc.1.1", + "soroban-spec", + "stellar-xdr", "temp-dir", "thiserror", "walkdir", @@ -4888,26 +4594,26 @@ dependencies = [ [[package]] name = "soroban-test" -version = "21.5.0" +version = "22.0.0" dependencies = [ "assert_cmd", "assert_fs", - "ed25519-dalek 2.1.1", + "ed25519-dalek", "fs_extra", "hex", - "predicates 2.1.5", + "predicates", "sep5", "serde_json", "sha2 0.10.8", "soroban-cli", - "soroban-ledger-snapshot 22.0.0-rc.1.1", - "soroban-spec 22.0.0-rc.1.1", + "soroban-ledger-snapshot", + "soroban-spec", "soroban-spec-tools", "stellar-rpc-client", "stellar-strkey 0.0.11", "thiserror", "tokio", - "toml 0.8.19", + "toml", "ulid", "walkdir", "which", @@ -4915,11 +4621,11 @@ dependencies = [ [[package]] name = "soroban-token-sdk" -version = "21.7.2" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6ede0da76646037f3ea5db9ccd37830865444bb24f137cb8f0af8282c784f5" +checksum = "17bb933a3dcf41d234f6d669b077eb755663773630c6899a1c8a30dddf950f52" dependencies = [ - "soroban-sdk 21.7.2", + "soroban-sdk", ] [[package]] @@ -4931,24 +4637,7 @@ dependencies = [ "smallvec", "spin", "wasmi_arena", - "wasmi_core 0.13.0", - "wasmparser-nostd", -] - -[[package]] -name = "soroban-wasmi" -version = "0.36.1-soroban.22.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7044ea0ee6ff67039df1f232f0d3d98121f69a0409e944774912fc5f043c280f" -dependencies = [ - "arrayvec", - "multi-stash", - "num-derive", - "num-traits", - "smallvec", - "spin", - "wasmi_collections", - "wasmi_core 0.36.3", + "wasmi_core", "wasmparser-nostd", ] @@ -4974,21 +4663,25 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stellar-bye" +version = "0.0.0" + [[package]] name = "stellar-cli" -version = "21.5.0" +version = "22.0.0" dependencies = [ "soroban-cli", ] [[package]] name = "stellar-ledger" -version = "21.5.0" +version = "22.0.0" dependencies = [ "async-trait", "bollard", - "byteorder", - "ed25519-dalek 2.1.1", + "byteorder 1.5.0", + "ed25519-dalek", "env_logger", "futures", "hex", @@ -5000,18 +4693,18 @@ dependencies = [ "once_cell", "phf", "pretty_assertions", - "reqwest 0.11.27", + "reqwest", "sep5", "serde", "serde_derive", "serde_json", "serial_test", - "sha2 0.9.9", - "slip10", - "soroban-spec 22.0.0-rc.1.1", + "sha2 0.10.8", + "slipped10", + "soroban-spec", "stellar-rpc-client", "stellar-strkey 0.0.11", - "stellar-xdr 22.0.0-rc.1.1", + "stellar-xdr", "test-case", "testcontainers", "thiserror", @@ -5021,8 +4714,9 @@ dependencies = [ [[package]] name = "stellar-rpc-client" -version = "21.4.0" -source = "git+https://github.com/stellar/rs-stellar-rpc-client?branch=main#7554d4c87c026313a1f5b3c7ae66a92b5ff7e091" +version = "22.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaba3219c517ceba11f99e1f9adb1e801fe8416bc9ff35c6842b5fe8cac19290" dependencies = [ "clap", "hex", @@ -5036,7 +4730,7 @@ dependencies = [ "serde_with", "sha2 0.10.8", "stellar-strkey 0.0.9", - "stellar-xdr 22.0.0-rc.1.1", + "stellar-xdr", "termcolor", "termcolor_output", "thiserror", @@ -5051,7 +4745,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12d2bf45e114117ea91d820a846fd1afbe3ba7d717988fee094ce8227a3bf8bd" dependencies = [ "base32", - "crate-git-revision 0.0.6", + "crate-git-revision", "thiserror", ] @@ -5061,7 +4755,7 @@ version = "0.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e3aa3ed00e70082cb43febc1c2afa5056b9bb3e348bbb43d0cd0aa88a611144" dependencies = [ - "crate-git-revision 0.0.6", + "crate-git-revision", "data-encoding", "thiserror", ] @@ -5072,35 +4766,20 @@ version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0989c9a05eccbd08b60e603a1c7e3ed3ec92c0de73b8681fc964d272ab2b2697" dependencies = [ - "crate-git-revision 0.0.6", + "crate-git-revision", "data-encoding", ] -[[package]] -name = "stellar-xdr" -version = "21.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2675a71212ed39a806e415b0dbf4702879ff288ec7f5ee996dda42a135512b50" -dependencies = [ - "arbitrary", - "base64 0.13.1", - "crate-git-revision 0.0.6", - "escape-bytes", - "hex", - "serde", - "serde_with", - "stellar-strkey 0.0.8", -] - [[package]] name = "stellar-xdr" version = "22.0.0-rc.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c88dc0e928b9cb65ea43836b52560bb4ead3e32895f5019ca223dc7cd1966cbf" dependencies = [ + "arbitrary", "base64 0.13.1", "clap", - "crate-git-revision 0.0.6", + "crate-git-revision", "escape-bytes", "hex", "schemars", @@ -5111,17 +4790,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "string-interner" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6a0d765f5807e98a091107bae0a56ea3799f66a5de47b2c84c94a39c09974e" -dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "serde", -] - [[package]] name = "string_cache" version = "0.8.7" @@ -5135,12 +4803,6 @@ dependencies = [ "precomputed-hash", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -5235,12 +4897,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "sync_wrapper" version = "1.0.1" @@ -5252,20 +4908,20 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -5365,47 +5021,54 @@ dependencies = [ "test-case-core", ] +[[package]] +name = "test_constructor" +version = "0.0.0" +dependencies = [ + "soroban-sdk", +] + [[package]] name = "test_custom_account" -version = "21.5.0" +version = "0.0.0" dependencies = [ - "soroban-sdk 21.7.2", + "soroban-sdk", ] [[package]] name = "test_custom_types" -version = "21.5.0" +version = "0.0.0" dependencies = [ - "soroban-sdk 21.7.2", + "soroban-sdk", ] [[package]] name = "test_hello_world" -version = "21.5.0" +version = "0.0.0" dependencies = [ - "soroban-sdk 21.7.2", + "soroban-sdk", ] [[package]] name = "test_swap" -version = "21.5.0" +version = "0.0.0" dependencies = [ - "soroban-sdk 21.7.2", + "soroban-sdk", ] [[package]] name = "test_token" -version = "21.5.0" +version = "0.0.0" dependencies = [ - "soroban-sdk 21.7.2", + "soroban-sdk", "soroban-token-sdk", ] [[package]] name = "test_udt" -version = "21.5.0" +version = "22.0.0" dependencies = [ - "soroban-sdk 21.7.2", + "soroban-sdk", ] [[package]] @@ -5418,7 +5081,7 @@ dependencies = [ "bollard", "bollard-stubs", "bytes", - "dirs 5.0.1", + "dirs", "docker_credential", "either", "futures", @@ -5426,7 +5089,7 @@ dependencies = [ "memchr", "parse-display", "pin-project-lite", - "reqwest 0.12.7", + "reqwest", "serde", "serde_json", "serde_with", @@ -5637,15 +5300,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - [[package]] name = "toml" version = "0.8.19" @@ -5655,7 +5309,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.20", + "toml_edit", ] [[package]] @@ -5667,17 +5321,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" -dependencies = [ - "indexmap 2.3.0", - "toml_datetime", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.22.20" @@ -5688,7 +5331,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.18", + "winnow", ] [[package]] @@ -6010,11 +5653,21 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-gen" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b854b1461005a7b3365742310f7faa3cac3add809d66928c64a40c7e9e842ebb" +dependencies = [ + "byteorder 0.5.3", + "leb128", +] + [[package]] name = "wasm-opt" -version = "0.114.2" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effbef3bd1dde18acb401f73e740a6f3d4a1bc651e9773bddc512fe4d8d68f67" +checksum = "2fd87a4c135535ffed86123b6fb0f0a5a0bc89e50416c942c5f0662c645f679c" dependencies = [ "anyhow", "libc", @@ -6028,9 +5681,9 @@ dependencies = [ [[package]] name = "wasm-opt-cxx-sys" -version = "0.114.2" +version = "0.116.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c09e24eb283919ace2ed5733bda4842a59ce4c8de110ef5c6d98859513d17047" +checksum = "8c57b28207aa724318fcec6575fe74803c23f6f266fce10cbc9f3f116762f12e" dependencies = [ "anyhow", "cxx", @@ -6040,9 +5693,9 @@ dependencies = [ [[package]] name = "wasm-opt-sys" -version = "0.114.2" +version = "0.116.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f2f817bed2e8d65eb779fa37317e74de15585751f903c9118342d1970703a4" +checksum = "8a1cce564dc768dacbdb718fc29df2dba80bd21cb47d8f77ae7e3d95ceb98cbe" dependencies = [ "anyhow", "cc", @@ -6069,17 +5722,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" -[[package]] -name = "wasmi_collections" -version = "0.36.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ee1cf2328e7fbb8654fda8449395c64c36ef5a30511e79fae0265a96e1a446" -dependencies = [ - "ahash", - "hashbrown 0.14.5", - "string-interner", -] - [[package]] name = "wasmi_core" version = "0.13.0" @@ -6092,27 +5734,6 @@ dependencies = [ "paste", ] -[[package]] -name = "wasmi_core" -version = "0.36.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e77c1e701d21edfd263e5c6c940861202c6b840c715040cfdca6211bf8857aa" -dependencies = [ - "downcast-rs", - "libm", - "num-traits", - "paste", -] - -[[package]] -name = "wasmparser" -version = "0.90.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62c8d843f4423efee314dc75a1049886deba3214f7e7f9ff0e4e58b4d618581" -dependencies = [ - "indexmap 1.9.3", -] - [[package]] name = "wasmparser" version = "0.116.1" @@ -6398,15 +6019,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - [[package]] name = "winnow" version = "0.6.18" @@ -6438,7 +6050,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", + "byteorder 1.5.0", "zerocopy-derive", ] diff --git a/Cargo.toml b/Cargo.toml index ef052c569..d2572e9c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,29 +6,37 @@ members = [ "cmd/crates/*", "cmd/crates/soroban-test/tests/fixtures/test-wasms/*", "cmd/crates/soroban-test/tests/fixtures/hello", + "cmd/crates/soroban-test/tests/fixtures/bye", +] +default-members = [ + "cmd/soroban-cli", + "cmd/crates/soroban-spec-tools", + "cmd/crates/soroban-test", +] +exclude = [ + "cmd/crates/soroban-test/tests/fixtures/hello", + "cmd/crates/soroban-test/tests/fixtures/bye", ] -default-members = ["cmd/soroban-cli", "cmd/crates/soroban-spec-tools", "cmd/crates/soroban-test"] -exclude = ["cmd/crates/soroban-test/tests/fixtures/hello"] [workspace.package] -version = "21.5.0" +version = "22.0.0" rust-version = "1.81.0" # Dependencies located in this repo: [workspace.dependencies.soroban-cli] -version = "=21.5.0" +version = "=22.0.0" path = "cmd/soroban-cli" [workspace.dependencies.soroban-spec-json] -version = "=21.5.0" +version = "=22.0.0" path = "./cmd/crates/soroban-spec-json" [workspace.dependencies.soroban-spec-typescript] -version = "21.5.0" +version = "22.0.0" path = "./cmd/crates/soroban-spec-typescript" [workspace.dependencies.soroban-spec-tools] -version = "21.5.0" +version = "22.0.0" path = "./cmd/crates/soroban-spec-tools" # Dependencies from the rs-stellar-xdr repo: @@ -38,26 +46,24 @@ default-features = true # Dependencies from the rs-soroban-sdk repo: [workspace.dependencies.soroban-spec] -version = "=22.0.0-rc.1.1" +version = "=22.0.0-rc.3" [workspace.dependencies.soroban-spec-rust] -version = "=22.0.0-rc.1.1" +version = "=22.0.0-rc.3" [workspace.dependencies.soroban-sdk] -version = "=22.0.0-rc.1.1" +version = "=22.0.0-rc.3" [workspace.dependencies.soroban-token-sdk] -version = "=22.0.0-rc.1.1" +version = "=22.0.0-rc.3" [workspace.dependencies.soroban-ledger-snapshot] -version = "=22.0.0-rc.1.1" +version = "=22.0.0-rc.3" # Dependencies from the rs-stellar-rpc-client repo: [workspace.dependencies.soroban-rpc] package = "stellar-rpc-client" -version = "21.4.0" -git = "https://github.com/stellar/rs-stellar-rpc-client" -branch = "main" +version = "=22.0.0-rc.1" # Dependencies from elsewhere shared by crates: [workspace.dependencies] @@ -85,7 +91,7 @@ tracing = "0.1.37" tracing-subscriber = "0.3.16" tracing-appender = "0.2.2" which = "4.4.0" -wasmparser = "0.90.0" +wasmparser = "0.116.1" directories = "5.0.1" ulid = "1.1" termcolor = "1.1.3" @@ -94,7 +100,11 @@ ed25519-dalek = ">= 2.1.1" http = "1.0.0" jsonrpsee-http-client = "0.20.1" jsonrpsee-core = "0.20.1" -tokio = "1.28.1" +walkdir = "2.5.0" +toml_edit = "0.22.20" +toml = "0.8.19" +reqwest = "0.12.7" +predicates = "3.1.2" [profile.test-wasms] inherits = "release" diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 41575aea0..1ba366ac0 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -18,7 +18,7 @@ For additional information see: - Stellar Docs: https://developers.stellar.org - Smart Contract Docs: https://developers.stellar.org/docs/build/smart-contracts/overview -- CLI Docs: https://developers.stellar.org/docs/tools/stellar-cli +- CLI Docs: https://developers.stellar.org/docs/tools/developer-tools/cli/stellar-cli To get started generate a new identity: @@ -45,8 +45,10 @@ Anything after the `--` double dash (the "slop") is parsed as arguments to the c * `contract` — Tools for smart contract developers * `events` — Watch the network for contract events +* `env` — Prints the current environment variables or defaults to the stdout, in a format that can be used as .env file. Environment variables have precedency over defaults * `keys` — Create and manage identities including keys and addresses -* `network` — Start and configure networks +* `network` — Configure connection to networks +* `container` — Start local networks in containers * `snapshot` — Download a snapshot of a ledger from an archive * `tx` — Sign, Simulate, and Send transactions * `xdr` — Decode and encode XDR @@ -196,7 +198,7 @@ Add contract alias ###### **Arguments:** -* `` — The contract alias that will be removed +* `` — The contract alias that will be used ###### **Options:** @@ -334,6 +336,7 @@ To view the commands that will be executed, without executing them, use the --pr If ommitted, wasm files are written only to the cargo target directory. * `--print-commands-only` — Print commands to build without executing them +* `--meta ` — Add key-value to contract meta (adds the meta to the `contractmetav0` custom section) @@ -386,7 +389,11 @@ If no keys are specified the contract itself is extended. Deploy a wasm contract -**Usage:** `stellar contract deploy [OPTIONS] --source-account <--wasm |--wasm-hash >` +**Usage:** `stellar contract deploy [OPTIONS] --source-account <--wasm |--wasm-hash > [-- ...]` + +###### **Arguments:** + +* `` — If provided, will be passed to the contract's `__constructor` function with provided arguments for that function as `--arg-name value` ###### **Options:** @@ -515,7 +522,7 @@ Outputs no data when no data is present in the contract. * `--wasm ` — Wasm file to extract the data from * `--wasm-hash ` — Wasm hash to get the data for -* `--id ` — Contract id to get the data for +* `--id ` — Contract id or contract alias to get the data for * `--rpc-url ` — RPC server endpoint * `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server @@ -555,7 +562,7 @@ Outputs no data when no data is present in the contract. * `--wasm ` — Wasm file to extract the data from * `--wasm-hash ` — Wasm hash to get the data for -* `--id ` — Contract id to get the data for +* `--id ` — Contract id or contract alias to get the data for * `--rpc-url ` — RPC server endpoint * `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server @@ -595,7 +602,7 @@ Outputs no data when no data is present in the contract. * `--wasm ` — Wasm file to extract the data from * `--wasm-hash ` — Wasm hash to get the data for -* `--id ` — Contract id to get the data for +* `--id ` — Contract id or contract alias to get the data for * `--rpc-url ` — RPC server endpoint * `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server @@ -896,6 +903,19 @@ Watch the network for contract events +## `stellar env` + +Prints the current environment variables or defaults to the stdout, in a format that can be used as .env file. Environment variables have precedency over defaults + +**Usage:** `stellar env [OPTIONS]` + +###### **Options:** + +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." + + + ## `stellar keys` Create and manage identities including keys and addresses @@ -911,6 +931,7 @@ Create and manage identities including keys and addresses * `ls` — List identities * `rm` — Remove an identity * `show` — Given an identity return its private key +* `use` — Set the default identity that will be used on all commands. This allows you to skip `--source-account` or setting a environment variable, while reusing this value in all commands that require it @@ -999,6 +1020,7 @@ Generate a new identity with a seed phrase, currently 12 words * `--fund` — Fund generated key pair Default value: `false` +* `--overwrite` — Overwrite existing identity if it already exists @@ -1051,9 +1073,26 @@ Given an identity return its private key +## `stellar keys use` + +Set the default identity that will be used on all commands. This allows you to skip `--source-account` or setting a environment variable, while reusing this value in all commands that require it + +**Usage:** `stellar keys use [OPTIONS] ` + +###### **Arguments:** + +* `` — Set the default network name + +###### **Options:** + +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." + + + ## `stellar network` -Start and configure networks +Configure connection to networks **Usage:** `stellar network ` @@ -1064,7 +1103,8 @@ Start and configure networks * `ls` — List networks * `start` — ⚠️ Deprecated: use `stellar container start` instead * `stop` — ⚠️ Deprecated: use `stellar container stop` instead -* `container` — Commands to start, stop and get logs for a quickstart container +* `use` — Set the default network that will be used on all commands. This allows you to skip `--network` or setting a environment variable, while reusing this value in all commands that require it +* `container` — ⚠️ Deprecated: use `stellar container` instead @@ -1173,8 +1213,27 @@ Stop a network started with `network start`. For example, if you ran `stellar ne +## `stellar network use` + +Set the default network that will be used on all commands. This allows you to skip `--network` or setting a environment variable, while reusing this value in all commands that require it + +**Usage:** `stellar network use [OPTIONS] ` + +###### **Arguments:** + +* `` — Set the default network name + +###### **Options:** + +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." + + + ## `stellar network container` +⚠️ Deprecated: use `stellar container` instead + Commands to start, stop and get logs for a quickstart container **Usage:** `stellar network container ` @@ -1183,7 +1242,7 @@ Commands to start, stop and get logs for a quickstart container * `logs` — Get logs from a running network container * `start` — Start a container running a Stellar node, RPC, API, and friendbot (faucet) -* `stop` — Stop a network container started with `network container start` +* `stop` — Stop a network container started with `stellar container start` @@ -1207,7 +1266,7 @@ Get logs from a running network container Start a container running a Stellar node, RPC, API, and friendbot (faucet). -`stellar network container start NETWORK [OPTIONS]` +`stellar container start NETWORK [OPTIONS]` By default, when starting a testnet container, without any optional arguments, it will run the equivalent of the following docker command: @@ -1237,7 +1296,7 @@ By default, when starting a testnet container, without any optional arguments, i ## `stellar network container stop` -Stop a network container started with `network container start` +Stop a network container started with `stellar container start` **Usage:** `stellar network container stop [OPTIONS] ` @@ -1251,6 +1310,84 @@ Stop a network container started with `network container start` +## `stellar container` + +Start local networks in containers + +**Usage:** `stellar container ` + +###### **Subcommands:** + +* `logs` — Get logs from a running network container +* `start` — Start a container running a Stellar node, RPC, API, and friendbot (faucet) +* `stop` — Stop a network container started with `stellar container start` + + + +## `stellar container logs` + +Get logs from a running network container + +**Usage:** `stellar container logs [OPTIONS] ` + +###### **Arguments:** + +* `` — Container to get logs from + +###### **Options:** + +* `-d`, `--docker-host ` — Optional argument to override the default docker host. This is useful when you are using a non-standard docker host path for your Docker-compatible container runtime, e.g. Docker Desktop defaults to $HOME/.docker/run/docker.sock instead of /var/run/docker.sock + + + +## `stellar container start` + +Start a container running a Stellar node, RPC, API, and friendbot (faucet). + +`stellar container start NETWORK [OPTIONS]` + +By default, when starting a testnet container, without any optional arguments, it will run the equivalent of the following docker command: + +`docker run --rm -p 8000:8000 --name stellar stellar/quickstart:testing --testnet --enable rpc,horizon` + +**Usage:** `stellar container start [OPTIONS] ` + +###### **Arguments:** + +* `` — Network to start + + Possible values: `local`, `testnet`, `futurenet`, `pubnet` + + +###### **Options:** + +* `-d`, `--docker-host ` — Optional argument to override the default docker host. This is useful when you are using a non-standard docker host path for your Docker-compatible container runtime, e.g. Docker Desktop defaults to $HOME/.docker/run/docker.sock instead of /var/run/docker.sock +* `--name ` — Optional argument to specify the container name +* `-l`, `--limits ` — Optional argument to specify the limits for the local network only +* `-p`, `--ports-mapping ` — Argument to specify the `HOST_PORT:CONTAINER_PORT` mapping + + Default value: `8000:8000` +* `-t`, `--image-tag-override ` — Optional argument to override the default docker image tag for the given network +* `--protocol-version ` — Optional argument to specify the protocol version for the local network only + + + +## `stellar container stop` + +Stop a network container started with `stellar container start` + +**Usage:** `stellar container stop [OPTIONS] ` + +###### **Arguments:** + +* `` — Container to stop + +###### **Options:** + +* `-d`, `--docker-host ` — Optional argument to override the default docker host. This is useful when you are using a non-standard docker host path for your Docker-compatible container runtime, e.g. Docker Desktop defaults to $HOME/.docker/run/docker.sock instead of /var/run/docker.sock + + + ## `stellar snapshot` Download a snapshot of a ledger from an archive @@ -1487,7 +1624,7 @@ Creates, updates, or deletes a trustline Learn more about trustlines https://dev * `--line ` * `--limit ` — Limit for the trust line, 0 to remove the trust line - Default value: `18446744073709551615` + Default value: `9223372036854775807` @@ -1517,7 +1654,7 @@ Creates and funds a new account with the specified starting balance * `--destination ` — Account Id to create, e.g. `GBX...` * `--starting-balance ` — Initial balance in stroops of the account, default 1 XLM - Default value: `10_000_000` + Default value: `10000000` @@ -1612,9 +1749,9 @@ Set option for an account such as flags, inflation destination, signers, home do * `--signer ` — Add, update, or remove a signer from an account * `--signer-weight ` — Signer weight is a number from 0-255 (inclusive). The signer is deleted if the weight is 0 * `--set-required` — When enabled, an issuer must approve an account before that account can hold its asset. https://developers.stellar.org/docs/tokens/control-asset-access#authorization-required-0x1 -* `--set-revocable` — When enabled, an issuer can revoke an existing trustline’s authorization, thereby freezing the asset held by an account. https://developers.stellar.org/docs/tokens/control-asset-access#authorization-revocable-0x2 +* `--set-revocable` — When enabled, an issuer can revoke an existing trustline's authorization, thereby freezing the asset held by an account. https://developers.stellar.org/docs/tokens/control-asset-access#authorization-revocable-0x2 * `--set-clawback-enabled` — Enables the issuing account to take back (burning) all of the asset. https://developers.stellar.org/docs/tokens/control-asset-access#clawback-enabled-0x8 -* `--set-immutable` — With this setting, none of the other authorization flags (`AUTH_REQUIRED_FLAG`, `AUTH_REVOCABLE_FLAG`) can be set, and the issuing account can’t be merged. https://developers.stellar.org/docs/tokens/control-asset-access#authorization-immutable-0x4 +* `--set-immutable` — With this setting, none of the other authorization flags (`AUTH_REQUIRED_FLAG`, `AUTH_REVOCABLE_FLAG`) can be set, and the issuing account can't be merged. https://developers.stellar.org/docs/tokens/control-asset-access#authorization-immutable-0x4 * `--clear-required` * `--clear-revocable` * `--clear-immutable` diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..a084d8624 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 Stellar Development Foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Makefile b/Makefile index 4db0946ce..92cdacd73 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ endif # By default make `?=` operator will treat empty assignment as a set value and will not use the default value. # Both cases should fallback to default of getting the version from git tag. ifeq ($(strip $(REPOSITORY_VERSION)),) - override REPOSITORY_VERSION = "$(shell git describe --tags --always --abbrev=0 --match='v[0-9]*.[0-9]*.[0-9]*' 2> /dev/null | sed 's/^.//')" + override REPOSITORY_VERSION = "$(shell ( git describe --tags --always --abbrev=0 --match='v[0-9]*.[0-9]*.[0-9]*' 2> /dev/null | sed 's/^.//' ) )" endif REPOSITORY_BRANCH := "$(shell git rev-parse --abbrev-ref HEAD)" BUILD_TIMESTAMP ?= $(shell date '+%Y-%m-%dT%H:%M:%S') @@ -31,6 +31,7 @@ install_rust: install install: cargo install --force --locked --path ./cmd/stellar-cli --debug cargo install --force --locked --path ./cmd/crates/soroban-test/tests/fixtures/hello --root ./target --debug --quiet + cargo install --force --locked --path ./cmd/crates/soroban-test/tests/fixtures/bye --root ./target --debug --quiet # regenerate the example lib in `cmd/crates/soroban-spec-typsecript/fixtures/ts` build-snapshot: typescript-bindings-fixtures @@ -47,7 +48,7 @@ generate-full-help-doc: cargo run --bin doc-gen --features clap-markdown test: build-test - cargo test + cargo test --workspace e2e-test: cargo test --features it --test it -- integration diff --git a/README.md b/README.md index c39d883c2..f8ba1f052 100644 --- a/README.md +++ b/README.md @@ -10,16 +10,13 @@ This repo is home to the Stellar CLI, the command-line multi-tool for running an - [Documentation](#documentation) - [Cookbook](#cookbook) -- [Installation](#installation) -- [Installation with Experimental Features](#installation-with-experimental-features) +- [Install](#install) - [Autocomplete](#autocomplete) - [Latest Release](#latest-release) - [Upcoming Features](#upcoming-features) - [To Contribute](#to-contribute) - [Additional Developer Resources](#additional-developer-resources) - - ## Documentation For installation options see below, for usage instructions [see the full help docs](FULL_HELP_DOCS.md). @@ -27,28 +24,24 @@ For installation options see below, for usage instructions [see the full help do ## Cookbook To understand how to get the most of the Stellar CLI, see the [Stellar CLI Cookbook](https://github.com/stellar/stellar-cli/tree/main/cookbook) for recipes and a collection of resources to teach you how to use the CLI. Examples of recipes included in the CLI cookbook include: send payments, manage contract lifecycle, extend contract instance/storage/wasm, and more. -## Installation -Install the latest version from source: -``` -cargo install --locked stellar-cli --features opt -``` +## Install + +Install with Homebrew (macOS, Linux): -Install with `cargo-binstall`: ``` -cargo install --locked cargo-binstall -cargo binstall -y stellar-cli +brew install stellar-cli ``` -Install with Homebrew: - +Install the latest version from source: ``` -brew install stellar-cli +cargo install --locked stellar-cli --features opt ``` -## Installation with Experimental Features -To use the potentially unreleased bleeding edge CLI functionalities, install from git: +Install or run the unreleased main branch with nix: ``` -cargo install --locked stellar-cli --features opt --git https://github.com/stellar/stellar-cli.git +$ nix run 'github:stellar/stellar-cli' -- --help +or install +$ nix profile install github:stellar/stellar-cli ``` ## Autocomplete @@ -60,12 +53,12 @@ stellar completion --shell Possible SHELL values are `bash`, `elvish`, `fish`, `powershell`, `zsh`, etc. To enable autocomplete in the current bash shell, run: -``` +```bash source <(stellar completion --shell bash) ``` To enable autocomplete permanently, run: -``` +```bash echo "source <(stellar completion --shell bash)" >> ~/.bashrc ``` diff --git a/action.yml b/action.yml index 3ed6af8e7..9b1426d94 100644 --- a/action.yml +++ b/action.yml @@ -15,12 +15,15 @@ runs: run: | mkdir -p $HOME/.local/bin echo "$HOME/.local/bin" >> $GITHUB_PATH + - name: Determine version to install + id: version + shell: bash + run: | + echo "version=${{ inputs.version || github.action_ref }}" >> "$GITHUB_OUTPUT" - name: Copy binary to install location shell: bash - env: - REF: ${{ github.action_ref }} run: | - version="${{ inputs.version || env.REF }}" + version="${{ steps.version.outputs.version }}" case "${{ runner.os }}-${{ runner.arch }}" in 'Linux-X64') os_arch=x86_64-unknown-linux-gnu @@ -45,3 +48,17 @@ runs: url="https://github.com/stellar/stellar-cli/releases/download/v$version/$file" echo "$url" curl -fL "$url" | tar xvz -C $HOME/.local/bin + - name: Verify binary against attestation + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + version="${{ steps.version.outputs.version }}" + subject="$(gh attestation verify ~/.local/bin/stellar --repo stellar/stellar-cli --format json -q '.[].verificationResult.signature.certificate.subjectAlternativeName')" + echo "Found subject: $subject" >&2 + expected_subject="https://github.com/stellar/stellar-cli/.github/workflows/binaries.yml@refs/tags/v$version" + echo "Expected subject: $expected_subject" >&2 + if [[ "$subject" != "$expected_subject" ]]; then + echo "Attestation verification found unexpected subject" >&2 + exit 1 + fi diff --git a/cmd/crates/soroban-spec-tools/src/contract.rs b/cmd/crates/soroban-spec-tools/src/contract.rs index 9984a19d1..501609111 100644 --- a/cmd/crates/soroban-spec-tools/src/contract.rs +++ b/cmd/crates/soroban-spec-tools/src/contract.rs @@ -40,9 +40,9 @@ pub enum Error { impl Spec { pub fn new(bytes: &[u8]) -> Result { - let mut env_meta: Option<&[u8]> = None; - let mut meta: Option<&[u8]> = None; - let mut spec: Option<&[u8]> = None; + let mut env_meta: Option> = None; + let mut meta: Option> = None; + let mut spec: Option> = None; for payload in wasmparser::Parser::new(0).parse_all(bytes) { let payload = payload?; if let wasmparser::Payload::CustomSection(section) = payload { @@ -52,13 +52,19 @@ impl Spec { "contractspecv0" => &mut spec, _ => continue, }; - *out = Some(section.data()); + + if let Some(existing_data) = out { + let combined_data = [existing_data, section.data()].concat(); + *out = Some(combined_data); + } else { + *out = Some(section.data().to_vec()); + } }; } let mut env_meta_base64 = None; let env_meta = if let Some(env_meta) = env_meta { - env_meta_base64 = Some(base64.encode(env_meta)); + env_meta_base64 = Some(base64.encode(&env_meta)); let cursor = Cursor::new(env_meta); let mut read = Limited::new(cursor, Limits::none()); ScEnvMetaEntry::read_xdr_iter(&mut read).collect::, xdr::Error>>()? @@ -68,7 +74,7 @@ impl Spec { let mut meta_base64 = None; let meta = if let Some(meta) = meta { - meta_base64 = Some(base64.encode(meta)); + meta_base64 = Some(base64.encode(&meta)); let cursor = Cursor::new(meta); let mut depth_limit_read = Limited::new(cursor, Limits::none()); ScMetaEntry::read_xdr_iter(&mut depth_limit_read) @@ -78,7 +84,7 @@ impl Spec { }; let (spec_base64, spec) = if let Some(spec) = spec { - let (spec_base64, spec) = Spec::spec_to_base64(spec)?; + let (spec_base64, spec) = Spec::spec_to_base64(&spec)?; (Some(spec_base64), spec) } else { (None, vec![]) diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package-lock.json b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package-lock.json index f45484ffb..a4e3759e2 100644 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package-lock.json +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package-lock.json @@ -8,22 +8,26 @@ "name": "test_custom_types", "version": "0.0.0", "dependencies": { - "@stellar/stellar-sdk": "12.1.0", + "@stellar/stellar-sdk": "13.0.0", "buffer": "6.0.3" }, "devDependencies": { - "typescript": "5.3.3" + "typescript": "^5.6.2" } }, "node_modules/@stellar/js-xdr": { - "version": "3.1.1", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-3.1.2.tgz", + "integrity": "sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==", "license": "Apache-2.0" }, "node_modules/@stellar/stellar-base": { - "version": "12.0.1", + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-13.0.1.tgz", + "integrity": "sha512-Xbd12mc9Oj/130Tv0URmm3wXG77XMshZtZ2yNCjqX5ZbMD5IYpbBs3DVCteLU/4SLj/Fnmhh1dzhrQXnk4r+pQ==", "license": "Apache-2.0", "dependencies": { - "@stellar/js-xdr": "^3.1.1", + "@stellar/js-xdr": "^3.1.2", "base32.js": "^0.1.0", "bignumber.js": "^9.1.2", "buffer": "^6.0.3", @@ -31,17 +35,20 @@ "tweetnacl": "^1.0.3" }, "optionalDependencies": { - "sodium-native": "^4.1.1" + "sodium-native": "^4.3.0" } }, "node_modules/@stellar/stellar-sdk": { - "version": "12.1.0", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-13.0.0.tgz", + "integrity": "sha512-+wvmKi+XWwu27nLYTM17EgBdpbKohEkOfCIK4XKfsI4WpMXAqvnqSm98i9h5dAblNB+w8BJqzGs1JY0PtTGm4A==", "license": "Apache-2.0", "dependencies": { - "@stellar/stellar-base": "^12.0.1", - "axios": "^1.7.2", + "@stellar/stellar-base": "^13.0.1", + "axios": "^1.7.7", "bignumber.js": "^9.1.2", "eventsource": "^2.0.2", + "feaxios": "^0.0.20", "randombytes": "^2.1.0", "toml": "^3.0.0", "urijs": "^1.19.1" @@ -49,12 +56,15 @@ }, "node_modules/asynckit": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, "node_modules/axios": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", - "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -63,6 +73,8 @@ }, "node_modules/base32.js": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", + "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==", "license": "MIT", "engines": { "node": ">=0.12.0" @@ -88,6 +100,8 @@ }, "node_modules/bignumber.js": { "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", "license": "MIT", "engines": { "node": "*" @@ -117,6 +131,8 @@ }, "node_modules/combined-stream": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -127,6 +143,8 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", "engines": { "node": ">=0.4.0" @@ -139,8 +157,19 @@ "node": ">=12.0.0" } }, + "node_modules/feaxios": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/feaxios/-/feaxios-0.0.20.tgz", + "integrity": "sha512-g3hm2YDNffNxA3Re3Hd8ahbpmDee9Fv1Pb1C/NoWsjY7mtD8nyNeJytUzn+DK0Hyl9o6HppeWOrtnqgmhOYfWA==", + "license": "MIT", + "dependencies": { + "is-retry-allowed": "^3.0.0" + } + }, "node_modules/follow-redirects": { - "version": "1.15.6", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", @@ -158,7 +187,9 @@ } }, "node_modules/form-data": { - "version": "4.0.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -189,10 +220,26 @@ }, "node_modules/inherits": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/is-retry-allowed": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-3.0.0.tgz", + "integrity": "sha512-9xH0xvoggby+u0uGF7cZXdrutWiBiaFG8ZT4YFPXL8NzkyAwX3AKGLeFQLvzDpM430+nDFBZ1LHkie/8ocL06A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mime-db": { "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -200,6 +247,8 @@ }, "node_modules/mime-types": { "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -209,7 +258,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.8.1", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.3.tgz", + "integrity": "sha512-EMS95CMJzdoSKoIiXo8pxKoL8DYxwIZXYlLmgPb8KUv794abpnLK6ynsCAWNliOjREKruYKdzbh76HHYUHX7nw==", "license": "MIT", "optional": true, "bin": { @@ -220,6 +271,8 @@ }, "node_modules/proxy-from-env": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, "node_modules/randombytes": { @@ -249,6 +302,8 @@ }, "node_modules/sha.js": { "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "license": "(MIT AND BSD-3-Clause)", "dependencies": { "inherits": "^2.0.1", @@ -259,8 +314,9 @@ } }, "node_modules/sodium-native": { - "version": "4.1.1", - "hasInstallScript": true, + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-4.3.1.tgz", + "integrity": "sha512-YdP64gAdpIKHfL4ttuX4aIfjeunh9f+hNeQJpE9C8UMndB3zkgZ7YmmGT4J2+v6Ibyp6Wem8D1TcSrtdW0bqtg==", "license": "MIT", "optional": true, "dependencies": { @@ -273,13 +329,16 @@ }, "node_modules/tweetnacl": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", "license": "Unlicense" }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package.json b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package.json index 5cc5e337f..8c3762b91 100644 --- a/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package.json +++ b/cmd/crates/soroban-spec-typescript/fixtures/test_custom_types/package.json @@ -8,10 +8,10 @@ "build": "tsc" }, "dependencies": { - "buffer": "6.0.3", - "@stellar/stellar-sdk": "12.1.0" + "@stellar/stellar-sdk": "13.0.0", + "buffer": "6.0.3" }, "devDependencies": { - "typescript": "5.3.3" + "typescript": "^5.6.2" } } diff --git a/cmd/crates/soroban-spec-typescript/src/project_template/package.json b/cmd/crates/soroban-spec-typescript/src/project_template/package.json index e9ba581a3..b5c6459e4 100644 --- a/cmd/crates/soroban-spec-typescript/src/project_template/package.json +++ b/cmd/crates/soroban-spec-typescript/src/project_template/package.json @@ -8,10 +8,10 @@ "build": "tsc" }, "dependencies": { - "buffer": "6.0.3", - "@stellar/stellar-sdk": "12.1.0" + "@stellar/stellar-sdk": "13.0.0", + "buffer": "6.0.3" }, "devDependencies": { - "typescript": "5.3.3" + "typescript": "^5.6.2" } } diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh b/cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh index d85eff115..031061cca 100755 --- a/cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh +++ b/cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh @@ -37,6 +37,7 @@ function deploy_all() { } function bind() { exe eval "./soroban contract bindings typescript --contract-id $(cat $1) --output-dir ./node_modules/$2 --overwrite" + exe eval "sh -c \"cd ./node_modules/$2 && npm install && npm run build\"" } function bind_all() { bind contract-id-custom-types.txt test-custom-types diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/package-lock.json b/cmd/crates/soroban-spec-typescript/ts-tests/package-lock.json index deee8f09d..45cc97a67 100644 --- a/cmd/crates/soroban-spec-typescript/ts-tests/package-lock.json +++ b/cmd/crates/soroban-spec-typescript/ts-tests/package-lock.json @@ -7,14 +7,14 @@ "hasInstallScript": true, "devDependencies": { "@ava/typescript": "^4.1.0", - "@stellar/stellar-sdk": "12.2.0", + "@stellar/stellar-sdk": "13.0.0", "@types/node": "^20.4.9", "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", "ava": "^5.3.1", "dotenv": "^16.3.1", "eslint": "^8.53.0", - "typescript": "^5.3.3" + "typescript": "^5.6.2" } }, "node_modules/@ava/typescript": { @@ -202,13 +202,15 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-3.1.2.tgz", "integrity": "sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/@stellar/stellar-base": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-12.1.0.tgz", - "integrity": "sha512-pWwn+XWP5NotmIteZNuJzHeNn9DYSqH3lsYbtFUoSYy1QegzZdi9D8dK6fJ2fpBAnf/rcDjHgHOw3gtHaQFVbg==", + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-13.0.1.tgz", + "integrity": "sha512-Xbd12mc9Oj/130Tv0URmm3wXG77XMshZtZ2yNCjqX5ZbMD5IYpbBs3DVCteLU/4SLj/Fnmhh1dzhrQXnk4r+pQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@stellar/js-xdr": "^3.1.2", "base32.js": "^0.1.0", @@ -218,19 +220,21 @@ "tweetnacl": "^1.0.3" }, "optionalDependencies": { - "sodium-native": "^4.1.1" + "sodium-native": "^4.3.0" } }, "node_modules/@stellar/stellar-sdk": { - "version": "12.2.0", - "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-12.2.0.tgz", - "integrity": "sha512-Wy5sDOqb5JvAC76f4sQIV6Pe3JNyZb0PuyVNjwt3/uWsjtxRkFk6s2yTHTefBLWoR+mKxDjO7QfzhycF1v8FXQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-13.0.0.tgz", + "integrity": "sha512-+wvmKi+XWwu27nLYTM17EgBdpbKohEkOfCIK4XKfsI4WpMXAqvnqSm98i9h5dAblNB+w8BJqzGs1JY0PtTGm4A==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@stellar/stellar-base": "^12.1.0", - "axios": "^1.7.2", + "@stellar/stellar-base": "^13.0.1", + "axios": "^1.7.7", "bignumber.js": "^9.1.2", "eventsource": "^2.0.2", + "feaxios": "^0.0.20", "randombytes": "^2.1.0", "toml": "^3.0.0", "urijs": "^1.19.1" @@ -601,7 +605,8 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/ava": { "version": "5.3.1", @@ -700,10 +705,11 @@ } }, "node_modules/axios": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", - "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "dev": true, + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -721,6 +727,7 @@ "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -750,6 +757,7 @@ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", "dev": true, + "license": "MIT", "engines": { "node": "*" } @@ -1043,6 +1051,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -1162,6 +1171,7 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -1581,6 +1591,16 @@ "reusify": "^1.0.4" } }, + "node_modules/feaxios": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/feaxios/-/feaxios-0.0.20.tgz", + "integrity": "sha512-g3hm2YDNffNxA3Re3Hd8ahbpmDee9Fv1Pb1C/NoWsjY7mtD8nyNeJytUzn+DK0Hyl9o6HppeWOrtnqgmhOYfWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-retry-allowed": "^3.0.0" + } + }, "node_modules/figures": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", @@ -1658,9 +1678,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "dev": true, "funding": [ { @@ -1668,6 +1688,7 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -1682,6 +1703,7 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -2029,6 +2051,19 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "dev": true }, + "node_modules/is-retry-allowed": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-3.0.0.tgz", + "integrity": "sha512-9xH0xvoggby+u0uGF7cZXdrutWiBiaFG8ZT4YFPXL8NzkyAwX3AKGLeFQLvzDpM430+nDFBZ1LHkie/8ocL06A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", @@ -2247,6 +2282,7 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -2256,6 +2292,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -2303,10 +2340,11 @@ "dev": true }, "node_modules/node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.3.tgz", + "integrity": "sha512-EMS95CMJzdoSKoIiXo8pxKoL8DYxwIZXYlLmgPb8KUv794abpnLK6ynsCAWNliOjREKruYKdzbh76HHYUHX7nw==", "dev": true, + "license": "MIT", "optional": true, "bin": { "node-gyp-build": "bin.js", @@ -2703,7 +2741,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/punycode": { "version": "2.3.1", @@ -2906,6 +2945,7 @@ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, + "license": "(MIT AND BSD-3-Clause)", "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -2967,11 +3007,11 @@ } }, "node_modules/sodium-native": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-4.1.1.tgz", - "integrity": "sha512-LXkAfRd4FHtkQS4X6g+nRcVaN7mWVNepV06phIsC6+IZFvGh1voW5TNQiQp2twVaMf05gZqQjuS+uWLM6gHhNQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-4.3.1.tgz", + "integrity": "sha512-YdP64gAdpIKHfL4ttuX4aIfjeunh9f+hNeQJpE9C8UMndB3zkgZ7YmmGT4J2+v6Ibyp6Wem8D1TcSrtdW0bqtg==", "dev": true, - "hasInstallScript": true, + "license": "MIT", "optional": true, "dependencies": { "node-gyp-build": "^4.8.0" @@ -3167,7 +3207,8 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "dev": true + "dev": true, + "license": "Unlicense" }, "node_modules/type-check": { "version": "0.4.0", @@ -3194,10 +3235,11 @@ } }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/package.json b/cmd/crates/soroban-spec-typescript/ts-tests/package.json index 734b26bba..a8bb3c1ff 100644 --- a/cmd/crates/soroban-spec-typescript/ts-tests/package.json +++ b/cmd/crates/soroban-spec-typescript/ts-tests/package.json @@ -8,14 +8,14 @@ }, "devDependencies": { "@ava/typescript": "^4.1.0", + "@stellar/stellar-sdk": "13.0.0", "@types/node": "^20.4.9", "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", "ava": "^5.3.1", "dotenv": "^16.3.1", "eslint": "^8.53.0", - "@stellar/stellar-sdk": "12.2.0", - "typescript": "^5.3.3" + "typescript": "^5.6.2" }, "ava": { "typescript": { @@ -29,4 +29,3 @@ ] } } - diff --git a/cmd/crates/soroban-test/Cargo.toml b/cmd/crates/soroban-test/Cargo.toml index b240494e4..d035652d1 100644 --- a/cmd/crates/soroban-test/Cargo.toml +++ b/cmd/crates/soroban-test/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/stellar/soroban-test" authors = ["Stellar Development Foundation "] license = "Apache-2.0" readme = "README.md" -version = "21.5.0" +version.workspace = true edition = "2021" rust-version.workspace = true autobins = false @@ -29,9 +29,9 @@ thiserror = "1.0.31" sha2 = "0.10.6" assert_cmd = "2.0.4" assert_fs = "1.0.7" -predicates = "2.1.5" +predicates = { workspace = true } fs_extra = "1.3.0" -toml = "0.8.10" +toml = { workspace = true } [dev-dependencies] diff --git a/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.lock b/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.lock new file mode 100644 index 000000000..59924f294 --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "stellar-bye" +version = "0.1.0" diff --git a/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.toml new file mode 100644 index 000000000..deccb9c75 --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "stellar-bye" +version = "0.0.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/cmd/crates/soroban-test/tests/fixtures/bye/src/main.rs b/cmd/crates/soroban-test/tests/fixtures/bye/src/main.rs new file mode 100644 index 000000000..5fb678863 --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/bye/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Bye, world!"); +} diff --git a/cmd/crates/soroban-test/tests/fixtures/hello/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/hello/Cargo.toml index 0c3b1512f..fe7eb636a 100644 --- a/cmd/crates/soroban-test/tests/fixtures/hello/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/hello/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "soroban-hello" -version = "21.5.0" +version = "0.0.0" edition = "2021" publish = false diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/Cargo.toml new file mode 100644 index 000000000..69d8a6430 --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "test_constructor" +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 = { workspace = true } + +[dev-dependencies] +soroban-sdk = { workspace = true, features = ["testutils"]} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/src/lib.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/src/lib.rs new file mode 100644 index 000000000..8b867968f --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/src/lib.rs @@ -0,0 +1,18 @@ +#![no_std] +use soroban_sdk::{contract, contractimpl, symbol_short, Env, Symbol}; + +#[contract] +pub struct Contract; +const COUNTER: Symbol = symbol_short!("COUNTER"); + +#[contractimpl] +impl Contract { + /// Example constructor + pub fn __constructor(env: Env, counter: u32) { + env.storage().persistent().set(&COUNTER, &counter); + } + /// Counter value + pub fn counter(env: Env) -> u32 { + env.storage().persistent().get(&COUNTER).unwrap() + } +} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/Cargo.toml index 792925edb..765f671c6 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/Cargo.toml @@ -1,18 +1,17 @@ [package] name = "test_custom_account" -version = "21.5.0" +version = "0.0.0" authors = ["Stellar Development Foundation "] license = "Apache-2.0" edition = "2021" publish = false -rust-version.workspace = true [lib] crate-type = ["cdylib"] doctest = false [dependencies] -soroban-sdk = { version = "=21.7.2" } +soroban-sdk = { workspace = true } [dev-dependencies] -soroban-sdk = { version = "=21.7.2", features = ["testutils"]} +soroban-sdk = { workspace = true, features = ["testutils"]} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/src/lib.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/src/lib.rs index c71324110..ed924322b 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/src/lib.rs +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/src/lib.rs @@ -115,7 +115,9 @@ impl CustomAccountInterface for Contract { return Err(Error::NotPermitted); } } - Context::CreateContractHostFn(_) => return Err(Error::InvalidContext), + Context::CreateContractWithCtorHostFn(_) | Context::CreateContractHostFn(_) => { + return Err(Error::InvalidContext) + } }; } diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_type/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_type/Cargo.toml index 398520104..b5a17c6a5 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_type/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_type/Cargo.toml @@ -1,18 +1,17 @@ [package] name = "test_custom_types" -version = "21.5.0" +version = "0.0.0" authors = ["Stellar Development Foundation "] license = "Apache-2.0" edition = "2021" publish = false -rust-version.workspace = true [lib] crate-type = ["cdylib", "rlib"] doctest = false [dependencies] -soroban-sdk = { version = "=21.7.2" } +soroban-sdk = { workspace = true } [dev-dependencies] -soroban-sdk = { version = "=21.7.2", features = ["testutils"]} +soroban-sdk = { workspace = true, features = ["testutils"]} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/Cargo.toml index 5f8b1256f..a13c98a39 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/Cargo.toml @@ -1,18 +1,17 @@ [package] name = "test_hello_world" -version = "21.5.0" +version = "0.0.0" authors = ["Stellar Development Foundation "] license = "Apache-2.0" edition = "2021" publish = false -rust-version.workspace = true [lib] crate-type = ["cdylib", "rlib"] doctest = false [dependencies] -soroban-sdk = { version = "=21.7.2" } +soroban-sdk = { workspace = true } [dev-dependencies] -soroban-sdk = { version = "=21.7.2", features = ["testutils"]} +soroban-sdk = { workspace = true, features = ["testutils"]} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs index 40006a1b7..b4b53c237 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs @@ -77,7 +77,7 @@ mod test { #[test] fn test_hello() { let env = Env::default(); - let contract_id = env.register_contract(None, Contract); + let contract_id = env.register(Contract, ()); let client = ContractClient::new(&env, &contract_id); let world = symbol_short!("world"); let res = client.hello(&world); diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml index 509483408..495a52d71 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml @@ -1,18 +1,17 @@ [package] name = "test_swap" -version.workspace = true +version = "0.0.0" authors = ["Stellar Development Foundation "] license = "Apache-2.0" edition = "2021" publish = false -rust-version.workspace = true [lib] crate-type = ["cdylib"] doctest = false [dependencies] -soroban-sdk = { version = "=21.7.2" } +soroban-sdk = { workspace = true } [dev-dependencies] -soroban-sdk = { version = "=21.7.2", features = ["testutils"] } +soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/test.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/test.rs index e32b45e0a..a60615162 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/test.rs +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/test.rs @@ -11,7 +11,9 @@ use token::Client as TokenClient; use token::StellarAssetClient as TokenAdminClient; fn create_token_contract<'a>(e: &Env, admin: &Address) -> (TokenClient<'a>, TokenAdminClient<'a>) { - let contract_address = e.register_stellar_asset_contract(admin.clone()); + let contract_address = e + .register_stellar_asset_contract_v2(admin.clone()) + .address(); ( TokenClient::new(e, &contract_address), TokenAdminClient::new(e, &contract_address), @@ -19,7 +21,7 @@ fn create_token_contract<'a>(e: &Env, admin: &Address) -> (TokenClient<'a>, Toke } fn create_atomic_swap_contract(e: &Env) -> AtomicSwapContractClient { - AtomicSwapContractClient::new(e, &e.register_contract(None, AtomicSwapContract {})) + AtomicSwapContractClient::new(e, &e.register(AtomicSwapContract {}, ())) } #[test] diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml index 03055f2b7..6456983e8 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml @@ -1,19 +1,18 @@ [package] name = "test_token" -version.workspace = true +version = "0.0.0" description = "Soroban standard token contract" authors = ["Stellar Development Foundation "] license = "Apache-2.0" edition = "2021" publish = false -rust-version.workspace = true [lib] crate-type = ["cdylib"] [dependencies] -soroban-sdk = { version = "=21.7.2" } -soroban-token-sdk = { version = "=21.7.2" } +soroban-sdk = { workspace = true } +soroban-token-sdk = { workspace = true } [dev-dependencies] -soroban-sdk = { version = "=21.7.2", features = ["testutils"] } +soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/test.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/test.rs index 08be5f9ef..cd892a646 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/test.rs +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/src/test.rs @@ -9,7 +9,7 @@ use soroban_sdk::{ }; fn create_token<'a>(e: &Env, admin: &Address) -> TokenClient<'a> { - let token = TokenClient::new(e, &e.register_contract(None, Token {})); + let token = TokenClient::new(e, &e.register(Token {}, ())); token.initialize(admin, &7, &"name".into_val(e), &"symbol".into_val(e)); token } @@ -247,7 +247,7 @@ fn initialize_already_initialized() { fn decimal_is_over_max() { let e = Env::default(); let admin = Address::generate(&e); - let token = TokenClient::new(&e, &e.register_contract(None, Token {})); + let token = TokenClient::new(&e, &e.register(Token {}, ())); token.initialize( &admin, &(u32::from(u8::MAX) + 1), diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/udt/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/udt/Cargo.toml index aea356447..206284802 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/udt/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/udt/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["cdylib"] doctest = false [dependencies] -soroban-sdk = { version = "=21.7.2" } +soroban-sdk = { workspace = true } [dev-dependencies] -soroban-sdk = { version = "=21.7.2" , features = ["testutils"]} +soroban-sdk = { workspace = true , features = ["testutils"]} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/udt/src/lib.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/udt/src/lib.rs index 695d8a7a3..efb3cf480 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/udt/src/lib.rs +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/udt/src/lib.rs @@ -80,7 +80,7 @@ mod test { #[test] fn test_add() { let e = Env::default(); - let contract_id = e.register_contract(None, Contract); + let contract_id = e.register(Contract, ()); let client = ContractClient::new(&e, &contract_id); let udt = UdtStruct { diff --git a/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/src/lib.rs b/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/src/lib.rs index 1552f5855..d04162b5a 100644 --- a/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/src/lib.rs +++ b/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/src/lib.rs @@ -1,9 +1,11 @@ #![no_std] -use soroban_sdk::{contract, contractimpl}; +use soroban_sdk::{contract, contractimpl, contractmeta}; #[contract] pub struct Contract; +contractmeta!(key = "Description", val = "A test add contract"); + #[contractimpl] impl Contract { pub fn add(x: u64, y: u64) -> u128 { diff --git a/cmd/crates/soroban-test/tests/it/build.rs b/cmd/crates/soroban-test/tests/it/build.rs index aba6aadf5..925d88df1 100644 --- a/cmd/crates/soroban-test/tests/it/build.rs +++ b/cmd/crates/soroban-test/tests/it/build.rs @@ -116,3 +116,37 @@ cargo rustc --manifest-path=contracts/add/Cargo.toml --crate-type=cdylib --targe ", )); } + +#[test] +fn build_with_metadata() { + let sandbox = TestEnv::default(); + let cargo_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let fixture_path = cargo_dir.join("tests/fixtures/workspace/contracts/add"); + let outdir = sandbox.dir().join("out"); + + sandbox + .new_assert_cmd("contract") + .current_dir(&fixture_path) + .arg("build") + .arg("--meta") + .arg("contract meta=added on build") + .arg("--out-dir") + .arg(&outdir) + .assert() + .success(); + + // verify that the metadata added in the contract code via contractmetadata! macro is present + // as well as the meta that is included on build + let wasm_path = fixture_path.join(&outdir).join("add.wasm"); + sandbox + .new_assert_cmd("contract") + .current_dir(&fixture_path) + .arg("info") + .arg("meta") + .arg("--wasm") + .arg(wasm_path) + .assert() + .success() + .stdout(predicate::str::contains("Description: A test add contract")) + .stdout(predicate::str::contains("contract meta: added on build")); +} diff --git a/cmd/crates/soroban-test/tests/it/config.rs b/cmd/crates/soroban-test/tests/it/config.rs index 70dfaa693..b796910a8 100644 --- a/cmd/crates/soroban-test/tests/it/config.rs +++ b/cmd/crates/soroban-test/tests/it/config.rs @@ -1,4 +1,5 @@ use assert_fs::TempDir; +use predicates::prelude::predicate; use soroban_test::{AssertExt, TestEnv}; use std::{fs, path::Path}; @@ -288,3 +289,107 @@ fn use_env() { .success() .stdout("SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD\n"); } + +#[test] +fn config_dirs_precedence() { + let sandbox = TestEnv::default(); + + sandbox + .new_assert_cmd("keys") + .env( + "SOROBAN_SECRET_KEY", + "SC4ZPYELVR7S7EE7KZDZN3ETFTNQHHLTUL34NUAAWZG5OK2RGJ4V2U3Z", + ) + .arg("add") + .arg("alice") + .assert() + .success(); + + fs::rename( + sandbox.dir().join(".stellar"), + sandbox.dir().join("_soroban"), + ) + .unwrap(); + + sandbox + .new_assert_cmd("keys") + .env( + "SOROBAN_SECRET_KEY", + "SAQMV6P3OWM2SKCK3OEWNXSRYWK5RNNUL5CPHQGIJF2WVT4EI2BZ63GG", + ) + .arg("add") + .arg("alice") + .assert() + .success(); + + fs::rename( + sandbox.dir().join("_soroban"), + sandbox.dir().join(".soroban"), + ) + .unwrap(); + + sandbox + .new_assert_cmd("keys") + .arg("show") + .arg("alice") + .arg("--verbose") + .assert() + .success() + .stderr(predicate::str::contains( + "WARN soroban_cli::utils: the .stellar and .soroban config directories exist at path", + )) + .stdout("SAQMV6P3OWM2SKCK3OEWNXSRYWK5RNNUL5CPHQGIJF2WVT4EI2BZ63GG\n"); +} + +#[test] +fn set_default_identity() { + let sandbox = TestEnv::default(); + + sandbox + .new_assert_cmd("keys") + .env( + "SOROBAN_SECRET_KEY", + "SC4ZPYELVR7S7EE7KZDZN3ETFTNQHHLTUL34NUAAWZG5OK2RGJ4V2U3Z", + ) + .arg("add") + .arg("alice") + .assert() + .success(); + + sandbox + .new_assert_cmd("keys") + .arg("use") + .arg("alice") + .assert() + .stderr(predicate::str::contains( + "The default source account is set to `alice`", + )) + .success(); + + sandbox + .new_assert_cmd("env") + .assert() + .stdout(predicate::str::contains("STELLAR_ACCOUNT=alice")) + .success(); +} + +#[test] +fn set_default_network() { + let sandbox = TestEnv::default(); + + sandbox + .new_assert_cmd("network") + .arg("use") + .arg("testnet") + .assert() + .stderr(predicate::str::contains( + "The default network is set to `testnet`", + )) + .success(); + + sandbox + .new_assert_cmd("env") + .assert() + .stdout(predicate::str::contains("STELLAR_NETWORK=testnet")) + .success(); +} diff --git a/cmd/crates/soroban-test/tests/it/integration.rs b/cmd/crates/soroban-test/tests/it/integration.rs index 3e8521869..3ec0d61ed 100644 --- a/cmd/crates/soroban-test/tests/it/integration.rs +++ b/cmd/crates/soroban-test/tests/it/integration.rs @@ -1,9 +1,10 @@ mod bindings; +mod constructor; mod cookbook; mod custom_types; mod dotenv; -mod fund; mod hello_world; +mod keys; mod snapshot; mod tx; mod util; diff --git a/cmd/crates/soroban-test/tests/it/integration/constructor.rs b/cmd/crates/soroban-test/tests/it/integration/constructor.rs new file mode 100644 index 000000000..1919b56b0 --- /dev/null +++ b/cmd/crates/soroban-test/tests/it/integration/constructor.rs @@ -0,0 +1,73 @@ +use assert_cmd::Command; + +use soroban_cli::xdr::{ + self, CreateContractArgsV2, HostFunction, InvokeHostFunctionOp, Limits, OperationBody, ReadXdr, + Transaction, TransactionV1Envelope, +}; +use soroban_test::{AssertExt, TestEnv}; + +use super::util::CONSTRUCTOR; + +fn constructor_cmd(sandbox: &TestEnv, value: u32, arg: &str) -> Command { + let mut cmd = sandbox.new_assert_cmd("contract"); + cmd.arg("deploy") + .arg("--wasm") + .arg(CONSTRUCTOR.path()) + .arg("--alias=init"); + if !arg.is_empty() { + cmd.arg(arg); + } + cmd.arg("--").arg("--counter").arg(value.to_string()); + cmd +} + +#[tokio::test] +async fn deploy_constructor_contract() { + let sandbox = TestEnv::new(); + let value = 100; + let build = constructor_cmd(&sandbox, value, "--build-only") + .assert() + .stdout_as_str(); + let tx = xdr::TransactionEnvelope::from_xdr_base64(&build, Limits::none()).unwrap(); + let ops = if let xdr::TransactionEnvelope::Tx(TransactionV1Envelope { + tx: Transaction { operations, .. }, + .. + }) = tx + { + operations + } else { + panic!() + } + .to_vec(); + let first = ops.first().unwrap(); + let args = match first { + xdr::Operation { + body: + OperationBody::InvokeHostFunction(InvokeHostFunctionOp { + host_function: + HostFunction::CreateContractV2(CreateContractArgsV2 { + constructor_args, .. + }), + .. + }), + .. + } => constructor_args, + _ => panic!("expected invoke host function with create contract v2"), + } + .to_vec(); + + match args.first().unwrap() { + xdr::ScVal::U32(u32) => assert_eq!(*u32, value), + _ => panic!("Expected U32"), + } + + constructor_cmd(&sandbox, value, "").assert().success(); + + let res = sandbox + .new_assert_cmd("contract") + .args(["invoke", "--id=init", "--", "counter"]) + .assert() + .success() + .stdout_as_str(); + assert_eq!(res.trim(), value.to_string()); +} diff --git a/cmd/crates/soroban-test/tests/it/integration/fund.rs b/cmd/crates/soroban-test/tests/it/integration/fund.rs deleted file mode 100644 index 263775412..000000000 --- a/cmd/crates/soroban-test/tests/it/integration/fund.rs +++ /dev/null @@ -1,23 +0,0 @@ -use soroban_test::TestEnv; - -#[tokio::test] -#[allow(clippy::too_many_lines)] -async fn fund() { - let sandbox = &TestEnv::new(); - sandbox - .new_assert_cmd("keys") - .arg("generate") - .arg("test") - .assert() - .success(); - sandbox - .new_assert_cmd("keys") - .arg("fund") - .arg("test") - .assert() - // Don't expect error if friendbot indicated that the account is - // already fully funded to the starting balance, because the - // user's goal is to get funded, and the account is funded - // so it is success much the same. - .success(); -} diff --git a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs index 767c9a07c..fd38c2012 100644 --- a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs +++ b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs @@ -20,9 +20,7 @@ async fn invoke_view_with_non_existent_source_account() { let id = deploy_hello(sandbox).await; let world = "world"; let mut cmd = hello_world_cmd(&id, world); - cmd.config.source_account = Address::default(); - cmd.is_view = true; - let res = sandbox.run_cmd_with(cmd, "test").await.unwrap(); + let res = sandbox.run_cmd_with(cmd, "").await.unwrap(); assert_eq!(res, TxnResult::Res(format!(r#"["Hello",{world:?}]"#))); } @@ -74,7 +72,7 @@ async fn invoke() { .assert() .stdout_as_str(); let dir = sandbox.dir(); - let seed_phrase = std::fs::read_to_string(dir.join(".soroban/identity/test.toml")).unwrap(); + let seed_phrase = std::fs::read_to_string(dir.join(".stellar/identity/test.toml")).unwrap(); let s = toml::from_str::(&seed_phrase).unwrap(); let secret::Secret::SeedPhrase { seed_phrase } = s else { panic!("Expected seed phrase") @@ -98,7 +96,7 @@ async fn invoke() { .arg("--id") .arg(id) .assert() - .stdout(predicates::str::contains(id).not()) + .stdout(predicates::str::contains(id)) .success(); invoke_hello_world_with_lib(sandbox, id).await; let config_locator = locator::Args { @@ -113,7 +111,7 @@ async fn invoke() { }, ) .unwrap(); - let sk_from_file = std::fs::read_to_string(dir.join(".soroban/identity/testone.toml")).unwrap(); + let sk_from_file = std::fs::read_to_string(dir.join(".stellar/identity/testone.toml")).unwrap(); assert_eq!(sk_from_file, format!("secret_key = \"{secret_key_1}\"\n")); let secret_key_1_readin = sandbox @@ -161,7 +159,7 @@ pub(crate) fn invoke_hello_world(sandbox: &TestEnv, id: &str) { fn hello_world_cmd(id: &str, arg: &str) -> contract::invoke::Cmd { contract::invoke::Cmd { - contract_id: id.to_string(), + contract_id: id.parse().unwrap(), slop: vec!["hello".into(), format!("--world={arg}").into()], ..Default::default() } diff --git a/cmd/crates/soroban-test/tests/it/integration/keys.rs b/cmd/crates/soroban-test/tests/it/integration/keys.rs new file mode 100644 index 000000000..267a0b095 --- /dev/null +++ b/cmd/crates/soroban-test/tests/it/integration/keys.rs @@ -0,0 +1,76 @@ +use predicates::prelude::predicate; +use soroban_test::AssertExt; +use soroban_test::TestEnv; + +fn pubkey_for_identity(sandbox: &TestEnv, name: &str) -> String { + let output = sandbox + .new_assert_cmd("keys") + .arg("address") + .arg(name) + .assert() + .stdout_as_str(); + return output; +} + +#[tokio::test] +#[allow(clippy::too_many_lines)] +async fn fund() { + let sandbox = &TestEnv::new(); + sandbox + .new_assert_cmd("keys") + .arg("generate") + .arg("test2") + .assert() + .success(); + sandbox + .new_assert_cmd("keys") + .arg("fund") + .arg("test2") + .assert() + // Don't expect error if friendbot indicated that the account is + // already fully funded to the starting balance, because the + // user's goal is to get funded, and the account is funded + // so it is success much the same. + .success(); +} + +#[tokio::test] +#[allow(clippy::too_many_lines)] +async fn overwrite_identity() { + let sandbox = &TestEnv::new(); + sandbox + .new_assert_cmd("keys") + .arg("generate") + .arg("test2") + .assert() + .success(); + + let initial_pubkey = sandbox + .new_assert_cmd("keys") + .arg("address") + .arg("test2") + .assert() + .stdout_as_str(); + + sandbox + .new_assert_cmd("keys") + .arg("generate") + .arg("test2") + .assert() + .stderr(predicate::str::contains( + "error: An identity with the name 'test2' already exists", + )); + + assert_eq!(initial_pubkey, pubkey_for_identity(&sandbox, "test2")); + + sandbox + .new_assert_cmd("keys") + .arg("generate") + .arg("test2") + .arg("--overwrite") + .assert() + .stderr(predicate::str::contains("Overwriting identity 'test2'")) + .success(); + + assert_ne!(initial_pubkey, pubkey_for_identity(&sandbox, "test2")); +} diff --git a/cmd/crates/soroban-test/tests/it/integration/util.rs b/cmd/crates/soroban-test/tests/it/integration/util.rs index 438428e38..486b00a1b 100644 --- a/cmd/crates/soroban-test/tests/it/integration/util.rs +++ b/cmd/crates/soroban-test/tests/it/integration/util.rs @@ -6,6 +6,7 @@ use soroban_test::{TestEnv, Wasm}; use std::fmt::Display; pub const HELLO_WORLD: &Wasm = &Wasm::Custom("test-wasms", "test_hello_world"); +pub const CONSTRUCTOR: &Wasm = &Wasm::Custom("test-wasms", "test_constructor"); pub const CUSTOM_TYPES: &Wasm = &Wasm::Custom("test-wasms", "test_custom_types"); pub const CUSTOM_ACCOUNT: &Wasm = &Wasm::Custom("test-wasms", "test_custom_account"); pub const SWAP: &Wasm = &Wasm::Custom("test-wasms", "test_swap"); diff --git a/cmd/crates/soroban-test/tests/it/plugin.rs b/cmd/crates/soroban-test/tests/it/plugin.rs index 450902d65..5e5eabee9 100644 --- a/cmd/crates/soroban-test/tests/it/plugin.rs +++ b/cmd/crates/soroban-test/tests/it/plugin.rs @@ -1,7 +1,7 @@ /* -This function calls the soroban executable via cargo and checks that the output +This function calls the stellar executable via cargo and checks that the output is correct. The PATH environment variable is set to include the target/bin -directory, so that the soroban executable can be found. +directory, so that the stellar executable can be found. */ use std::{ffi::OsString, path::PathBuf}; @@ -11,7 +11,7 @@ fn soroban_hello() { // Add the target/bin directory to the iterator of paths let paths = get_paths(); // Call soroban with the PATH variable set to include the target/bin directory - assert_cmd::Command::cargo_bin("soroban") + assert_cmd::Command::cargo_bin("stellar") .unwrap_or_else(|_| assert_cmd::Command::new("soroban")) .arg("hello") .env("PATH", &paths) @@ -19,23 +19,37 @@ fn soroban_hello() { .stdout("Hello, world!\n"); } +#[test] +fn stellar_bye() { + // Add the target/bin directory to the iterator of paths + let paths = get_paths(); + // Call soroban with the PATH variable set to include the target/bin directory + assert_cmd::Command::cargo_bin("stellar") + .unwrap_or_else(|_| assert_cmd::Command::new("stellar")) + .arg("bye") + .env("PATH", &paths) + .assert() + .stdout("Bye, world!\n"); +} + #[test] fn list() { // Call `soroban --list` with the PATH variable set to include the target/bin directory - assert_cmd::Command::cargo_bin("soroban") - .unwrap_or_else(|_| assert_cmd::Command::new("soroban")) + assert_cmd::Command::cargo_bin("stellar") + .unwrap_or_else(|_| assert_cmd::Command::new("stellar")) .arg("--list") .env("PATH", get_paths()) .assert() - .stdout(predicates::str::contains("hello")); + .stdout(predicates::str::contains("hello")) + .stdout(predicates::str::contains("bye")); } #[test] #[cfg(not(unix))] fn has_no_path() { // Call soroban with the PATH variable set to include just target/bin directory - assert_cmd::Command::cargo_bin("soroban") - .unwrap_or_else(|_| assert_cmd::Command::new("soroban")) + assert_cmd::Command::cargo_bin("stellar") + .unwrap_or_else(|_| assert_cmd::Command::new("stellar")) .arg("hello") .env("PATH", target_bin()) .assert() @@ -45,8 +59,8 @@ fn has_no_path() { #[test] fn has_no_path_failure() { // Call soroban with the PATH variable set to include just target/bin directory - assert_cmd::Command::cargo_bin("soroban") - .unwrap_or_else(|_| assert_cmd::Command::new("soroban")) + assert_cmd::Command::cargo_bin("stellar") + .unwrap_or_else(|_| assert_cmd::Command::new("stellar")) .arg("hello") .assert() .stderr(predicates::str::contains("error: no such command: `hello`")); diff --git a/cmd/crates/stellar-ledger/Cargo.toml b/cmd/crates/stellar-ledger/Cargo.toml index abf73c375..b3d6318a4 100644 --- a/cmd/crates/stellar-ledger/Cargo.toml +++ b/cmd/crates/stellar-ledger/Cargo.toml @@ -10,26 +10,31 @@ version.workspace = true edition = "2021" rust-version.workspace = true +# This crate has not yet ever been published. Skip publishing until these +# security issues are addressed: +# https://github.com/stellar/stellar-cli/issues/1706 +publish = false + [dependencies] soroban-spec = { workspace = true } thiserror = "1.0.32" serde = "1.0.82" serde_derive = "1.0.82" serde_json = "1.0.82" -sha2 = "0.9.9" +sha2 = { workspace = true } ed25519-dalek = { workspace = true } stellar-strkey = { workspace = true } ledger-transport-hid = "0.10.0" ledger-transport = "0.10.0" sep5.workspace = true -slip10 = "0.4.3" +slip10 = { package = "slipped10", version = "0.4.6" } tracing = { workspace = true } hex.workspace = true byteorder = "1.5.0" bollard = { workspace = true } home = "0.5.9" tokio = { version = "1", features = ["full"] } -reqwest = { version = "0.11", features = ["json"] } +reqwest = { workspace = true, features = ["json"] } soroban-rpc.workspace = true phf = { version = "0.11.2", features = ["macros"] } futures = "0.3.30" @@ -47,7 +52,7 @@ once_cell = "1.19.0" pretty_assertions = "1.2.1" serial_test = "3.0.0" httpmock = "0.7.0-rc.1" -test-case = "*" +test-case = "3.3.1" testcontainers = "0.20.1" diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 0cc12e74f..2f2a26255 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/stellar/stellar-cli" authors = ["Stellar Development Foundation "] license = "Apache-2.0" readme = "README.md" -version = "21.5.0" +version.workspace = true edition = "2021" rust-version.workspace = true autobins = false @@ -83,18 +83,17 @@ reqwest = { version = "0.12.7", default-features = false, features = [ jsonrpsee-http-client = "0.20.1" jsonrpsee-core = "0.20.1" regex = "1.6.0" -wasm-opt = { version = "0.114.0", optional = true } +wasm-opt = { version = "0.116.1", optional = true } chrono = { version = "0.4.27", features = ["serde"] } rpassword = "7.2.0" -dirs = "4.0.0" -toml = "0.5.9" +toml = { workspace = true } itertools = "0.10.5" shlex = "1.1.0" sep5 = { workspace = true } ethnum = { workspace = true } clap-markdown = { version = "0.1.4", optional = true } which = { workspace = true, features = ["regex"] } -strsim = "0.10.0" +strsim = "0.11.1" heck = "0.5.0" tracing = { workspace = true } tracing-appender = { workspace = true } @@ -107,9 +106,9 @@ ulid = { workspace = true, features = ["serde"] } strum = "0.17.1" strum_macros = "0.17.1" async-compression = { version = "0.4.12", features = ["tokio", "gzip"] } - +shell-escape = "0.1.5" tempfile = "3.8.1" -toml_edit = "0.21.0" +toml_edit = { workspace = true } rust-embed = { version = "8.2.0", features = ["debug-embed"] } bollard = { workspace = true } futures-util = "0.3.30" @@ -124,9 +123,10 @@ glob = "0.3.1" fqdn = "0.3.12" open = "5.3.0" url = "2.5.2" +wasm-gen = "0.1.4" [build-dependencies] -crate-git-revision = "0.0.4" +crate-git-revision = "0.0.6" serde.workspace = true thiserror.workspace = true @@ -134,6 +134,6 @@ thiserror.workspace = true [dev-dependencies] assert_cmd = "2.0.4" assert_fs = "1.0.7" -predicates = "2.1.5" +predicates = { workspace = true } walkdir = "2.5.0" mockito = "1.5.0" diff --git a/cmd/soroban-cli/src/cli.rs b/cmd/soroban-cli/src/cli.rs index 5470562db..76c5c3194 100644 --- a/cmd/soroban-cli/src/cli.rs +++ b/cmd/soroban-cli/src/cli.rs @@ -2,8 +2,10 @@ use clap::CommandFactory; use dotenvy::dotenv; use tracing_subscriber::{fmt, EnvFilter}; +use crate::config::Config; +use crate::print::Print; use crate::upgrade_check::upgrade_check; -use crate::{commands, print, Root}; +use crate::{commands, Root}; #[tokio::main] pub async fn main() { @@ -32,6 +34,8 @@ pub async fn main() { } } + set_env_from_config(); + let mut root = Root::new().unwrap_or_else(|e| match e { commands::Error::Clap(e) => { let mut cmd = Root::command(); @@ -42,6 +46,7 @@ pub async fn main() { std::process::exit(1); } }); + // Now use root to setup the logger if let Some(level) = root.global_args.log_level() { let mut e_filter = EnvFilter::from_default_env() @@ -78,9 +83,33 @@ pub async fn main() { upgrade_check(root.global_args.quiet).await; }); - let printer = print::Print::new(root.global_args.quiet); + let printer = Print::new(root.global_args.quiet); if let Err(e) = root.run().await { printer.errorln(format!("error: {e}")); std::process::exit(1); } } + +// Load ~/.config/stellar/config.toml defaults as env vars. +fn set_env_from_config() { + if let Ok(config) = Config::new() { + set_env_value_from_config("STELLAR_ACCOUNT", config.defaults.identity); + set_env_value_from_config("STELLAR_NETWORK", config.defaults.network); + } +} + +// Set an env var from a config file if the env var is not already set. +// Additionally, a `$NAME_SOURCE` variant will be set, which allows +// `stellar env` to properly identity the source. +fn set_env_value_from_config(name: &str, value: Option) { + let Some(value) = value else { + return; + }; + + std::env::remove_var(format!("{name}_SOURCE")); + + if std::env::var(name).is_err() { + std::env::set_var(name, value); + std::env::set_var(format!("{name}_SOURCE"), "use"); + } +} diff --git a/cmd/soroban-cli/src/commands/network/container/logs.rs b/cmd/soroban-cli/src/commands/container/logs.rs similarity index 94% rename from cmd/soroban-cli/src/commands/network/container/logs.rs rename to cmd/soroban-cli/src/commands/container/logs.rs index aaccffdde..85824b06c 100644 --- a/cmd/soroban-cli/src/commands/network/container/logs.rs +++ b/cmd/soroban-cli/src/commands/container/logs.rs @@ -1,7 +1,7 @@ use futures_util::TryStreamExt; use crate::{ - commands::{global, network::container::shared::Error as ConnectionError}, + commands::{container::shared::Error as ConnectionError, global}, print, }; diff --git a/cmd/soroban-cli/src/commands/network/container.rs b/cmd/soroban-cli/src/commands/container/mod.rs similarity index 91% rename from cmd/soroban-cli/src/commands/network/container.rs rename to cmd/soroban-cli/src/commands/container/mod.rs index d14dc72e4..08203095d 100644 --- a/cmd/soroban-cli/src/commands/network/container.rs +++ b/cmd/soroban-cli/src/commands/container/mod.rs @@ -16,13 +16,13 @@ pub enum Cmd { Logs(logs::Cmd), /// Start a container running a Stellar node, RPC, API, and friendbot (faucet). /// - /// `stellar network container start NETWORK [OPTIONS]` + /// `stellar container start NETWORK [OPTIONS]` /// /// By default, when starting a testnet container, without any optional arguments, it will run the equivalent of the following docker command: /// /// `docker run --rm -p 8000:8000 --name stellar stellar/quickstart:testing --testnet --enable rpc,horizon` Start(start::Cmd), - /// Stop a network container started with `network container start`. + /// Stop a network container started with `stellar container start`. Stop(stop::Cmd), } diff --git a/cmd/soroban-cli/src/commands/network/container/shared.rs b/cmd/soroban-cli/src/commands/container/shared.rs similarity index 100% rename from cmd/soroban-cli/src/commands/network/container/shared.rs rename to cmd/soroban-cli/src/commands/container/shared.rs diff --git a/cmd/soroban-cli/src/commands/network/container/start.rs b/cmd/soroban-cli/src/commands/container/start.rs similarity index 99% rename from cmd/soroban-cli/src/commands/network/container/start.rs rename to cmd/soroban-cli/src/commands/container/start.rs index 2f16d3c1e..6c5a5c291 100644 --- a/cmd/soroban-cli/src/commands/network/container/start.rs +++ b/cmd/soroban-cli/src/commands/container/start.rs @@ -9,8 +9,8 @@ use futures_util::TryStreamExt; use crate::{ commands::{ + container::shared::{Error as ConnectionError, Network}, global, - network::container::shared::{Error as ConnectionError, Network}, }, print, }; diff --git a/cmd/soroban-cli/src/commands/network/container/stop.rs b/cmd/soroban-cli/src/commands/container/stop.rs similarity index 95% rename from cmd/soroban-cli/src/commands/network/container/stop.rs rename to cmd/soroban-cli/src/commands/container/stop.rs index 77f674c46..6211c629f 100644 --- a/cmd/soroban-cli/src/commands/network/container/stop.rs +++ b/cmd/soroban-cli/src/commands/container/stop.rs @@ -1,5 +1,5 @@ use crate::{ - commands::{global, network::container::shared::Error as BollardConnectionError}, + commands::{container::shared::Error as BollardConnectionError, global}, print, }; diff --git a/cmd/soroban-cli/src/commands/contract/alias/add.rs b/cmd/soroban-cli/src/commands/contract/alias/add.rs index 5a7a17ddb..923ead5e9 100644 --- a/cmd/soroban-cli/src/commands/contract/alias/add.rs +++ b/cmd/soroban-cli/src/commands/contract/alias/add.rs @@ -13,9 +13,9 @@ pub struct Cmd { pub config_locator: locator::Args, #[command(flatten)] - network: network::Args, + pub network: network::Args, - /// The contract alias that will be removed. + /// The contract alias that will be used. pub alias: String, /// Overwrite the contract alias if it already exists. @@ -41,7 +41,7 @@ pub enum Error { AlreadyExist { alias: String, network_passphrase: String, - contract: String, + contract: stellar_strkey::Contract, }, } @@ -57,7 +57,7 @@ impl Cmd { .get_contract_id(&self.alias, network_passphrase)?; if let Some(contract) = contract { - if contract != self.contract_id.to_string() && !self.overwrite { + if contract != self.contract_id && !self.overwrite { return Err(Error::AlreadyExist { alias: alias.to_string(), network_passphrase: network_passphrase.to_string(), diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index 2eb3c2696..21fa2f383 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -62,6 +62,9 @@ pub fn build_host_function_parameters( } cmd.build(); let long_help = cmd.render_long_help(); + + // get_matches_from exits the process if `help`, `--help` or `-h`are passed in the slop + // see clap documentation for more info: https://github.com/clap-rs/clap/blob/v4.1.8/src/builder/command.rs#L631 let mut matches_ = cmd.get_matches_from(slop); let Some((function, matches_)) = &matches_.remove_subcommand() else { println!("{long_help}"); diff --git a/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs b/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs index 84e2b1762..679e757f4 100644 --- a/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs +++ b/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs @@ -5,14 +5,13 @@ use soroban_spec_tools::contract as contract_spec; use soroban_spec_typescript::{self as typescript, boilerplate::Project}; use stellar_strkey::DecodeError; +use crate::print::Print; use crate::wasm; use crate::{ commands::{contract::fetch, global, NetworkRunnable}, - config::{ - self, locator, - network::{self, Network}, - }, + config::{self, locator, network}, get_spec::{self, get_remote_contract_spec}, + xdr::{Hash, ScAddress}, }; #[derive(Parser, Debug, Clone)] @@ -83,20 +82,27 @@ impl NetworkRunnable for Cmd { global_args: Option<&global::Args>, config: Option<&config::Args>, ) -> Result<(), Error> { + let print = Print::new(global_args.is_some_and(|a| a.quiet)); + + let network = self.network.get(&self.locator).ok().unwrap_or_else(|| { + network::DEFAULTS + .get("testnet") + .expect("no network specified and testnet network not found") + .into() + }); + + let contract_id = self + .locator + .resolve_contract_id(&self.contract_id, &network.network_passphrase)? + .0; + let contract_address = ScAddress::Contract(Hash(contract_id)); + let spec = if let Some(wasm) = &self.wasm { + print.infoln("Loading contract spec from file..."); let wasm: wasm::Args = wasm.into(); wasm.parse()?.spec } else { - let network = config.map_or_else( - || self.network.get(&self.locator).map_err(Error::from), - |c| c.get_network().map_err(Error::from), - )?; - - let contract_id = self - .locator - .resolve_contract_id(&self.contract_id, &network.network_passphrase)? - .0; - + print.globeln(format!("Downloading contract spec: {contract_address}")); get_remote_contract_spec( &contract_id, &self.locator, @@ -119,16 +125,7 @@ impl NetworkRunnable for Cmd { } std::fs::create_dir_all(&self.output_dir)?; let p: Project = self.output_dir.clone().try_into()?; - let Network { - rpc_url, - network_passphrase, - .. - } = self.network.get(&self.locator).ok().unwrap_or_else(|| { - network::DEFAULTS - .get("futurenet") - .expect("why did we remove the default futurenet network?") - .into() - }); + print.infoln(format!("Network: {}", network.network_passphrase)); let absolute_path = self.output_dir.canonicalize()?; let file_name = absolute_path .file_name() @@ -136,24 +133,19 @@ impl NetworkRunnable for Cmd { let contract_name = &file_name .to_str() .ok_or_else(|| Error::NotUtf8(file_name.to_os_string()))?; + print.infoln(format!("Embedding contract address: {contract_address}")); p.init( contract_name, - &self.contract_id, - &rpc_url, - &network_passphrase, + &contract_address.to_string(), + &network.rpc_url, + &network.network_passphrase, &spec, )?; - std::process::Command::new("npm") - .arg("install") - .current_dir(&self.output_dir) - .spawn()? - .wait()?; - std::process::Command::new("npm") - .arg("run") - .arg("build") - .current_dir(&self.output_dir) - .spawn()? - .wait()?; + print.checkln("Generated!"); + print.infoln(format!( + "Run \"npm install && npm run build\" in {:?} to build the JavaScript NPM package.", + self.output_dir + )); Ok(()) } } diff --git a/cmd/soroban-cli/src/commands/contract/build.rs b/cmd/soroban-cli/src/commands/contract/build.rs index c71327a38..ac1aa1302 100644 --- a/cmd/soroban-cli/src/commands/contract/build.rs +++ b/cmd/soroban-cli/src/commands/contract/build.rs @@ -1,6 +1,8 @@ +use cargo_metadata::{Metadata, MetadataCommand, Package}; use clap::Parser; use itertools::Itertools; use std::{ + borrow::Cow, collections::HashSet, env, ffi::OsStr, @@ -9,8 +11,9 @@ use std::{ path::{self, Path, PathBuf}, process::{Command, ExitStatus, Stdio}, }; +use stellar_xdr::curr::{Limits, ScMetaEntry, ScMetaV0, StringM, WriteXdr}; -use cargo_metadata::{Metadata, MetadataCommand, Package}; +use crate::{commands::global, print::Print}; /// Build a contract from source /// @@ -62,6 +65,20 @@ pub struct Cmd { /// Print commands to build without executing them #[arg(long, conflicts_with = "out_dir", help_heading = "Other")] pub print_commands_only: bool, + /// Add key-value to contract meta (adds the meta to the `contractmetav0` custom section) + #[arg(long, num_args=1, value_parser=parse_meta_arg, action=clap::ArgAction::Append, help_heading = "Metadata")] + pub meta: Vec<(String, String)>, +} + +fn parse_meta_arg(s: &str) -> Result<(String, String), Error> { + let parts = s.splitn(2, '='); + + let (key, value) = parts + .map(str::trim) + .next_tuple() + .ok_or_else(|| Error::MetaArg("must be in the form 'key=value'".to_string()))?; + + Ok((key.to_string(), value.to_string())) } #[derive(thiserror::Error, Debug)] @@ -82,10 +99,23 @@ pub enum Error { CopyingWasmFile(io::Error), #[error("getting the current directory: {0}")] GettingCurrentDir(io::Error), + #[error("retreiving CARGO_HOME: {0}")] + CargoHome(io::Error), + #[error("reading wasm file: {0}")] + ReadingWasmFile(io::Error), + #[error("writing wasm file: {0}")] + WritingWasmFile(io::Error), + #[error("invalid meta entry: {0}")] + MetaArg(String), } +const WASM_TARGET: &str = "wasm32-unknown-unknown"; +const META_CUSTOM_SECTION_NAME: &str = "contractmetav0"; + impl Cmd { - pub fn run(&self) -> Result<(), Error> { + pub fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let print = Print::new(global_args.quiet); + let working_dir = env::current_dir().map_err(Error::GettingCurrentDir)?; let metadata = self.metadata()?; @@ -111,7 +141,7 @@ impl Cmd { manifest_path.to_string_lossy() )); cmd.arg("--crate-type=cdylib"); - cmd.arg("--target=wasm32-unknown-unknown"); + cmd.arg(format!("--target={WASM_TARGET}")); if self.profile == "release" { cmd.arg("--release"); } else { @@ -131,28 +161,46 @@ impl Cmd { cmd.arg(format!("--features={activate}")); } } - let cmd_str = format!( - "cargo {}", - cmd.get_args().map(OsStr::to_string_lossy).join(" ") + + if let Some(rustflags) = make_rustflags_to_remap_absolute_paths(&print)? { + cmd.env("CARGO_BUILD_RUSTFLAGS", rustflags); + } + + let mut cmd_str_parts = Vec::::new(); + cmd_str_parts.extend(cmd.get_envs().map(|(key, val)| { + format!( + "{}={}", + key.to_string_lossy(), + shell_escape::escape(val.unwrap_or_default().to_string_lossy()) + ) + })); + cmd_str_parts.push("cargo".to_string()); + cmd_str_parts.extend( + cmd.get_args() + .map(OsStr::to_string_lossy) + .map(Cow::into_owned), ); + let cmd_str = cmd_str_parts.join(" "); if self.print_commands_only { println!("{cmd_str}"); } else { - eprintln!("{cmd_str}"); + print.infoln(cmd_str); let status = cmd.status().map_err(Error::CargoCmd)?; if !status.success() { return Err(Error::Exit(status)); } + let file = format!("{}.wasm", p.name.replace('-', "_")); + let target_file_path = Path::new(target_dir) + .join(WASM_TARGET) + .join(&self.profile) + .join(&file); + + self.handle_contract_metadata_args(&target_file_path)?; + if let Some(out_dir) = &self.out_dir { fs::create_dir_all(out_dir).map_err(Error::CreatingOutDir)?; - - let file = format!("{}.wasm", p.name.replace('-', "_")); - let target_file_path = Path::new(target_dir) - .join("wasm32-unknown-unknown") - .join(&self.profile) - .join(&file); let out_file_path = Path::new(out_dir).join(&file); fs::copy(target_file_path, out_file_path).map_err(Error::CopyingWasmFile)?; } @@ -228,4 +276,130 @@ impl Cmd { // the output. cmd.exec() } + + fn handle_contract_metadata_args(&self, target_file_path: &PathBuf) -> Result<(), Error> { + if self.meta.is_empty() { + return Ok(()); + } + + let mut wasm_bytes = fs::read(target_file_path).map_err(Error::ReadingWasmFile)?; + + for (k, v) in self.meta.clone() { + let key: StringM = k + .clone() + .try_into() + .map_err(|e| Error::MetaArg(format!("{k} is an invalid metadata key: {e}")))?; + + let val: StringM = v + .clone() + .try_into() + .map_err(|e| Error::MetaArg(format!("{v} is an invalid metadata value: {e}")))?; + let meta_entry = ScMetaEntry::ScMetaV0(ScMetaV0 { key, val }); + let xdr: Vec = meta_entry + .to_xdr(Limits::none()) + .map_err(|e| Error::MetaArg(format!("failed to encode metadata entry: {e}")))?; + + wasm_gen::write_custom_section(&mut wasm_bytes, META_CUSTOM_SECTION_NAME, &xdr); + } + + fs::write(target_file_path, wasm_bytes).map_err(Error::WritingWasmFile) + } +} + +/// Configure cargo/rustc to replace absolute paths in panic messages / debuginfo +/// with relative paths. +/// +/// This is required for reproducible builds. +/// +/// This works for paths to crates in the registry. The compiler already does +/// something similar for standard library paths and local paths. It may not +/// work for crates that come from other sources, including the standard library +/// compiled from source, though it may be possible to accomodate such cases in +/// the future. +/// +/// This in theory breaks the ability of debuggers to find source code, but +/// since we are only targetting wasm, which is not typically run in a debugger, +/// and stellar-cli only compiles contracts in release mode, the impact is on +/// debugging is expected to be minimal. +/// +/// This works by setting the `CARGO_BUILD_RUSTFLAGS` environment variable, +/// with appropriate `--remap-path-prefix` option. It preserves the values of an +/// existing `CARGO_BUILD_RUSTFLAGS` environment variable. +/// +/// This must be done some via some variation of `RUSTFLAGS` and not as +/// arguments to `cargo rustc` because the latter only applies to the crate +/// directly being compiled, while `RUSTFLAGS` applies to all crates, including +/// dependencies. +/// +/// `CARGO_BUILD_RUSTFLAGS` is an alias for the `build.rustflags` configuration +/// variable. Cargo automatically merges the contents of the environment variable +/// and the variables from config files; and `build.rustflags` has the lowest +/// priority of all the variations of rustflags that Cargo accepts. And because +/// we merge our values with an existing `CARGO_BUILD_RUSTFLAGS`, +/// our setting of this environment variable should not interfere with the +/// user's ability to set rustflags in any way they want, but it does mean +/// that if the user sets a higher-priority rustflags that our path remapping +/// will be ignored. +/// +/// The major downside of using `CARGO_BUILD_RUSTFLAGS` is that it is whitespace +/// separated, which means we cannot support paths with spaces. If we encounter +/// such paths we will emit a warning. Spaces could be accomodated by using +/// `CARGO_ENCODED_RUSTFLAGS`, but that has high precedence over other rustflags, +/// so we could be interfering with the user's own use of rustflags. There is +/// no "encoded" variant of `CARGO_BUILD_RUSTFLAGS` at time of writing. +/// +/// This assumes that paths are Unicode and that any existing `CARGO_BUILD_RUSTFLAGS` +/// variables are Unicode. Non-Unicode paths will fail to correctly perform the +/// the absolute path replacement. Non-Unicode `CARGO_BUILD_RUSTFLAGS` will result in the +/// existing rustflags being ignored, which is also the behavior of +/// Cargo itself. +fn make_rustflags_to_remap_absolute_paths(print: &Print) -> Result, Error> { + let cargo_home = home::cargo_home().map_err(Error::CargoHome)?; + let cargo_home = format!("{}", cargo_home.display()); + + if cargo_home.find(|c: char| c.is_whitespace()).is_some() { + print.warnln("Cargo home directory contains whitespace. Dependency paths will not be remapped; builds may not be reproducible."); + return Ok(None); + } + + if env::var("RUSTFLAGS").is_ok() { + print.warnln("`RUSTFLAGS` set. Dependency paths will not be remapped; builds may not be reproducible."); + return Ok(None); + } + + if env::var("CARGO_ENCODED_RUSTFLAGS").is_ok() { + print.warnln("`CARGO_ENCODED_RUSTFLAGS` set. Dependency paths will not be remapped; builds may not be reproducible."); + return Ok(None); + } + + if env::var("TARGET_wasm32-unknown-unknown_RUSTFLAGS").is_ok() { + print.warnln("`TARGET_wasm32-unknown-unknown_RUSTFLAGS` set. Dependency paths will not be remapped; builds may not be reproducible."); + return Ok(None); + } + + let registry_prefix = format!("{cargo_home}/registry/src/"); + let new_rustflag = format!("--remap-path-prefix={registry_prefix}="); + + let mut rustflags = get_rustflags().unwrap_or_default(); + rustflags.push(new_rustflag); + + let rustflags = rustflags.join(" "); + + Ok(Some(rustflags)) +} + +/// Get any existing `CARGO_BUILD_RUSTFLAGS`, split on whitespace. +/// +/// This conveniently ignores non-Unicode values, as does Cargo. +fn get_rustflags() -> Option> { + if let Ok(a) = env::var("CARGO_BUILD_RUSTFLAGS") { + let args = a + .split_whitespace() + .map(str::trim) + .filter(|s| !s.is_empty()) + .map(str::to_string); + return Some(args.collect()); + } + + None } diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs index 263908521..04a0380ed 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs @@ -93,11 +93,11 @@ impl NetworkRunnable for Cmd { .verify_network_passphrase(Some(&network.network_passphrase)) .await?; let source_account = config.source_account()?; - // Get the account sequence number - let public_strkey = source_account.to_string(); // TODO: use symbols for the method names (both here and in serve) - let account_details = client.get_account(&public_strkey).await?; + let account_details = client + .get_account(&source_account.clone().to_string()) + .await?; let sequence: i64 = account_details.seq_num.into(); let network_passphrase = &network.network_passphrase; let contract_id = contract_id_hash_from_asset(asset, network_passphrase); @@ -110,12 +110,12 @@ impl NetworkRunnable for Cmd { source_account, )?; if self.fee.build_only { - return Ok(TxnResult::Txn(tx)); + return Ok(TxnResult::Txn(Box::new(tx))); } let txn = simulate_and_assemble_transaction(&client, &tx).await?; let txn = self.fee.apply_to_assembled_txn(txn).transaction().clone(); if self.fee.sim_only { - return Ok(TxnResult::Txn(txn)); + return Ok(TxnResult::Txn(Box::new(txn))); } let get_txn_resp = client .send_transaction_polling(&self.config.sign_with_local_key(txn).await?) diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 21c685b93..1d85832b0 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -1,33 +1,38 @@ use std::array::TryFromSliceError; +use std::ffi::OsString; use std::fmt::Debug; use std::num::ParseIntError; use crate::xdr::{ AccountId, ContractExecutable, ContractIdPreimage, ContractIdPreimageFromAddress, - CreateContractArgs, Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, Limits, Memo, - MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, ScAddress, SequenceNumber, - Transaction, TransactionExt, Uint256, VecM, WriteXdr, + CreateContractArgs, CreateContractArgsV2, Error as XdrError, Hash, HostFunction, + InvokeContractArgs, InvokeHostFunctionOp, Limits, Memo, MuxedAccount, Operation, OperationBody, + Preconditions, PublicKey, ScAddress, SequenceNumber, Transaction, TransactionExt, Uint256, + VecM, WriteXdr, }; use clap::{arg, command, Parser}; use rand::Rng; use regex::Regex; +use soroban_spec_tools::contract as contract_spec; + use crate::{ assembled::simulate_and_assemble_transaction, - commands::{contract::install, HEADING_RPC}, - config::{self, data, locator, network}, - rpc, utils, wasm, -}; -use crate::{ commands::{ - contract::{self, id::wasm::get_contract_id}, + contract::{self, arg_parsing, id::wasm::get_contract_id, install}, global, txn_result::{TxnEnvelopeResult, TxnResult}, - NetworkRunnable, + NetworkRunnable, HEADING_RPC, }, + config::{self, data, locator, network}, print::Print, + rpc, + utils::{self, rpc::get_remote_wasm_from_hash}, + wasm, }; +pub const CONSTRUCTOR_FUNCTION_NAME: &str = "__constructor"; + #[derive(Parser, Debug, Clone)] #[command(group( clap::ArgGroup::new("wasm_src") @@ -60,6 +65,9 @@ pub struct Cmd { /// configuration without asking for confirmation. #[arg(long, value_parser = clap::builder::ValueParser::new(alias_validator))] pub alias: Option, + /// If provided, will be passed to the contract's `__constructor` function with provided arguments for that function as `--arg-name value` + #[arg(last = true, id = "CONTRACT_CONSTRUCTOR_ARGS")] + pub slop: Vec, } #[derive(thiserror::Error, Debug)] @@ -110,6 +118,10 @@ pub enum Error { InvalidAliasFormat { alias: String }, #[error(transparent)] Locator(#[from] locator::Error), + #[error(transparent)] + ContractSpec(#[from] contract_spec::Error), + #[error(transparent)] + ArgParse(#[from] arg_parsing::Error), #[error("Only ed25519 accounts are allowed")] OnlyEd25519AccountsAllowed, } @@ -157,6 +169,7 @@ impl NetworkRunnable for Cmd { type Error = Error; type Result = TxnResult; + #[allow(clippy::too_many_lines)] async fn run_against_rpc_server( &self, global_args: Option<&global::Args>, @@ -211,22 +224,55 @@ impl NetworkRunnable for Cmd { client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; + let MuxedAccount::Ed25519(bytes) = config.source_account()? else { return Err(Error::OnlyEd25519AccountsAllowed); }; + let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(bytes)); + let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress { + address: ScAddress::Account(source_account.clone()), + salt: Uint256(salt), + }); + let contract_id = + get_contract_id(contract_id_preimage.clone(), &network.network_passphrase)?; + let raw_wasm = if let Some(wasm) = self.wasm.as_ref() { + wasm::Args { wasm: wasm.clone() }.read()? + } else { + get_remote_wasm_from_hash(&client, &wasm_hash).await? + }; + let entries = soroban_spec_tools::contract::Spec::new(&raw_wasm)?.spec; + let res = soroban_spec_tools::Spec::new(entries.clone()); + let constructor_params = if let Ok(func) = res.find_function(CONSTRUCTOR_FUNCTION_NAME) { + if func.inputs.len() == 0 { + None + } else { + let mut slop = vec![OsString::from(CONSTRUCTOR_FUNCTION_NAME)]; + slop.extend_from_slice(&self.slop); + Some( + arg_parsing::build_host_function_parameters( + &stellar_strkey::Contract(contract_id.0), + &slop, + &entries, + config, + )? + .2, + ) + } + } else { + None + }; - let key = stellar_strkey::ed25519::PublicKey(bytes.into()); // Get the account sequence number - let account_details = client.get_account(&key.to_string()).await?; + let account_details = client.get_account(&source_account.to_string()).await?; let sequence: i64 = account_details.seq_num.into(); - let (txn, contract_id) = build_create_contract_tx( + let txn = Box::new(build_create_contract_tx( wasm_hash, sequence + 1, self.fee.fee, - &network.network_passphrase, - salt, - key, - )?; + source_account, + contract_id_preimage, + constructor_params.as_ref(), + )?); if self.fee.build_only { print.checkln("Transaction built!"); @@ -236,7 +282,7 @@ impl NetworkRunnable for Cmd { print.infoln("Simulating deploy transaction…"); let txn = simulate_and_assemble_transaction(&client, &txn).await?; - let txn = self.fee.apply_to_assembled_txn(txn).transaction().clone(); + let txn = Box::new(self.fee.apply_to_assembled_txn(txn).transaction().clone()); if self.fee.sim_only { print.checkln("Done!"); @@ -247,7 +293,7 @@ impl NetworkRunnable for Cmd { print.log_transaction(&txn, &network, true)?; let get_txn_resp = client - .send_transaction_polling(&config.sign_with_local_key(txn).await?) + .send_transaction_polling(&config.sign_with_local_key(*txn).await?) .await? .try_into()?; @@ -266,33 +312,39 @@ impl NetworkRunnable for Cmd { } fn build_create_contract_tx( - hash: Hash, + wasm_hash: Hash, sequence: i64, fee: u32, - network_passphrase: &str, - salt: [u8; 32], - key: stellar_strkey::ed25519::PublicKey, -) -> Result<(Transaction, stellar_strkey::Contract), Error> { - let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(key.0.into())); - - let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress { - address: ScAddress::Account(source_account), - salt: Uint256(salt), - }); - let contract_id = get_contract_id(contract_id_preimage.clone(), network_passphrase)?; - - let op = Operation { - source_account: None, - body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { - host_function: HostFunction::CreateContract(CreateContractArgs { - contract_id_preimage, - executable: ContractExecutable::Wasm(hash), + key: AccountId, + contract_id_preimage: ContractIdPreimage, + constructor_params: Option<&InvokeContractArgs>, +) -> Result { + let op = if let Some(InvokeContractArgs { args, .. }) = constructor_params { + Operation { + source_account: None, + body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { + host_function: HostFunction::CreateContractV2(CreateContractArgsV2 { + contract_id_preimage, + executable: ContractExecutable::Wasm(wasm_hash), + constructor_args: args.clone(), + }), + auth: VecM::default(), }), - auth: VecM::default(), - }), + } + } else { + Operation { + source_account: None, + body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { + host_function: HostFunction::CreateContract(CreateContractArgs { + contract_id_preimage, + executable: ContractExecutable::Wasm(wasm_hash), + }), + auth: VecM::default(), + }), + } }; let tx = Transaction { - source_account: MuxedAccount::Ed25519(key.0.into()), + source_account: key.into(), fee, seq_num: SequenceNumber(sequence), cond: Preconditions::None, @@ -301,7 +353,7 @@ fn build_create_contract_tx( ext: TransactionExt::V0, }; - Ok((tx, contract_id)) + Ok(tx) } #[cfg(test)] @@ -314,18 +366,26 @@ mod tests { .unwrap() .try_into() .unwrap(); + let salt = [0u8; 32]; + let key = + &utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") + .unwrap(); + let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(Uint256( + key.verifying_key().to_bytes(), + ))); + + let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress { + address: ScAddress::Account(source_account.clone()), + salt: Uint256(salt), + }); + let result = build_create_contract_tx( Hash(hash), 300, 1, - "Public Global Stellar Network ; September 2015", - [0u8; 32], - stellar_strkey::ed25519::PublicKey( - utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") - .unwrap() - .verifying_key() - .to_bytes(), - ), + source_account, + contract_id_preimage, + None, ); assert!(result.is_ok()); diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs index 24aac54c5..e56cbe166 100644 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ b/cmd/soroban-cli/src/commands/contract/extend.rs @@ -135,10 +135,12 @@ impl NetworkRunnable for Cmd { let extend_to = self.ledgers_to_extend(); // Get the account sequence number - let account_details = client.get_account(&source_account.to_string()).await?; + let account_details = client + .get_account(&source_account.clone().to_string()) + .await?; let sequence: i64 = account_details.seq_num.into(); - let tx = Transaction { + let tx = Box::new(Transaction { source_account, fee: self.fee.fee, seq_num: SequenceNumber(sequence + 1), @@ -165,7 +167,7 @@ impl NetworkRunnable for Cmd { }, resource_fee: 0, }), - }; + }); if self.fee.build_only { return Ok(TxnResult::Txn(tx)); } @@ -184,10 +186,7 @@ impl NetworkRunnable for Cmd { if !events.is_empty() { tracing::info!("Events:\n {events:#?}"); } - let meta = res - .result_meta - .as_ref() - .ok_or(Error::MissingOperationResult)?; + let meta = res.result_meta.ok_or(Error::MissingOperationResult)?; // The transaction from core will succeed regardless of whether it actually found & extended // the entry, so we have to inspect the result meta to tell if it worked or not. diff --git a/cmd/soroban-cli/src/commands/contract/fetch.rs b/cmd/soroban-cli/src/commands/contract/fetch.rs index 2714b1f07..31ed191ff 100644 --- a/cmd/soroban-cli/src/commands/contract/fetch.rs +++ b/cmd/soroban-cli/src/commands/contract/fetch.rs @@ -22,7 +22,7 @@ use crate::{ pub struct Cmd { /// Contract ID to fetch #[arg(long = "id", env = "STELLAR_CONTRACT_ID")] - pub contract_id: String, + pub contract_id: config::ContractAddress, /// Where to write output otherwise stdout is used #[arg(long, short = 'o')] pub out_file: Option, @@ -111,6 +111,12 @@ impl NetworkRunnable for Cmd { config: Option<&config::Args>, ) -> Result, Error> { let network = config.map_or_else(|| self.network(), |c| Ok(c.get_network()?))?; - return Ok(wasm::fetch_from_contract(&self.contract_id, &network, &self.locator).await?); + Ok(wasm::fetch_from_contract( + &self + .contract_id + .resolve_contract_id(&self.locator, &network.network_passphrase)?, + &network, + ) + .await?) } } diff --git a/cmd/soroban-cli/src/commands/contract/info/shared.rs b/cmd/soroban-cli/src/commands/contract/info/shared.rs index 0974632ae..33a95b607 100644 --- a/cmd/soroban-cli/src/commands/contract/info/shared.rs +++ b/cmd/soroban-cli/src/commands/contract/info/shared.rs @@ -1,13 +1,13 @@ use std::path::PathBuf; -use crate::xdr; use clap::arg; use crate::{ commands::contract::info::shared::Error::InvalidWasmHash, - config::{locator, network}, + config::{self, locator, network}, utils::rpc::get_remote_wasm_from_hash, wasm::{self, Error::ContractIsStellarAsset}, + xdr, }; #[derive(Debug, clap::Args, Clone, Default)] @@ -24,9 +24,9 @@ pub struct Args { /// Wasm hash to get the data for #[arg(long = "wasm-hash", group = "Source")] pub wasm_hash: Option, - /// Contract id to get the data for + /// Contract id or contract alias to get the data for #[arg(long = "id", env = "STELLAR_CONTRACT_ID", group = "Source")] - pub contract_id: Option, + pub contract_id: Option, #[command(flatten)] pub network: network::Args, #[command(flatten)] @@ -56,14 +56,21 @@ pub enum Error { InvalidWasmHash(String), #[error(transparent)] Rpc(#[from] soroban_rpc::Error), + #[error(transparent)] + Locator(#[from] locator::Error), } pub async fn fetch_wasm(args: &Args) -> Result>, Error> { - let network = &args.network.get(&args.locator)?; + // Check if a local WASM file path is provided + if let Some(path) = &args.wasm { + // Read the WASM file and return its contents + let wasm_bytes = wasm::Args { wasm: path.clone() }.read()?; + return Ok(Some(wasm_bytes)); + } - let wasm = if let Some(path) = &args.wasm { - wasm::Args { wasm: path.clone() }.read()? - } else if let Some(wasm_hash) = &args.wasm_hash { + // If no local wasm, then check for wasm_hash and fetch from the network + let network = &args.network.get(&args.locator)?; + let wasm = if let Some(wasm_hash) = &args.wasm_hash { let hash = hex::decode(wasm_hash) .map_err(|_| InvalidWasmHash(wasm_hash.clone()))? .try_into() @@ -79,7 +86,9 @@ pub async fn fetch_wasm(args: &Args) -> Result>, Error> { get_remote_wasm_from_hash(&client, &hash).await? } else if let Some(contract_id) = &args.contract_id { - let res = wasm::fetch_from_contract(contract_id, network, &args.locator).await; + let contract_id = + contract_id.resolve_contract_id(&args.locator, &network.network_passphrase)?; + let res = wasm::fetch_from_contract(&contract_id, network).await; if let Some(ContractIsStellarAsset) = res.as_ref().err() { return Ok(None); } diff --git a/cmd/soroban-cli/src/commands/contract/init.rs b/cmd/soroban-cli/src/commands/contract/init.rs index 96f28c64e..9147bacf0 100644 --- a/cmd/soroban-cli/src/commands/contract/init.rs +++ b/cmd/soroban-cli/src/commands/contract/init.rs @@ -275,7 +275,7 @@ mod tests { let contract_dir = project_dir.join("contracts").join(contract_name); let cargo_toml_path = contract_dir.as_path().join("Cargo.toml"); let cargo_toml_str = read_to_string(cargo_toml_path.clone()).unwrap(); - let doc = cargo_toml_str.parse::().unwrap(); + let doc: toml_edit::DocumentMut = cargo_toml_str.parse().unwrap(); assert!( doc.get("dependencies") .unwrap() diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index cd6e93b24..0a9ec856d 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -137,14 +137,16 @@ impl NetworkRunnable for Cmd { // Get the account sequence number let source_account = config.source_account()?; - let account_details = client.get_account(&source_account.to_string()).await?; + let account_details = client + .get_account(&source_account.clone().to_string()) + .await?; let sequence: i64 = account_details.seq_num.into(); let (tx_without_preflight, hash) = build_install_contract_code_tx(&contract, sequence + 1, self.fee.fee, &source_account)?; if self.fee.build_only { - return Ok(TxnResult::Txn(tx_without_preflight)); + return Ok(TxnResult::Txn(Box::new(tx_without_preflight))); } // Don't check whether the contract is already installed when the user @@ -184,7 +186,7 @@ impl NetworkRunnable for Cmd { print.infoln("Simulating install transaction…"); let txn = simulate_and_assemble_transaction(&client, &tx_without_preflight).await?; - let txn = self.fee.apply_to_assembled_txn(txn).transaction().clone(); + let txn = Box::new(self.fee.apply_to_assembled_txn(txn).transaction().clone()); if self.fee.sim_only { return Ok(TxnResult::Txn(txn)); @@ -193,7 +195,7 @@ impl NetworkRunnable for Cmd { print.globeln("Submitting install transaction…"); let txn_resp = client - .send_transaction_polling(&self.config.sign_with_local_key(txn).await?) + .send_transaction_polling(&self.config.sign_with_local_key(*txn).await?) .await?; if args.map_or(true, |a| !a.no_cache) { @@ -204,7 +206,7 @@ impl NetworkRunnable for Cmd { if let Some(TransactionResult { result: TransactionResultResult::TxInternalError, .. - }) = txn_resp.result.as_ref() + }) = txn_resp.result { // Now just need to restore it and don't have to install again restore::Cmd { diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 5d43f9bfa..c7b631343 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -7,7 +7,7 @@ use std::{fmt::Debug, fs, io}; use clap::{arg, command, Parser, ValueEnum}; -use soroban_rpc::{SimulateHostFunctionResult, SimulateTransactionResponse}; +use soroban_rpc::{Client, SimulateHostFunctionResult, SimulateTransactionResponse}; use soroban_spec::read::FromWasmError; use super::super::events; @@ -25,10 +25,10 @@ use crate::{ print, rpc, xdr::{ self, AccountEntry, AccountEntryExt, AccountId, ContractEvent, ContractEventType, - DiagnosticEvent, HostFunction, InvokeContractArgs, InvokeHostFunctionOp, LedgerEntryData, - Limits, Memo, MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, - ScSpecEntry, SequenceNumber, String32, StringM, Thresholds, Transaction, TransactionExt, - Uint256, VecM, WriteXdr, + DiagnosticEvent, HostFunction, InvokeContractArgs, InvokeHostFunctionOp, Limits, Memo, + MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, ScSpecEntry, + SequenceNumber, String32, StringM, Thresholds, Transaction, TransactionExt, Uint256, VecM, + WriteXdr, }, Pwd, }; @@ -40,7 +40,7 @@ use soroban_spec_tools::contract; pub struct Cmd { /// Contract ID to invoke #[arg(long = "id", env = "STELLAR_CONTRACT_ID")] - pub contract_id: String, + pub contract_id: config::ContractAddress, // For testing only #[arg(skip)] pub wasm: Option, @@ -93,8 +93,6 @@ pub enum Error { ParseIntError(#[from] ParseIntError), #[error(transparent)] Rpc(#[from] rpc::Error), - #[error("unexpected contract code data type: {0:?}")] - UnexpectedContractCodeDataType(LedgerEntryData), #[error("missing operation result")] MissingOperationResult, #[error("error loading signing key: {0}")] @@ -163,7 +161,7 @@ impl Cmd { .transpose() } - fn send(&self, sim_res: &SimulateTransactionResponse) -> Result { + fn should_send_tx(&self, sim_res: &SimulateTransactionResponse) -> Result { Ok(match self.send { Send::Default => { if self.is_view { @@ -179,6 +177,28 @@ impl Cmd { Send::Yes => ShouldSend::Yes, }) } + + // uses a default account to check if the tx should be sent after the simulation + async fn should_send_after_sim( + &self, + host_function_params: InvokeContractArgs, + rpc_client: Client, + ) -> Result { + let account_details = default_account_entry(); + let sequence: i64 = account_details.seq_num.into(); + let AccountId(PublicKey::PublicKeyTypeEd25519(account_id)) = account_details.account_id; + + let tx = build_invoke_contract_tx( + host_function_params.clone(), + sequence + 1, + self.fee.fee, + account_id, + )?; + let txn = simulate_and_assemble_transaction(&rpc_client, &tx).await?; + let txn = self.fee.apply_to_assembled_txn(txn); // do we need this part? + let sim_res = txn.sim_response(); + self.should_send_tx(sim_res) + } } #[async_trait::async_trait] @@ -195,9 +215,8 @@ impl NetworkRunnable for Cmd { let network = config.get_network()?; tracing::trace!(?network); let contract_id = self - .config - .locator - .resolve_contract_id(&self.contract_id, &network.network_passphrase)?; + .contract_id + .resolve_contract_id(&config.locator, &network.network_passphrase)?; let spec_entries = self.spec_entries()?; if let Some(spec_entries) = &spec_entries { @@ -205,19 +224,6 @@ impl NetworkRunnable for Cmd { let _ = build_host_function_parameters(&contract_id, &self.slop, spec_entries, config)?; } let client = network.rpc_client()?; - let account_details = if self.is_view { - default_account_entry() - } else { - client - .verify_network_passphrase(Some(&network.network_passphrase)) - .await?; - - client - .get_account(&config.source_account()?.to_string()) - .await? - }; - let sequence: i64 = account_details.seq_num.into(); - let AccountId(PublicKey::PublicKeyTypeEd25519(account_id)) = account_details.account_id; let spec_entries = get_remote_contract_spec( &contract_id.0, @@ -229,38 +235,56 @@ impl NetworkRunnable for Cmd { .await .map_err(Error::from)?; - // Get the ledger footprint let (function, spec, host_function_params, signers) = build_host_function_parameters(&contract_id, &self.slop, &spec_entries, config)?; - let tx = build_invoke_contract_tx( + + let should_send_tx = self + .should_send_after_sim(host_function_params.clone(), client.clone()) + .await?; + + let account_details = if should_send_tx == ShouldSend::Yes { + client + .verify_network_passphrase(Some(&network.network_passphrase)) + .await?; + + client + .get_account(&config.source_account()?.to_string()) + .await? + } else { + default_account_entry() + }; + let sequence: i64 = account_details.seq_num.into(); + let AccountId(PublicKey::PublicKeyTypeEd25519(account_id)) = account_details.account_id; + + let tx = Box::new(build_invoke_contract_tx( host_function_params.clone(), sequence + 1, self.fee.fee, account_id, - )?; + )?); if self.fee.build_only { return Ok(TxnResult::Txn(tx)); } let txn = simulate_and_assemble_transaction(&client, &tx).await?; - let txn = self.fee.apply_to_assembled_txn(txn); + let assembled = self.fee.apply_to_assembled_txn(txn); + let mut txn = Box::new(assembled.transaction().clone()); if self.fee.sim_only { - return Ok(TxnResult::Txn(txn.transaction().clone())); + return Ok(TxnResult::Txn(txn)); } - let sim_res = txn.sim_response(); + let sim_res = assembled.sim_response(); if global_args.map_or(true, |a| !a.no_cache) { data::write(sim_res.clone().into(), &network.rpc_uri()?)?; } - let should_send = self.send(sim_res)?; + let should_send = self.should_send_tx(sim_res)?; let (return_value, events) = match should_send { ShouldSend::Yes => { let global::Args { no_cache, .. } = global_args.cloned().unwrap_or_default(); // Need to sign all auth entries - let mut txn = txn.transaction().clone(); if let Some(tx) = config.sign_soroban_authorizations(&txn, &signers).await? { - txn = tx; + txn = Box::new(tx); } let res = client - .send_transaction_polling(&config.sign_with_local_key(txn).await?) + .send_transaction_polling(&config.sign_with_local_key(*txn).await?) .await?; if !no_cache { data::write(res.clone().try_into()?, &network.rpc_uri()?)?; @@ -365,6 +389,7 @@ pub enum Send { Yes, } +#[derive(Debug, PartialEq)] enum ShouldSend { DefaultNo, No, diff --git a/cmd/soroban-cli/src/commands/contract/mod.rs b/cmd/soroban-cli/src/commands/contract/mod.rs index d0524e82b..d72ce62b6 100644 --- a/cmd/soroban-cli/src/commands/contract/mod.rs +++ b/cmd/soroban-cli/src/commands/contract/mod.rs @@ -146,7 +146,7 @@ impl Cmd { match &self { Cmd::Asset(asset) => asset.run().await?, Cmd::Bindings(bindings) => bindings.run().await?, - Cmd::Build(build) => build.run()?, + Cmd::Build(build) => build.run(global_args)?, Cmd::Extend(extend) => extend.run().await?, Cmd::Alias(alias) => alias.run(global_args)?, Cmd::Deploy(deploy) => deploy.run(global_args).await?, diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index 87a52a9f6..cb35e6304 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -136,11 +136,12 @@ impl NetworkRunnable for Cmd { let source_account = config.source_account()?; // Get the account sequence number - let public_strkey = source_account.to_string(); - let account_details = client.get_account(&public_strkey).await?; + let account_details = client + .get_account(&source_account.clone().to_string()) + .await?; let sequence: i64 = account_details.seq_num.into(); - let tx = Transaction { + let tx = Box::new(Transaction { source_account, fee: self.fee.fee, seq_num: SequenceNumber(sequence + 1), @@ -166,12 +167,12 @@ impl NetworkRunnable for Cmd { }, resource_fee: 0, }), - }; + }); if self.fee.build_only { return Ok(TxnResult::Txn(tx)); } let res = client - .send_transaction_polling(&config.sign_with_local_key(tx).await?) + .send_transaction_polling(&config.sign_with_local_key(*tx).await?) .await?; if args.map_or(true, |a| !a.no_cache) { data::write(res.clone().try_into()?, &network.rpc_uri()?)?; @@ -206,7 +207,7 @@ impl NetworkRunnable for Cmd { ); } Ok(TxnResult::Res( - parse_operations(operations).ok_or(Error::MissingOperationResult)?, + parse_operations(&operations.to_vec()).ok_or(Error::MissingOperationResult)?, )) } } diff --git a/cmd/soroban-cli/src/commands/env/mod.rs b/cmd/soroban-cli/src/commands/env/mod.rs new file mode 100644 index 000000000..298cac3d2 --- /dev/null +++ b/cmd/soroban-cli/src/commands/env/mod.rs @@ -0,0 +1,61 @@ +use crate::{ + commands::global, + config::locator::{self}, + print::Print, +}; +use clap::Parser; + +#[derive(Debug, Parser)] +pub struct Cmd { + #[command(flatten)] + pub config_locator: locator::Args, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Locator(#[from] locator::Error), +} + +impl Cmd { + pub fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let print = Print::new(global_args.quiet); + let mut lines: Vec<(String, String)> = Vec::new(); + + if let Some(data) = get("STELLAR_NETWORK") { + lines.push(data); + } + + if let Some(data) = get("STELLAR_ACCOUNT") { + lines.push(data); + } + + if lines.is_empty() { + print.warnln("No defaults or environment variables set".to_string()); + return Ok(()); + } + + let max_len = lines.iter().map(|l| l.0.len()).max().unwrap_or(0); + + lines.sort(); + + for (value, source) in lines { + println!("{value:max_len$} # {source}"); + } + + Ok(()) + } +} + +fn get(env_var: &str) -> Option<(String, String)> { + // The _SOURCE env var is set from cmd/soroban-cli/src/cli.rs#set_env_value_from_config + let source = std::env::var(format!("{env_var}_SOURCE")) + .ok() + .unwrap_or("env".to_string()); + + if let Ok(value) = std::env::var(env_var) { + return Some((format!("{env_var}={value}"), source)); + } + + None +} diff --git a/cmd/soroban-cli/src/commands/events.rs b/cmd/soroban-cli/src/commands/events.rs index a1f5de921..48d79c1b7 100644 --- a/cmd/soroban-cli/src/commands/events.rs +++ b/cmd/soroban-cli/src/commands/events.rs @@ -42,7 +42,7 @@ pub struct Cmd { num_args = 1..=6, help_heading = "FILTERS" )] - contract_ids: Vec, + contract_ids: Vec, /// A set of (up to 4) topic filters to filter event topics on. A single /// topic filter can contain 1-4 different segment filters, separated by /// commas, with an asterisk (`*` character) indicating a wildcard segment. @@ -218,9 +218,8 @@ impl NetworkRunnable for Cmd { .contract_ids .iter() .map(|id| { - Ok(self - .locator - .resolve_contract_id(id, &network.network_passphrase)? + Ok(id + .resolve_contract_id(&self.locator, &network.network_passphrase)? .to_string()) }) .collect::, Error>>()?; diff --git a/cmd/soroban-cli/src/commands/global.rs b/cmd/soroban-cli/src/commands/global.rs index be883b6fd..02c0b3b58 100644 --- a/cmd/soroban-cli/src/commands/global.rs +++ b/cmd/soroban-cli/src/commands/global.rs @@ -4,7 +4,7 @@ use clap::{ }; use std::path::PathBuf; -use super::config; +use super::{config, HEADING_GLOBAL}; const USAGE_STYLES: Styles = Styles::styled() .header(AnsiColor::Green.on_default().effects(Effects::BOLD)) @@ -24,19 +24,19 @@ pub struct Args { pub locator: config::locator::Args, /// Filter logs output. To turn on `stellar_cli::log::footprint=debug` or off `=off`. Can also use env var `RUST_LOG`. - #[arg(long, short = 'f', global = true)] + #[arg(long, short = 'f', global = true, help_heading = HEADING_GLOBAL)] pub filter_logs: Vec, /// Do not write logs to stderr including `INFO` - #[arg(long, short = 'q', global = true)] + #[arg(long, short = 'q', global = true, help_heading = HEADING_GLOBAL)] pub quiet: bool, /// Log DEBUG events - #[arg(long, short = 'v', global = true)] + #[arg(long, short = 'v', global = true, help_heading = HEADING_GLOBAL)] pub verbose: bool, /// Log DEBUG and TRACE events - #[arg(long, visible_alias = "vv", global = true)] + #[arg(long, visible_alias = "vv", global = true, help_heading = HEADING_GLOBAL)] pub very_verbose: bool, /// List installed plugins. E.g. `stellar-hello` @@ -44,7 +44,7 @@ pub struct Args { pub list: bool, /// Do not cache your simulations and transactions - #[arg(long, env = "STELLAR_NO_CACHE", global = true)] + #[arg(long, env = "STELLAR_NO_CACHE", global = true, help_heading = HEADING_GLOBAL)] pub no_cache: bool, } diff --git a/cmd/soroban-cli/src/commands/keys/default.rs b/cmd/soroban-cli/src/commands/keys/default.rs new file mode 100644 index 000000000..9aa180b6d --- /dev/null +++ b/cmd/soroban-cli/src/commands/keys/default.rs @@ -0,0 +1,35 @@ +use clap::command; + +use crate::{commands::global, config::locator, print::Print}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] locator::Error), +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +pub struct Cmd { + /// Set the default network name. + pub name: String, + + #[command(flatten)] + pub config_locator: locator::Args, +} + +impl Cmd { + pub fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let printer = Print::new(global_args.quiet); + let _ = self.config_locator.read_identity(&self.name)?; + + self.config_locator.write_default_identity(&self.name)?; + + printer.infoln(format!( + "The default source account is set to `{}`", + self.name, + )); + + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index b59e227fc..c6623386c 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -10,10 +10,15 @@ use crate::{commands::global, print::Print}; pub enum Error { #[error(transparent)] Config(#[from] locator::Error), + #[error(transparent)] Secret(#[from] secret::Error), + #[error(transparent)] Network(#[from] network::Error), + + #[error("An identity with the name '{0}' already exists")] + IdentityAlreadyExists(String), } #[derive(Debug, clap::Parser, Clone)] @@ -52,29 +57,47 @@ pub struct Cmd { /// Fund generated key pair #[arg(long, default_value = "false")] pub fund: bool, + + /// Overwrite existing identity if it already exists. + #[arg(long)] + pub overwrite: bool, } impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let print = Print::new(global_args.quiet); + + if self.config_locator.read_identity(&self.name).is_ok() { + if !self.overwrite { + return Err(Error::IdentityAlreadyExists(self.name.clone())); + } + + print.exclaimln(format!("Overwriting identity '{}'", &self.name)); + } + if !self.fund { - Print::new(global_args.quiet).warnln( + print.warnln( "Behavior of `generate` will change in the \ future, and it will no longer fund by default. If you want to fund please \ provide `--fund` flag. If you don't need to fund your keys in the future, ignore this \ warning. It can be suppressed with -q flag.", ); } + let seed_phrase = if self.default_seed { Secret::test_seed_phrase() } else { Secret::from_seed(self.seed.as_deref()) }?; + let secret = if self.as_secret { seed_phrase.private_key(self.hd_path)?.into() } else { seed_phrase }; + self.config_locator.write_identity(&self.name, &secret)?; + if !self.no_fund { let addr = secret.public_key(self.hd_path)?; let network = self.network.get(&self.config_locator)?; @@ -86,6 +109,7 @@ impl Cmd { }) .unwrap_or_default(); } + Ok(()) } } diff --git a/cmd/soroban-cli/src/commands/keys/mod.rs b/cmd/soroban-cli/src/commands/keys/mod.rs index 30df5ccee..8729ee9af 100644 --- a/cmd/soroban-cli/src/commands/keys/mod.rs +++ b/cmd/soroban-cli/src/commands/keys/mod.rs @@ -3,6 +3,7 @@ use clap::Parser; pub mod add; pub mod address; +pub mod default; pub mod fund; pub mod generate; pub mod ls; @@ -13,18 +14,30 @@ pub mod show; pub enum Cmd { /// Add a new identity (keypair, ledger, macOS keychain) Add(add::Cmd), + /// Given an identity return its address (public key) Address(address::Cmd), + /// Fund an identity on a test network Fund(fund::Cmd), + /// Generate a new identity with a seed phrase, currently 12 words Generate(generate::Cmd), + /// List identities Ls(ls::Cmd), + /// Remove an identity Rm(rm::Cmd), + /// Given an identity return its private key Show(show::Cmd), + + /// Set the default identity that will be used on all commands. + /// This allows you to skip `--source-account` or setting a environment + /// variable, while reusing this value in all commands that require it. + #[command(name = "use")] + Default(default::Cmd), } #[derive(thiserror::Error, Debug)] @@ -34,18 +47,24 @@ pub enum Error { #[error(transparent)] Address(#[from] address::Error), + #[error(transparent)] Fund(#[from] fund::Error), #[error(transparent)] Generate(#[from] generate::Error), + #[error(transparent)] Rm(#[from] rm::Error), + #[error(transparent)] Ls(#[from] ls::Error), #[error(transparent)] Show(#[from] show::Error), + + #[error(transparent)] + Default(#[from] default::Error), } impl Cmd { @@ -58,6 +77,7 @@ impl Cmd { Cmd::Ls(cmd) => cmd.run()?, Cmd::Rm(cmd) => cmd.run()?, Cmd::Show(cmd) => cmd.run()?, + Cmd::Default(cmd) => cmd.run(global_args)?, }; Ok(()) } diff --git a/cmd/soroban-cli/src/commands/mod.rs b/cmd/soroban-cli/src/commands/mod.rs index 0a1d4629b..25d61364d 100644 --- a/cmd/soroban-cli/src/commands/mod.rs +++ b/cmd/soroban-cli/src/commands/mod.rs @@ -7,7 +7,9 @@ use crate::config; pub mod cache; pub mod completion; +pub mod container; pub mod contract; +pub mod env; pub mod events; pub mod global; pub mod keys; @@ -20,6 +22,7 @@ pub mod version; pub mod txn_result; pub const HEADING_RPC: &str = "Options (RPC)"; +pub const HEADING_GLOBAL: &str = "Options (Global)"; const ABOUT: &str = "Work seamlessly with Stellar accounts, contracts, and assets from the command line. @@ -35,7 +38,7 @@ For additional information see: - Stellar Docs: https://developers.stellar.org - Smart Contract Docs: https://developers.stellar.org/docs/build/smart-contracts/overview -- CLI Docs: https://developers.stellar.org/docs/tools/stellar-cli"; +- CLI Docs: https://developers.stellar.org/docs/tools/developer-tools/cli/stellar-cli"; // long_about is shown when someone uses `--help`; short help when using `-h` const LONG_ABOUT: &str = " @@ -111,11 +114,13 @@ impl Root { Cmd::Events(events) => events.run().await?, Cmd::Xdr(xdr) => xdr.run()?, Cmd::Network(network) => network.run(&self.global_args).await?, + Cmd::Container(container) => container.run(&self.global_args).await?, Cmd::Snapshot(snapshot) => snapshot.run(&self.global_args).await?, Cmd::Version(version) => version.run(), Cmd::Keys(id) => id.run(&self.global_args).await?, Cmd::Tx(tx) => tx.run(&self.global_args).await?, - Cmd::Cache(data) => data.run()?, + Cmd::Cache(cache) => cache.run()?, + Cmd::Env(env) => env.run(&self.global_args)?, }; Ok(()) } @@ -134,17 +139,27 @@ pub enum Cmd { /// Tools for smart contract developers #[command(subcommand)] Contract(contract::Cmd), + /// Watch the network for contract events Events(events::Cmd), + /// Prints the current environment variables or defaults to the stdout, in + /// a format that can be used as .env file. Environment variables have + /// precedency over defaults. + Env(env::Cmd), + /// Create and manage identities including keys and addresses #[command(subcommand)] Keys(keys::Cmd), - /// Start and configure networks + /// Configure connection to networks #[command(subcommand)] Network(network::Cmd), + /// Start local networks in containers + #[command(subcommand)] + Container(container::Cmd), + /// Download a snapshot of a ledger from an archive. #[command(subcommand)] Snapshot(snapshot::Cmd), @@ -159,9 +174,11 @@ pub enum Cmd { /// Print shell completion code for the specified shell. #[command(long_about = completion::LONG_ABOUT)] Completion(completion::Cmd), + /// Cache for transactions and contract specs #[command(subcommand)] Cache(cache::Cmd), + /// Print version information Version(version::Cmd), } @@ -171,24 +188,39 @@ pub enum Error { // TODO: stop using Debug for displaying errors #[error(transparent)] Contract(#[from] contract::Error), + #[error(transparent)] Events(#[from] events::Error), + #[error(transparent)] Keys(#[from] keys::Error), + #[error(transparent)] Xdr(#[from] stellar_xdr::cli::Error), + #[error(transparent)] Clap(#[from] clap::error::Error), + #[error(transparent)] Plugin(#[from] plugin::Error), + #[error(transparent)] Network(#[from] network::Error), + + #[error(transparent)] + Container(#[from] container::Error), + #[error(transparent)] Snapshot(#[from] snapshot::Error), + #[error(transparent)] Tx(#[from] tx::Error), + #[error(transparent)] Cache(#[from] cache::Error), + + #[error(transparent)] + Env(#[from] env::Error), } #[async_trait] diff --git a/cmd/soroban-cli/src/commands/network/default.rs b/cmd/soroban-cli/src/commands/network/default.rs new file mode 100644 index 000000000..337e08a69 --- /dev/null +++ b/cmd/soroban-cli/src/commands/network/default.rs @@ -0,0 +1,34 @@ +use clap::command; + +use crate::{commands::global, print::Print}; + +use super::locator; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] locator::Error), +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +pub struct Cmd { + /// Set the default network name. + pub name: String, + + #[command(flatten)] + pub config_locator: locator::Args, +} + +impl Cmd { + pub fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let printer = Print::new(global_args.quiet); + let _ = self.config_locator.read_network(&self.name)?; + + self.config_locator.write_default_network(&self.name)?; + + printer.infoln(format!("The default network is set to `{}`", self.name)); + + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/network/mod.rs b/cmd/soroban-cli/src/commands/network/mod.rs index 8dd61b394..5cc2acc43 100644 --- a/cmd/soroban-cli/src/commands/network/mod.rs +++ b/cmd/soroban-cli/src/commands/network/mod.rs @@ -5,7 +5,7 @@ use crate::rpc::{self}; use super::{config::locator, global}; pub mod add; -pub mod container; +pub mod default; pub mod ls; pub mod rm; @@ -13,10 +13,13 @@ pub mod rm; pub enum Cmd { /// Add a new network Add(add::Cmd), + /// Remove a network Rm(rm::Cmd), + /// List networks Ls(ls::Cmd), + /// ⚠️ Deprecated: use `stellar container start` instead /// /// Start network @@ -28,19 +31,31 @@ pub enum Cmd { /// By default, when starting a testnet container, without any optional arguments, it will run the equivalent of the following docker command: /// /// `docker run --rm -p 8000:8000 --name stellar stellar/quickstart:testing --testnet --enable rpc,horizon` - Start(container::StartCmd), + Start(crate::commands::container::StartCmd), + /// ⚠️ Deprecated: use `stellar container stop` instead /// /// Stop a network started with `network start`. For example, if you ran `stellar network start local`, you can use `stellar network stop local` to stop it. - Stop(container::StopCmd), + Stop(crate::commands::container::StopCmd), + /// Set the default network that will be used on all commands. + /// This allows you to skip `--network` or setting a environment variable, + /// while reusing this value in all commands that require it. + #[command(name = "use")] + Default(default::Cmd), + + /// ⚠️ Deprecated: use `stellar container` instead + /// /// Commands to start, stop and get logs for a quickstart container #[command(subcommand)] - Container(container::Cmd), + Container(crate::commands::container::Cmd), } #[derive(thiserror::Error, Debug)] pub enum Error { + #[error(transparent)] + Default(#[from] default::Error), + #[error(transparent)] Add(#[from] add::Error), @@ -52,14 +67,14 @@ pub enum Error { // TODO: remove once `network start` is removed #[error(transparent)] - Start(#[from] container::start::Error), + Start(#[from] crate::commands::container::start::Error), // TODO: remove once `network stop` is removed #[error(transparent)] - Stop(#[from] container::stop::Error), + Stop(#[from] crate::commands::container::stop::Error), #[error(transparent)] - Container(#[from] container::Error), + Container(#[from] crate::commands::container::Error), #[error(transparent)] Config(#[from] locator::Error), @@ -81,6 +96,7 @@ pub enum Error { impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { match self { + Cmd::Default(cmd) => cmd.run(global_args)?, Cmd::Add(cmd) => cmd.run()?, Cmd::Rm(new) => new.run()?, Cmd::Ls(cmd) => cmd.run()?, @@ -88,12 +104,14 @@ impl Cmd { // TODO Remove this once `network start` is removed Cmd::Start(cmd) => { - eprintln!("⚠️ Warning: `network start` has been deprecated. Use `network container start` instead"); + eprintln!("⚠️ Warning: `network start` has been deprecated. Use `container start` instead"); cmd.run(global_args).await?; } // TODO Remove this once `network stop` is removed Cmd::Stop(cmd) => { - println!("⚠️ Warning: `network stop` has been deprecated. Use `network container stop` instead"); + println!( + "⚠️ Warning: `network stop` has been deprecated. Use `container stop` instead" + ); cmd.run(global_args).await?; } }; diff --git a/cmd/soroban-cli/src/commands/plugin.rs b/cmd/soroban-cli/src/commands/plugin.rs index 2fa028d5c..d933564f2 100644 --- a/cmd/soroban-cli/src/commands/plugin.rs +++ b/cmd/soroban-cli/src/commands/plugin.rs @@ -1,4 +1,4 @@ -use std::process::Command; +use std::{path::PathBuf, process::Command}; use clap::CommandFactory; use which::which; @@ -44,7 +44,7 @@ pub fn run() -> Result<(), Error> { return Ok(()); } - let bin = which(format!("soroban-{name}")).map_err(|_| { + let bin = find_bin(&name).map_err(|_| { let suggestion = if let Ok(bins) = list() { let suggested_name = bins .iter() @@ -53,6 +53,7 @@ pub fn run() -> Result<(), Error> { .min_by(|a, b| a.1.total_cmp(&b.1)) .map(|(a, _)| a.to_string()) .unwrap_or_default(); + if suggested_name.is_empty() { suggested_name } else { @@ -64,8 +65,10 @@ pub fn run() -> Result<(), Error> { } else { String::new() }; + Error::ExecutableNotFound(name, suggestion) })?; + std::process::exit( Command::new(bin) .args(args) @@ -78,19 +81,29 @@ pub fn run() -> Result<(), Error> { const MAX_HEX_LENGTH: usize = 10; +fn find_bin(name: &str) -> Result { + if let Ok(path) = which(format!("stellar-{name}")) { + Ok(path) + } else { + which(format!("soroban-{name}")) + } +} + pub fn list() -> Result, Error> { let re_str = if cfg!(target_os = "windows") { - r"^soroban-.*.exe$" + r"^(soroban|stellar)-.*.exe$" } else { - r"^soroban-.*" + r"^(soroban|stellar)-.*" }; + let re = regex::Regex::new(re_str)?; + Ok(which::which_re(re)? .filter_map(|b| { let s = b.file_name()?.to_str()?; Some(s.strip_suffix(".exe").unwrap_or(s).to_string()) }) .filter(|s| !(utils::is_hex_string(s) && s.len() > MAX_HEX_LENGTH)) - .map(|s| s.replace("soroban-", "")) + .map(|s| s.replace("soroban-", "").replace("stellar-", "")) .collect()) } diff --git a/cmd/soroban-cli/src/commands/tx/args.rs b/cmd/soroban-cli/src/commands/tx/args.rs index 7e032fd53..1da1f230a 100644 --- a/cmd/soroban-cli/src/commands/tx/args.rs +++ b/cmd/soroban-cli/src/commands/tx/args.rs @@ -39,7 +39,7 @@ impl Args { let source_account = self.source_account()?; let seq_num = self .config - .next_sequence_number(&source_account.to_string()) + .next_sequence_number(source_account.clone().account_id()) .await?; // Once we have a way to add operations this will be updated to allow for a different source account let operation = xdr::Operation { @@ -87,7 +87,7 @@ impl Args { let network = self.config.get_network()?; let client = Client::new(&network.rpc_url)?; if self.fee.build_only { - return Ok(TxnEnvelopeResult::TxnEnvelope(tx.into())); + return Ok(TxnEnvelopeResult::TxnEnvelope(Box::new(tx.into()))); } let txn_resp = client diff --git a/cmd/soroban-cli/src/commands/tx/new/change_trust.rs b/cmd/soroban-cli/src/commands/tx/new/change_trust.rs index 1ea4e737e..da9acc8cf 100644 --- a/cmd/soroban-cli/src/commands/tx/new/change_trust.rs +++ b/cmd/soroban-cli/src/commands/tx/new/change_trust.rs @@ -10,7 +10,7 @@ pub struct Cmd { #[arg(long)] pub line: builder::Asset, /// Limit for the trust line, 0 to remove the trust line - #[arg(long, default_value = u64::MAX.to_string())] + #[arg(long, default_value = i64::MAX.to_string())] pub limit: i64, } diff --git a/cmd/soroban-cli/src/commands/tx/new/create_account.rs b/cmd/soroban-cli/src/commands/tx/new/create_account.rs index 967c0cf43..9cc3a62ff 100644 --- a/cmd/soroban-cli/src/commands/tx/new/create_account.rs +++ b/cmd/soroban-cli/src/commands/tx/new/create_account.rs @@ -11,7 +11,7 @@ pub struct Cmd { #[arg(long)] pub destination: xdr::AccountId, /// Initial balance in stroops of the account, default 1 XLM - #[arg(long, default_value = "10_000_000")] + #[arg(long, default_value = "10000000")] pub starting_balance: i64, } diff --git a/cmd/soroban-cli/src/commands/tx/new/set_options.rs b/cmd/soroban-cli/src/commands/tx/new/set_options.rs index 3410b69e8..69cd10745 100644 --- a/cmd/soroban-cli/src/commands/tx/new/set_options.rs +++ b/cmd/soroban-cli/src/commands/tx/new/set_options.rs @@ -40,7 +40,7 @@ pub struct Cmd { /// https://developers.stellar.org/docs/tokens/control-asset-access#authorization-required-0x1 pub set_required: bool, #[arg(long, conflicts_with = "clear_revocable")] - /// When enabled, an issuer can revoke an existing trustline’s authorization, thereby freezing the asset held by an account. + /// When enabled, an issuer can revoke an existing trustline's authorization, thereby freezing the asset held by an account. /// https://developers.stellar.org/docs/tokens/control-asset-access#authorization-revocable-0x2 pub set_revocable: bool, #[arg(long, conflicts_with = "clear_clawback_enabled")] @@ -48,7 +48,7 @@ pub struct Cmd { /// https://developers.stellar.org/docs/tokens/control-asset-access#clawback-enabled-0x8 pub set_clawback_enabled: bool, #[arg(long, conflicts_with = "clear_immutable")] - /// With this setting, none of the other authorization flags (`AUTH_REQUIRED_FLAG`, `AUTH_REVOCABLE_FLAG`) can be set, and the issuing account can’t be merged. + /// With this setting, none of the other authorization flags (`AUTH_REQUIRED_FLAG`, `AUTH_REVOCABLE_FLAG`) can be set, and the issuing account can't be merged. /// https://developers.stellar.org/docs/tokens/control-asset-access#authorization-immutable-0x4 pub set_immutable: bool, #[arg(long)] diff --git a/cmd/soroban-cli/src/commands/tx/send.rs b/cmd/soroban-cli/src/commands/tx/send.rs index 22fbc860a..1e542eb6f 100644 --- a/cmd/soroban-cli/src/commands/tx/send.rs +++ b/cmd/soroban-cli/src/commands/tx/send.rs @@ -1,3 +1,4 @@ +use crate::{print::Print, utils::transaction_hash}; use async_trait::async_trait; use soroban_rpc::GetTransactionResponse; @@ -46,7 +47,7 @@ impl NetworkRunnable for Cmd { type Result = GetTransactionResponse; async fn run_against_rpc_server( &self, - _: Option<&global::Args>, + globals: Option<&global::Args>, config: Option<&config::Args>, ) -> Result { let network = if let Some(config) = config { @@ -56,6 +57,14 @@ impl NetworkRunnable for Cmd { }; let client = network.rpc_client()?; let tx_env = super::xdr::tx_envelope_from_stdin()?; + + if let Ok(Ok(hash)) = super::xdr::unwrap_envelope_v1(tx_env.clone()) + .map(|tx| transaction_hash(&tx, &network.network_passphrase)) + { + let print = Print::new(globals.map_or(false, |g| g.quiet)); + print.infoln(format!("Transaction Hash: {}", hex::encode(hash))); + } + Ok(client.send_transaction_polling(&tx_env).await?) } } diff --git a/cmd/soroban-cli/src/commands/txn_result.rs b/cmd/soroban-cli/src/commands/txn_result.rs index d6b308b3a..568fd79d2 100644 --- a/cmd/soroban-cli/src/commands/txn_result.rs +++ b/cmd/soroban-cli/src/commands/txn_result.rs @@ -2,7 +2,7 @@ use crate::xdr::{Transaction, TransactionEnvelope, TransactionV1Envelope, VecM}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum TxnResult { - Txn(Transaction), + Txn(Box), Res(R), } @@ -16,12 +16,12 @@ impl TxnResult { pub fn to_envelope(self) -> TxnEnvelopeResult { match self { - TxnResult::Txn(tx) => { - TxnEnvelopeResult::TxnEnvelope(TransactionEnvelope::Tx(TransactionV1Envelope { - tx, + TxnResult::Txn(tx) => TxnEnvelopeResult::TxnEnvelope(Box::new( + TransactionEnvelope::Tx(TransactionV1Envelope { + tx: *tx, signatures: VecM::default(), - })) - } + }), + )), TxnResult::Res(res) => TxnEnvelopeResult::Res(res), } } @@ -29,6 +29,6 @@ impl TxnResult { #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum TxnEnvelopeResult { - TxnEnvelope(TransactionEnvelope), + TxnEnvelope(Box), Res(R), } diff --git a/cmd/soroban-cli/src/config/alias.rs b/cmd/soroban-cli/src/config/alias.rs index 68ad556d6..9d1d8c11b 100644 --- a/cmd/soroban-cli/src/config/alias.rs +++ b/cmd/soroban-cli/src/config/alias.rs @@ -1,8 +1,49 @@ -use std::collections::HashMap; +use std::{collections::HashMap, convert::Infallible, str::FromStr}; use serde::{Deserialize, Serialize}; +use super::locator; + #[derive(Serialize, Deserialize, Default)] pub struct Data { pub ids: HashMap, } + +/// Address can be either a contract address, C.. or eventually an alias of a contract address. +#[derive(Clone, Debug)] +pub enum ContractAddress { + ContractId(stellar_strkey::Contract), + Alias(String), +} + +impl Default for ContractAddress { + fn default() -> Self { + ContractAddress::Alias(String::default()) + } +} + +impl FromStr for ContractAddress { + type Err = Infallible; + + fn from_str(value: &str) -> Result { + Ok(stellar_strkey::Contract::from_str(value).map_or_else( + |_| ContractAddress::Alias(value.to_string()), + ContractAddress::ContractId, + )) + } +} + +impl ContractAddress { + pub fn resolve_contract_id( + &self, + locator: &locator::Args, + network_passphrase: &str, + ) -> Result { + match self { + ContractAddress::ContractId(muxed_account) => Ok(*muxed_account), + ContractAddress::Alias(alias) => locator + .get_contract_id(alias, network_passphrase)? + .ok_or_else(|| locator::Error::ContractNotFound(alias.to_owned())), + } + } +} diff --git a/cmd/soroban-cli/src/config/data.rs b/cmd/soroban-cli/src/config/data.rs index bbfc6994e..f032a94d3 100644 --- a/cmd/soroban-cli/src/config/data.rs +++ b/cmd/soroban-cli/src/config/data.rs @@ -209,7 +209,7 @@ mod test { assert_eq!(rpc_uri, new_rpc_uri); match (action, original_action) { (Action::Simulate { response: a }, Action::Simulate { response: b }) => { - assert_eq!(a.cost.cpu_insns, b.cost.cpu_insns); + assert_eq!(a.min_resource_fee, b.min_resource_fee); } _ => panic!("Action mismatch"), } diff --git a/cmd/soroban-cli/src/config/locator.rs b/cmd/soroban-cli/src/config/locator.rs index 2fad2bb62..b6f5c75c1 100644 --- a/cmd/soroban-cli/src/config/locator.rs +++ b/cmd/soroban-cli/src/config/locator.rs @@ -1,27 +1,30 @@ use clap::arg; +use directories::UserDirs; use itertools::Itertools; use serde::de::DeserializeOwned; use std::{ ffi::OsStr, fmt::Display, fs::{self, create_dir_all, OpenOptions}, - io, - io::Write, + io::{self, Write}, path::{Path, PathBuf}, str::FromStr, }; use stellar_strkey::{Contract, DecodeError}; -use crate::{utils::find_config_dir, Pwd}; +use crate::{commands::HEADING_GLOBAL, utils::find_config_dir, Pwd}; use super::{ alias, network::{self, Network}, secret::Secret, + Config, }; #[derive(thiserror::Error, Debug)] pub enum Error { + #[error(transparent)] + TomlSerialize(#[from] toml::ser::Error), #[error("Failed to find home directory")] HomeDirNotFound, #[error("Failed read current directory")] @@ -34,6 +37,8 @@ pub enum Error { SecretFileRead { path: PathBuf }, #[error("Failed to read network file: {path};\nProbably need to use `stellar network add`")] NetworkFileRead { path: PathBuf }, + #[error("Failed to read file: {path}")] + FileRead { path: PathBuf }, #[error(transparent)] Toml(#[from] toml::de::Error), #[error("Secret file failed to deserialize")] @@ -72,6 +77,8 @@ pub enum Error { CannotAccessAliasConfigFile, #[error("cannot parse contract ID {0}: {1}")] CannotParseContractId(String, DecodeError), + #[error("contract not found: {0}")] + ContractNotFound(String), #[error("Failed to read upgrade check file: {path}: {error}")] UpgradeCheckReadFailed { path: PathBuf, error: io::Error }, #[error("Failed to write upgrade check file: {path}: {error}")] @@ -82,11 +89,11 @@ pub enum Error { #[group(skip)] pub struct Args { /// Use global config - #[arg(long, global = true)] + #[arg(long, global = true, help_heading = HEADING_GLOBAL)] pub global: bool, /// Location of config directory, default is "." - #[arg(long, global = true)] + #[arg(long, global = true, help_heading = HEADING_GLOBAL)] pub config_dir: Option, } @@ -145,7 +152,7 @@ impl Args { pub fn local_config(&self) -> Result { let pwd = self.current_dir()?; - Ok(find_config_dir(pwd.clone()).unwrap_or_else(|_| pwd.join(".soroban"))) + Ok(find_config_dir(pwd.clone()).unwrap_or_else(|_| pwd.join(".stellar"))) } pub fn current_dir(&self) -> Result { @@ -163,6 +170,14 @@ impl Args { KeyType::Network.write(name, network, &self.config_dir()?) } + pub fn write_default_network(&self, name: &str) -> Result<(), Error> { + Config::new()?.set_network(name).save() + } + + pub fn write_default_identity(&self, name: &str) -> Result<(), Error> { + Config::new()?.set_identity(name).save() + } + pub fn list_identities(&self) -> Result, Error> { Ok(KeyType::Identity .list_paths(&self.local_and_global()?)? @@ -320,12 +335,17 @@ impl Args { &self, alias: &str, network_passphrase: &str, - ) -> Result, Error> { + ) -> Result, Error> { let Some(alias_data) = self.load_contract_from_alias(alias)? else { return Ok(None); }; - Ok(alias_data.ids.get(network_passphrase).cloned()) + alias_data + .ids + .get(network_passphrase) + .map(|id| id.parse()) + .transpose() + .map_err(|e| Error::CannotParseContractId(alias.to_owned(), e)) } pub fn resolve_contract_id( @@ -333,14 +353,18 @@ impl Args { alias_or_contract_id: &str, network_passphrase: &str, ) -> Result { - let contract_id = self - .get_contract_id(alias_or_contract_id, network_passphrase)? - .unwrap_or_else(|| alias_or_contract_id.to_string()); + let Some(contract) = self.get_contract_id(alias_or_contract_id, network_passphrase)? else { + return alias_or_contract_id + .parse() + .map_err(|e| Error::CannotParseContractId(alias_or_contract_id.to_owned(), e)); + }; + Ok(contract) + } +} - Ok(Contract( - soroban_spec_tools::utils::contract_id_from_str(&contract_id) - .map_err(|e| Error::CannotParseContractId(contract_id.clone(), e))?, - )) +impl Pwd for Args { + fn set_pwd(&mut self, pwd: &Path) { + self.config_dir = Some(pwd.to_path_buf()); } } @@ -396,11 +420,10 @@ impl KeyType { } pub fn read_from_path(path: &Path) -> Result { - let data = fs::read(path).map_err(|_| Error::NetworkFileRead { + let data = fs::read_to_string(path).map_err(|_| Error::NetworkFileRead { path: path.to_path_buf(), })?; - let res = toml::from_slice(data.as_slice()); - Ok(res?) + Ok(toml::from_str(&data)?) } pub fn read_with_global(&self, key: &str, pwd: &Path) -> Result { @@ -468,18 +491,35 @@ impl KeyType { } pub fn global_config_path() -> Result { - Ok(if let Ok(config_home) = std::env::var("XDG_CONFIG_HOME") { + let config_dir = if let Ok(config_home) = std::env::var("XDG_CONFIG_HOME") { PathBuf::from_str(&config_home).map_err(|_| Error::XdgConfigHome(config_home))? } else { - dirs::home_dir() + UserDirs::new() .ok_or(Error::HomeDirNotFound)? + .home_dir() .join(".config") + }; + + let soroban_dir = config_dir.join("soroban"); + let stellar_dir = config_dir.join("stellar"); + let soroban_exists = soroban_dir.exists(); + let stellar_exists = stellar_dir.exists(); + + if stellar_exists && soroban_exists { + tracing::warn!("the .stellar and .soroban config directories exist at path {config_dir:?}, using the .stellar"); } - .join("soroban")) -} -impl Pwd for Args { - fn set_pwd(&mut self, pwd: &Path) { - self.config_dir = Some(pwd.to_path_buf()); + if stellar_exists { + return Ok(stellar_dir); } + + if soroban_exists { + return Ok(soroban_dir); + } + + Ok(stellar_dir) +} + +pub fn config_file() -> Result { + Ok(global_config_path()?.join("config.toml")) } diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index b961f0f67..a429ff434 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -1,6 +1,10 @@ use address::Address; use clap::{arg, command}; use serde::{Deserialize, Serialize}; +use std::{ + fs::{self, File}, + io::Write, +}; use crate::{ print::Print, @@ -8,7 +12,6 @@ use crate::{ xdr::{self, SequenceNumber, Transaction, TransactionEnvelope}, Pwd, }; - use network::Network; pub mod address; @@ -20,6 +23,8 @@ pub mod secret; pub mod sign_with; pub mod upgrade_check; +pub use alias::ContractAddress; + #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] @@ -112,10 +117,19 @@ impl Args { Ok(self.network.get(&self.locator)?) } - pub async fn next_sequence_number(&self, account_str: &str) -> Result { + pub async fn next_sequence_number( + &self, + account: impl Into, + ) -> Result { let network = self.get_network()?; let client = network.rpc_client()?; - Ok((client.get_account(account_str).await?.seq_num.0 + 1).into()) + Ok((client + .get_account(&account.into().to_string()) + .await? + .seq_num + .0 + + 1) + .into()) } } @@ -125,9 +139,6 @@ impl Pwd for Args { } } -#[derive(Default, Serialize, Deserialize)] -pub struct Config {} - #[derive(Debug, clap::Args, Clone, Default)] #[group(skip)] pub struct ArgsLocatorAndNetwork { @@ -143,3 +154,47 @@ impl ArgsLocatorAndNetwork { Ok(self.network.get(&self.locator)?) } } + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct Config { + pub defaults: Defaults, +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct Defaults { + pub network: Option, + pub identity: Option, +} + +impl Config { + pub fn new() -> Result { + let path = locator::config_file()?; + + if path.exists() { + let data = fs::read_to_string(&path).map_err(|_| locator::Error::FileRead { path })?; + Ok(toml::from_str(&data)?) + } else { + Ok(Config::default()) + } + } + + #[must_use] + pub fn set_network(mut self, s: &str) -> Self { + self.defaults.network = Some(s.to_string()); + self + } + + #[must_use] + pub fn set_identity(mut self, s: &str) -> Self { + self.defaults.identity = Some(s.to_string()); + self + } + + pub fn save(&self) -> Result<(), locator::Error> { + let toml_string = toml::to_string(&self)?; + let mut file = File::create(locator::config_file()?)?; + file.write_all(toml_string.as_bytes())?; + + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/config/network.rs b/cmd/soroban-cli/src/config/network.rs index ac7dc04bc..829716753 100644 --- a/cmd/soroban-cli/src/config/network.rs +++ b/cmd/soroban-cli/src/config/network.rs @@ -22,8 +22,23 @@ pub mod passphrase; pub enum Error { #[error(transparent)] Config(#[from] locator::Error), - #[error("network arg or rpc url and network passphrase are required if using the network")] + #[error( + r#"Access to the network is required +`--network` or `--rpc-url` and `--network-passphrase` are required if using the network. +Alternatively you can use their corresponding environment variables: +STELLAR_NETWORK, STELLAR_RPC_URL and STELLAR_NETWORK_PASSPHRASE"# + )] Network, + #[error( + "rpc-url is used but network passphrase is missing, use `--network-passphrase` or `STELLAR_NETWORK_PASSPHRASE`" + )] + MissingNetworkPassphrase, + #[error( + "network passphrase is used but rpc-url is missing, use `--rpc-url` or `STELLAR_RPC_URL`" + )] + MissingRpcUrl, + #[error("cannot use both `--rpc-url` and `--network`")] + CannotUseBothRpcAndNetwork, #[error(transparent)] Rpc(#[from] rpc::Error), #[error(transparent)] @@ -48,8 +63,6 @@ pub struct Args { /// RPC server endpoint #[arg( long = "rpc-url", - requires = "network_passphrase", - required_unless_present = "network", env = "STELLAR_RPC_URL", help_heading = HEADING_RPC, )] @@ -68,8 +81,6 @@ pub struct Args { /// Network passphrase to sign the transaction sent to the rpc server #[arg( long = "network-passphrase", - requires = "rpc_url", - required_unless_present = "network", env = "STELLAR_NETWORK_PASSPHRASE", help_heading = HEADING_RPC, )] @@ -77,8 +88,6 @@ pub struct Args { /// Name of network to use from config #[arg( long, - required_unless_present = "rpc_url", - required_unless_present = "network_passphrase", env = "STELLAR_NETWORK", help_heading = HEADING_RPC, )] @@ -87,21 +96,20 @@ pub struct Args { impl Args { pub fn get(&self, locator: &locator::Args) -> Result { - if let Some(name) = self.network.as_deref() { - if let Ok(network) = locator.read_network(name) { - return Ok(network); - } - } - if let (Some(rpc_url), Some(network_passphrase)) = - (self.rpc_url.clone(), self.network_passphrase.clone()) - { - Ok(Network { + match ( + self.network.as_deref(), + self.rpc_url.clone(), + self.network_passphrase.clone(), + ) { + (None, None, None) => Err(Error::Network), + (_, Some(_), None) => Err(Error::MissingNetworkPassphrase), + (_, None, Some(_)) => Err(Error::MissingRpcUrl), + (Some(network), None, None) => Ok(locator.read_network(network)?), + (_, Some(rpc_url), Some(network_passphrase)) => Ok(Network { rpc_url, rpc_headers: self.rpc_headers.clone(), network_passphrase, - }) - } else { - Err(Error::Network) + }), } } } diff --git a/cmd/soroban-cli/src/key.rs b/cmd/soroban-cli/src/key.rs index 3f4dcaf7c..b704541c4 100644 --- a/cmd/soroban-cli/src/key.rs +++ b/cmd/soroban-cli/src/key.rs @@ -4,7 +4,7 @@ use crate::xdr::{ }; use crate::{ commands::contract::Durability, - config::{locator, network::Network}, + config::{alias, locator, network::Network}, wasm, }; use clap::arg; @@ -34,7 +34,7 @@ pub struct Args { required_unless_present = "wasm", required_unless_present = "wasm_hash" )] - pub contract_id: Option, + pub contract_id: Option, /// Storage key (symbols only) #[arg(long = "key", conflicts_with = "key_xdr")] pub key: Option>, @@ -97,8 +97,11 @@ impl Args { } else { vec![ScVal::LedgerKeyContractInstance] }; - let contract = - locator.resolve_contract_id(self.contract_id.as_ref().unwrap(), network_passphrase)?; + let contract = self + .contract_id + .as_ref() + .unwrap() + .resolve_contract_id(locator, network_passphrase)?; Ok(keys .into_iter() diff --git a/cmd/soroban-cli/src/print.rs b/cmd/soroban-cli/src/print.rs index 5b8ca2bd2..fb9fb43dd 100644 --- a/cmd/soroban-cli/src/print.rs +++ b/cmd/soroban-cli/src/print.rs @@ -8,6 +8,7 @@ use crate::{ const TERMS: &[&str] = &["Apple_Terminal", "vscode"]; +#[derive(Clone)] pub struct Print { pub quiet: bool, } @@ -104,3 +105,4 @@ create_print_functions!(plus, plusln, "➕"); create_print_functions!(save, saveln, "💾"); create_print_functions!(search, searchln, "🔎"); create_print_functions!(warn, warnln, "⚠️"); +create_print_functions!(exclaim, exclaimln, "❗️"); diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index 6e87d15a6..0c4207a4e 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -95,10 +95,12 @@ pub fn find_config_dir(mut pwd: std::path::PathBuf) -> std::io::Result), #[error( "cannot fetch wasm for contract because the contract is \ a network built-in asset contract that does not have a downloadable code binary" @@ -121,16 +121,10 @@ pub fn len(p: &Path) -> Result { } pub async fn fetch_from_contract( - contract_id: &str, + stellar_strkey::Contract(contract_id): &stellar_strkey::Contract, network: &Network, - locator: &locator::Args, ) -> Result, Error> { tracing::trace!(?network); - - let contract_id = &locator - .resolve_contract_id(contract_id, &network.network_passphrase)? - .0; - let client = network.rpc_client()?; client .verify_network_passphrase(Some(&network.network_passphrase)) @@ -142,5 +136,5 @@ pub async fn fetch_from_contract( ContractExecutable::StellarAsset => Err(ContractIsStellarAsset), }; } - Err(UnexpectedContractToken(data_entry)) + Err(UnexpectedContractToken(Box::new(data_entry))) } diff --git a/cmd/stellar-cli/Cargo.toml b/cmd/stellar-cli/Cargo.toml index 2a388b4de..e1f1dce6b 100644 --- a/cmd/stellar-cli/Cargo.toml +++ b/cmd/stellar-cli/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/stellar/stellar-cli" authors = ["Stellar Development Foundation "] license = "Apache-2.0" readme = "README.md" -version = "21.5.0" +version.workspace = true edition = "2021" rust-version.workspace = true autobins = false diff --git a/cmd/stellar-cli/README.md b/cmd/stellar-cli/README.md index 9b95e474a..48be33da7 100644 --- a/cmd/stellar-cli/README.md +++ b/cmd/stellar-cli/README.md @@ -6,12 +6,13 @@ Docs: https://developers.stellar.org ## Install +Install with Homebrew (macOS, Linux): + ``` -cargo install --locked stellar-cli +brew install stellar-cli ``` -To install with the `opt` feature, which includes a WASM optimization feature and wasm-opt built in: - +Install the latest version from source: ``` cargo install --locked stellar-cli --features opt ``` diff --git a/deny.toml b/deny.toml new file mode 100644 index 000000000..238e91a14 --- /dev/null +++ b/deny.toml @@ -0,0 +1,344 @@ +# This template contains all of the possible sections and their default values + +# Note that all fields that take a lint level have these possible values: +# * deny - An error will be produced and the check will fail +# * warn - A warning will be produced, but the check will not fail +# * allow - No warning or error will be produced, though in some cases a note +# will be + +# The values provided in this template are the default values that will be used +# when any section or field is not specified in your own configuration + +# Root options + +# If 1 or more target triples (and optionally, target_features) are specified, +# only the specified targets will be checked when running `cargo deny check`. +# This means, if a particular package is only ever used as a target specific +# dependency, such as, for example, the `nix` crate only being used via the +# `target_family = "unix"` configuration, that only having windows targets in +# this list would mean the nix crate, as well as any of its exclusive +# dependencies not shared by any other crates, would be ignored, as the target +# list here is effectively saying which targets you are building for. +targets = [ + { triple = "x86_64-unknown-linux-gnu" }, + { triple = "aarch64-unknown-linux-gnu" }, + { triple = "x86_64-apple-darwin" }, + { triple = "aarch64-apple-darwin" }, + { triple = "x86_64-pc-windows-msvc" }, +] +# When creating the dependency graph used as the source of truth when checks are +# executed, this field can be used to prune crates from the graph, removing them +# from the view of cargo-deny. This is an extremely heavy hammer, as if a crate +# is pruned from the graph, all of its dependencies will also be pruned unless +# they are connected to another crate in the graph that hasn't been pruned, +# so it should be used with care. The identifiers are [Package ID Specifications] +# (https://doc.rust-lang.org/cargo/reference/pkgid-spec.html) + +exclude = [ +] + +# If true, metadata will be collected with `--all-features`. Note that this can't +# be toggled off if true, if you want to conditionally enable `--all-features` it +# is recommended to pass `--all-features` on the cmd line instead +all-features = true +# If true, metadata will be collected with `--no-default-features`. The same +# caveat with `all-features` applies +no-default-features = false +# If set, these feature will be enabled when collecting metadata. If `--features` +# is specified on the cmd line they will take precedence over this option. +#features = [] +# When outputting inclusion graphs in diagnostics that include features, this +# option can be used to specify the depth at which feature edges will be added. +# This option is included since the graphs can be quite large and the addition +# of features from the crate(s) to all of the graph roots can be far too verbose. +# This option can be overridden via `--feature-depth` on the cmd line +feature-depth = 1 + +# This section is considered when running `cargo deny check advisories` +# More documentation for the advisories section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html +[advisories] +# The path where the advisory database is cloned/fetched into +db-path = "~/.cargo/advisory-db" +# The url(s) of the advisory databases to use +db-urls = ["https://github.com/rustsec/advisory-db"] +# The lint level for security vulnerabilities +vulnerability = "deny" +# The lint level for unmaintained crates +unmaintained = "warn" +# The lint level for crates that have been yanked from their source registry +yanked = "warn" +# The lint level for crates with security notices. Note that as of +# 2019-12-17 there are no security notice advisories in +# https://github.com/rustsec/advisory-db +notice = "warn" +# A list of advisory IDs to ignore. Note that ignored advisories will still +# output a note when they are encountered. +ignore = [ + #"RUSTSEC-0000-0000", +] +# Threshold for security vulnerabilities, any vulnerability with a CVSS score +# lower than the range specified will be ignored. Note that ignored advisories +# will still output a note when they are encountered. +# * None - CVSS Score 0.0 +# * Low - CVSS Score 0.1 - 3.9 +# * Medium - CVSS Score 4.0 - 6.9 +# * High - CVSS Score 7.0 - 8.9 +# * Critical - CVSS Score 9.0 - 10.0 +#severity-threshold = + +# If this is true, then cargo deny will use the git executable to fetch advisory database. +# If this is false, then it uses a built-in git library. +# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support. +# See Git Authentication for more information about setting up git authentication. +#git-fetch-with-cli = true + +# This section is considered when running `cargo deny check licenses` +# More documentation for the licenses section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html +[licenses] +# The lint level for crates which do not have a detectable license +unlicensed = "deny" +# List of explicitly allowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. +allow = [ + "MIT", + "Apache-2.0", + "BSD-3-Clause", + "Apache-2.0 WITH LLVM-exception", + "Unicode-DFS-2016", + "ISC", + "BSD-2-Clause", +] +# List of explicitly disallowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. +deny = [ + #"Nokia", +] +# Lint level for licenses considered copyleft +copyleft = "deny" +# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses +# * both - The license will be approved if it is both OSI-approved *AND* FSF +# * either - The license will be approved if it is either OSI-approved *OR* FSF +# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF +# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved +# * neither - This predicate is ignored and the default lint level is used +allow-osi-fsf-free = "neither" +# Lint level used when no other predicates are matched +# 1. License isn't in the allow or deny lists +# 2. License isn't copyleft +# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" +default = "deny" +# The confidence threshold for detecting a license from license text. +# The higher the value, the more closely the license text must be to the +# canonical license text of a valid SPDX license file. +# [possible values: any between 0.0 and 1.0]. +confidence-threshold = 0.8 +# Allow 1 or more licenses on a per-crate basis, so that particular licenses +# aren't accepted for every possible crate as with the normal allow list +exceptions = [ + # The following dep licenses were inspected and approved for use because we + # do not fork them. Related conversation and approval is at: + # https://stellarfoundation.slack.com/archives/C07TC92R2P7/p1730924548048429?thread_ts=1730900591.962089&cid=C07TC92R2P7 + { allow = ["MPL-2.0"], name = "option-ext" }, + { allow = ["MPL-2.0"], name = "webpki-roots" }, + # The following dep licensed was manually reviewed and approved for use. Related conversation and approval is at: + # https://stellarfoundation.slack.com/archives/C07TC92R2P7/p1730917671563029?thread_ts=1730903286.490759&cid=C07TC92R2P7 + { allow = ["OpenSSL"], name = "ring" }, +] + +[[licenses.clarify]] +name = "ring" +expression = "MIT AND ISC AND OpenSSL" +license-files = [ + { path = "LICENSE", hash = 0xbd0eed23 } +] + +# Some crates don't have (easily) machine readable licensing information, +# adding a clarification entry for it allows you to manually specify the +# licensing information +#[[licenses.clarify]] +# The name of the crate the clarification applies to +#name = "ring" +# The optional version constraint for the crate +#version = "*" +# The SPDX expression for the license requirements of the crate +#expression = "MIT AND ISC AND OpenSSL" +# One or more files in the crate's source used as the "source of truth" for +# the license expression. If the contents match, the clarification will be used +# when running the license check, otherwise the clarification will be ignored +# and the crate will be checked normally, which may produce warnings or errors +# depending on the rest of your configuration +#license-files = [ + # Each entry is a crate relative path, and the (opaque) hash of its contents + #{ path = "LICENSE", hash = 0xbd0eed23 } +#] + +[licenses.private] +# If true, ignores workspace crates that aren't published, or are only +# published to private registries. +# To see how to mark a crate as unpublished (to the official registry), +# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field. +ignore = true +# One or more private registries that you might publish crates to, if a crate +# is only published to private registries, and ignore is true, the crate will +# not have its license(s) checked +registries = [ + #"https://sekretz.com/registry +] + +# This section is considered when running `cargo deny check bans`. +# More documentation about the 'bans' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html +[bans] +# Lint level for when multiple versions of the same crate are detected +multiple-versions = "deny" +# Lint level for when a crate version requirement is `*` +wildcards = "deny" +allow-wildcard-paths = true +# The graph highlighting used when creating dotgraphs for crates +# with multiple versions +# * lowest-version - The path to the lowest versioned duplicate is highlighted +# * simplest-path - The path to the version with the fewest edges is highlighted +# * all - Both lowest-version and simplest-path are used +highlight = "all" +# The default lint level for `default` features for crates that are members of +# the workspace that is being checked. This can be overridden by allowing/denying +# `default` on a crate-by-crate basis if desired. +workspace-default-features = "allow" +# The default lint level for `default` features for external crates that are not +# members of the workspace. This can be overridden by allowing/denying `default` +# on a crate-by-crate basis if desired. +external-default-features = "allow" +# List of crates that are allowed. Use with care! +allow = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +# List of crates to deny +deny = [ + # Each entry the name of a crate and a version range. If version is + # not specified, all versions will be matched. + #{ name = "ansi_term", version = "=0.11.0" }, + # + # Wrapper crates can optionally be specified to allow the crate when it + # is a direct dependency of the otherwise banned crate + #{ name = "ansi_term", version = "=0.11.0", wrappers = [] }, +] + +# List of features to allow/deny +# Each entry the name of a crate and a version range. If version is +# not specified, all versions will be matched. +#[[bans.features]] +#name = "reqwest" +# Features to not allow +#deny = ["json"] +# Features to allow +#allow = [ +# "rustls", +# "__rustls", +# "__tls", +# "hyper-rustls", +# "rustls", +# "rustls-pemfile", +# "rustls-tls-webpki-roots", +# "tokio-rustls", +# "webpki-roots", +#] +# If true, the allowed features must exactly match the enabled feature set. If +# this is set there is no point setting `deny` +#exact = true + +# Certain crates/versions that will be skipped when doing duplicate detection. +skip = [ + + # Requires updating slipped10 to newest sha2 others are dependents that will be updated + { crate = "sha2", reason = "temporary duplicate until upstream updates" }, + { crate = "digest", reason = "temporary duplicate until upstream updates" }, + { crate = "block-buffer", reason = "temporary duplicate until upstream updates" }, + # slipped again 0.12.1 + { crate = "hmac", reason = "temp" }, + + # update rpassword, hidapi (then ledger-transport-hid), and dirs-sys (then directories) to 0.52 + # { crate = "window-sys", reason = "temp" }, + + # syn is too large of a surface to check + { crate = "syn", reason = "Too many crates haven't updated to v2" }, + + # Need to release new version and update all stellar crates + { crate = "stellar-strkey", reason = "Temp until new release and updates upstream", version = "0.0.8" }, + + # Need to update jsonrpsee in stellar-rpc-client + { crate = "rustls-pemfile", reason = "Temp until new release and updates upstream" }, + { crate = "rustls-webpki", reason = "Temp until new release and updates upstream" }, + { crate = "rustls-native-certs", reason = "Temp until new release and updates upstream", version = "0.7.3" }, + { crate = "rustls", reason = "Temp until new release and updates upstream" }, + { crate = "hyper", reason = "temporary duplicate until upstream updates" }, + { crate = "hyper-rustls", reason = "temporary duplicate until upstream updates", version = "0.27.3" }, + { crate = "http-body", reason = "temporary duplicate until upstream updates" }, + { crate = "http", reason = "temporary duplicate until upstream updates" }, + { crate = "h2", reason = "temporary duplicate until upstream updates", version = "0.3.26" }, + { crate = "base64", reason = "temporary duplicate until upstream updates", version = "0.22.1" }, + # Upgrade stellar-rpc-client to use 0.26.0 + { crate = "tokio-rustls", reason = "temporary duplicate until upstream updates" }, + + # wasm-opt + { crate = "heck", reason = "wasm-opt needs to update to 0.5", version = "0.5.0"}, + { crate = "strum", reason = "wasm-opt needs to update", version = "0.26.3" }, + { crate = "strum_macros", reason = "wasm-opt needs to update", version = "0.26.4" }, + + + # soroban-env-host must upgrade ark-* to 0.14.5 + { crate = "hashbrown", reason = "temp", version = "13.2"}, + + { crate = "windows-sys", reason = "temp", version = "0.59.0"}, + { crate = "windows-targets", reason = "temp", version = "0.52.6"}, + { crate = "windows_x86_64_gnu", reason = "temp", version = "0.52.6"}, + { crate = "windows_x86_64_msvc", reason = "temp", version = "0.52.6"}, + # { crate = "dir-sys", reason = "temp", version } + # + # update tracing-subscriber + { crate = "regex-syntax", reason = "temp", version = "0.8.4" }, + { crate = "regex-automata", reason = "temp", version = "0.4.7" }, + + # wasm-gen update + { crate = "byteorder", reason = "temp", version = "1.5.0" }, + + # testcontainers + { crate = "idna", reason = "temp", version = "0.5.0" }, + + + # { name = "hashbrown", version = "=0.13.2" }, + # { name = "syn", version = "=1.0.109" }, +] +# Similarly to `skip` allows you to skip certain crates during duplicate +# detection. Unlike skip, it also includes the entire tree of transitive +# dependencies starting at the specified crate, up to a certain depth, which is +# by default infinite. +skip-tree = [ +] + +# This section is considered when running `cargo deny check sources`. +# More documentation about the 'sources' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html +[sources] +# Lint level for what to happen when a crate from a crate registry that is not +# in the allow list is encountered +unknown-registry = "deny" +# Lint level for what to happen when a crate from a git repository that is not +# in the allow list is encountered +unknown-git = "deny" +# List of URLs for allowed crate registries. Defaults to the crates.io index +# if not specified. If it is specified but empty, no registries are allowed. +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +# List of URLs for allowed Git repositories +allow-git = [] + +[sources.allow-org] +# 1 or more github.com organizations to allow git sources for +github = ["stellar"] +# 1 or more gitlab.com organizations to allow git sources for +# gitlab = [""] +# 1 or more bitbucket.org organizations to allow git sources for +# bitbucket = [""] diff --git a/flake.lock b/flake.lock index fef18714e..fd92bacb2 100644 --- a/flake.lock +++ b/flake.lock @@ -36,11 +36,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1718428119, - "narHash": "sha256-WdWDpNaq6u1IPtxtYHHWpl5BmabtpmLnMAx0RdJ/vo8=", + "lastModified": 1728538411, + "narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e6cea36f83499eb4e9cd184c8a8e823296b50ad5", + "rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221", "type": "github" }, "original": { @@ -62,11 +62,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1727144949, - "narHash": "sha256-uMZMjoCS2nf40TAE1686SJl3OXWfdfM+BDEfRdr+uLc=", + "lastModified": 1729823394, + "narHash": "sha256-RiinJqorqSLKh1oSpiMHnBe6nQdJzE45lX6fSnAuDnI=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "2e19799819104b46019d339e78d21c14372d3666", + "rev": "7e52e80f5faa374ad4c607d62c6d362589cb523f", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 1923d75bb..8e5ace887 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "stellar-cli development shell"; + description = "stellar-cli"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; @@ -14,14 +14,13 @@ pkgs = import nixpkgs { inherit system overlays; }; - in - with pkgs; - { - devShells.default = mkShell { - nativeBuildInputs = lib.optionals (stdenv.isDarwin) [ + stellardev = { + name = "stellar"; + src = ./.; + nativeBuildInputs = pkgs.lib.optionals (pkgs.stdenv.isDarwin) [ pkgs.darwin.apple_sdk.frameworks.SystemConfiguration ]; - buildInputs = [ + buildInputs = with pkgs; [ openssl pkg-config jq @@ -31,6 +30,29 @@ }) ] ++ lib.optionals (stdenv.isLinux) [libudev-zero]; }; + stellarcli = stellardev // { + cargoLock = { + lockFile = ./Cargo.lock; + }; + + cargoLock.outputHashes = { + # This is needed for any git+https dependency in Cargo.lock + # "somepackage-1.2.3" = "sha256-somehash"; + }; + + doCheck = false; + + GIT_REVISION = "${self.rev or self.dirtyRev or "unknown"}"; + }; + rustPlatformMod = pkgs.makeRustPlatform { + cargo = pkgs.rust-bin.stable.latest.default; + rustc = pkgs.rust-bin.stable.latest.default; + }; + in + with pkgs; + { + devShells.default = mkShell stellardev; + packages.default = rustPlatformMod.buildRustPackage stellarcli; } ); } diff --git a/installer.iss b/installer.iss new file mode 100644 index 000000000..895740ccb --- /dev/null +++ b/installer.iss @@ -0,0 +1,94 @@ +#define STELLAR_CLI_VERSION GetEnv("STELLAR_CLI_VERSION") +#define STELLAR_CLI_INSTALLER GetEnv("STELLAR_CLI_INSTALLER") + +[Setup] +AppName=Stellar CLI +AppVersion={#STELLAR_CLI_VERSION} +DefaultDirName={commonpf}\Stellar CLI +DefaultGroupName=Stellar CLI +OutputBaseFilename=stellar-installer +PrivilegesRequired=admin +LicenseFile=LICENSE +UninstallDisplayIcon={app}\stellar.ico +Compression=lzma +SolidCompression=yes +ChangesEnvironment=yes + +[Files] +Source: "stellar.exe"; DestDir: "{app}" +Source: "stellar.ico"; DestDir: "{app}" +Source: "LICENSE"; DestDir: "{app}"; Flags: ignoreversion + +[Icons] +; Windows optimizes start menu, and removes the uninstall entry. Unless we +; specify it twice. 🫠 +Name: "{group}\Uninstall Stellar CLI"; Filename: "{uninstallexe}" +Name: "{group}\Uninstall Stellar CLI"; Filename: "{uninstallexe}" +Name: "{group}\Stellar Developer Docs"; Filename: "https://stellar.org/docs" + +[Code] +const EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'; + +procedure EnvAddPath(Path: string); +var + Paths: string; +begin + { Retrieve current path (use empty string if entry not exists) } + if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) + then Paths := ''; + + { Skip if string already found in path } + if Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';') > 0 then exit; + + { App string to the end of the path variable } + Paths := Paths + ';'+ Path +';' + + { Overwrite (or create if missing) path environment variable } + if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) + then Log(Format('The [%s] added to PATH: [%s]', [Path, Paths])) + else Log(Format('Error while adding the [%s] to PATH: [%s]', [Path, Paths])); +end; + +procedure EnvRemovePath(Path: string); +var + Paths: string; + P: Integer; +begin + { Skip if registry entry not exists } + if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) then + exit; + + { Skip if string not found in path } + P := Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';'); + if P = 0 then exit; + + { Update path variable } + Delete(Paths, P - 1, Length(Path) + 1); + + { Overwrite path environment variable } + if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) + then Log(Format('The [%s] removed from PATH: [%s]', [Path, Paths])) + else Log(Format('Error while removing the [%s] from PATH: [%s]', [Path, Paths])); +end; + +procedure CurStepChanged(CurStep: TSetupStep); +begin + if CurStep = ssPostInstall + then EnvAddPath(ExpandConstant('{app}')); +end; + +procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); +begin + if CurUninstallStep = usPostUninstall + then EnvRemovePath(ExpandConstant('{app}')); +end; + +[UninstallDelete] +; Remove the Start Menu group +Type: filesandordirs; Name: "{group}" + +; Remove installed files and directory +Type: files; Name: "{app}\stellar.exe" +Type: files; Name: "{uninstallexe}" +Type: files; Name: "{app}\stellar.ico" +Type: dirifempty; Name: "{app}" diff --git a/stellar.ico b/stellar.ico new file mode 100644 index 000000000..4bbfa5f04 Binary files /dev/null and b/stellar.ico differ