diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index fb492f0c..6f9910b2 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -31,7 +31,7 @@ jobs: SYSTEM_TEST_CORE_COMPILE_CONFIGURE_FLAGS: "--disable-tests" # or set SYSTEM_TEST_CORE_GIT_REF to empty, and set SYSTEM_TEST_CORE_IMAGE # to pull a pre-compiled image from dockerhub instead - SYSTEM_TEST_CORE_IMAGE: stellar/stellar-core:20 + SYSTEM_TEST_CORE_IMAGE: stellar/stellar-core:21 SYSTEM_TEST_CORE_IMAGE_BIN_PATH: /usr/bin/stellar-core # sets the version of rust toolchain that will be pre-installed in the @@ -42,7 +42,7 @@ jobs: # resolution options, using npm release or a gh ref: # # option #1, set the version of stellar-sdk based on a npm release version - SYSTEM_TEST_JS_STELLAR_SDK_NPM_VERSION: 11.3.0 + SYSTEM_TEST_JS_STELLAR_SDK_NPM_VERSION: 12.3.0 # option #2, set the version of stellar-sdk used as a ref to a gh repo if # a value is set on SYSTEM_TEST_JS_STELLAR_SDK_GH_REPO, it takes # precedence over any SYSTEM_TEST_JS_STELLAR_SDK_NPM_VERSION @@ -50,17 +50,17 @@ jobs: SYSTEM_TEST_JS_STELLAR_SDK_GH_REF: # the version of rs-stellar-xdr to use for quickstart - SYSTEM_TEST_RS_XDR_GIT_REF: v20.0.2 + SYSTEM_TEST_RS_XDR_GIT_REF: v21.2.0 # system test will build quickstart image internally to use for running the service stack # configured in standalone network mode(core, rpc) - SYSTEM_TEST_QUICKSTART_GIT_REF: https://github.com/stellar/quickstart.git#412bb828ddb4a93745227ab5ad97c623d43f3a5f + SYSTEM_TEST_QUICKSTART_GIT_REF: https://github.com/stellar/quickstart.git#ae7fdb07283a0af836d4cd51ff09b02db93fb12c # triggers system test to log out details from quickstart's logs and test steps SYSTEM_TEST_VERBOSE_OUTPUT: "true" # the soroban test cases will compile various contracts from the examples repo - SYSTEM_TEST_SOROBAN_EXAMPLES_GIT_HASH: "v20.0.0" + SYSTEM_TEST_SOROBAN_EXAMPLES_GIT_HASH: "v21.6.0" SYSTEM_TEST_SOROBAN_EXAMPLES_GIT_REPO: "https://github.com/stellar/soroban-examples.git" steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/soroban-rpc.yml b/.github/workflows/soroban-rpc.yml index bc7ed064..6ce8184c 100644 --- a/.github/workflows/soroban-rpc.yml +++ b/.github/workflows/soroban-rpc.yml @@ -98,16 +98,17 @@ jobs: strategy: matrix: os: [ ubuntu-20.04, ubuntu-22.04 ] - protocol-version: [ 20, 21 ] + protocol-version: [ 21, 22 ] runs-on: ${{ matrix.os }} env: SOROBAN_RPC_INTEGRATION_TESTS_ENABLED: true SOROBAN_RPC_INTEGRATION_TESTS_CORE_MAX_SUPPORTED_PROTOCOL: ${{ matrix.protocol-version }} SOROBAN_RPC_INTEGRATION_TESTS_CAPTIVE_CORE_BIN: /usr/bin/stellar-core - PROTOCOL_20_CORE_DEBIAN_PKG_VERSION: 21.1.0-1909.rc1.b3aeb14cc.focal - PROTOCOL_20_CORE_DOCKER_IMG: stellar/stellar-core:21.1.0-1909.rc1.b3aeb14cc.focal - PROTOCOL_21_CORE_DEBIAN_PKG_VERSION: 21.1.0-1909.rc1.b3aeb14cc.focal - PROTOCOL_21_CORE_DOCKER_IMG: stellar/stellar-core:21.1.0-1909.rc1.b3aeb14cc.focal + PROTOCOL_21_CORE_DEBIAN_PKG_VERSION: 22.0.0-2088.rc1.2d8d764cd.focal + PROTOCOL_21_CORE_DOCKER_IMG: stellar/stellar-core:22.0.0-2088.rc1.2d8d764cd.focal + PROTOCOL_22_CORE_DEBIAN_PKG_VERSION: 22.0.0-2088.rc1.2d8d764cd.focal + PROTOCOL_22_CORE_DOCKER_IMG: stellar/stellar-core:22.0.0-2088.rc1.2d8d764cd.focal + steps: - uses: actions/checkout@v4 with: diff --git a/Cargo.lock b/Cargo.lock index d90f8a22..f725d384 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,18 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -47,6 +59,130 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "autocfg" version = "1.1.0" @@ -220,7 +356,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -244,7 +380,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.39", ] [[package]] @@ -255,9 +391,15 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.39", ] +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + [[package]] name = "der" version = "0.7.8" @@ -278,6 +420,17 @@ dependencies = [ "serde", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_arbitrary" version = "1.3.2" @@ -286,7 +439,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -462,11 +615,23 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", +] [[package]] name = "hex" @@ -549,6 +714,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.11.0" @@ -627,6 +801,23 @@ dependencies = [ "adler", ] +[[package]] +name = "multi-stash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685a9ac4b61f4e728e1d2c6a7844609c16527aeb5e6c865915c08e619c16410f" + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -641,7 +832,7 @@ checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -735,8 +926,10 @@ dependencies = [ "rand", "serde_json", "sha2", - "soroban-env-host", - "soroban-simulation", + "soroban-env-host 21.2.0", + "soroban-env-host 22.0.0", + "soroban-simulation 21.2.0", + "soroban-simulation 22.0.0", ] [[package]] @@ -863,7 +1056,7 @@ checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -904,7 +1097,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -946,39 +1139,67 @@ checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "soroban-builtin-sdk-macros" -version = "21.1.0" +version = "21.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084aab008009e712c445a9d7eab837a86559a6c2341f30bc4f33e9b258947688" +checksum = "44877373b3dc6c662377cb1600e3a62706d75e484b6064f9cd22e467c676b159" dependencies = [ - "itertools", + "itertools 0.11.0", "proc-macro2", "quote", - "syn", + "syn 2.0.39", +] + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "22.0.0" +source = "git+https://github.com/stellar/rs-soroban-env?rev=0497816694bef2b103494c8c61b7c8a06a72c7d3#0497816694bef2b103494c8c61b7c8a06a72c7d3" +dependencies = [ + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 2.0.39", ] [[package]] name = "soroban-env-common" -version = "21.1.0" +version = "21.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c16ee889fe99d6828bf3ffac00c84382793c31d62682401dbfa3e1b512f0c542" +checksum = "590add16843a61b01844e19e89bccaaee6aa21dc76809017b0662c17dc139ee9" +dependencies = [ + "arbitrary", + "crate-git-revision", + "ethnum", + "num-derive", + "num-traits", + "soroban-env-macros 21.2.0", + "soroban-wasmi 0.31.1-soroban.20.0.1", + "static_assertions", + "stellar-xdr 21.2.0", + "wasmparser", +] + +[[package]] +name = "soroban-env-common" +version = "22.0.0" +source = "git+https://github.com/stellar/rs-soroban-env?rev=0497816694bef2b103494c8c61b7c8a06a72c7d3#0497816694bef2b103494c8c61b7c8a06a72c7d3" dependencies = [ "arbitrary", "crate-git-revision", "ethnum", "num-derive", "num-traits", - "soroban-env-macros", - "soroban-wasmi", + "soroban-env-macros 22.0.0", + "soroban-wasmi 0.36.0-soroban.22.0.0", "static_assertions", - "stellar-xdr", + "stellar-xdr 22.0.0", "wasmparser", ] [[package]] name = "soroban-env-host" -version = "21.1.0" +version = "21.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b73f48ae8081ecfb6c0d56c67f139c52cb6d5b6800d5e1adb9eef8e14a155b9" +checksum = "4e25aaffe0c62eb65e0e349f725b4b8b13ad0764d78a15aab5bbccb5c4797726" dependencies = [ "backtrace", "curve25519-dalek", @@ -999,38 +1220,99 @@ dependencies = [ "sec1", "sha2", "sha3", - "soroban-builtin-sdk-macros", - "soroban-env-common", - "soroban-wasmi", + "soroban-builtin-sdk-macros 21.2.0", + "soroban-env-common 21.2.0", + "soroban-wasmi 0.31.1-soroban.20.0.1", + "static_assertions", + "stellar-strkey 0.0.8", + "wasmparser", +] + +[[package]] +name = "soroban-env-host" +version = "22.0.0" +source = "git+https://github.com/stellar/rs-soroban-env?rev=0497816694bef2b103494c8c61b7c8a06a72c7d3#0497816694bef2b103494c8c61b7c8a06a72c7d3" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "curve25519-dalek", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "generic-array", + "getrandom", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "p256", + "rand", + "rand_chacha", + "sec1", + "sha2", + "sha3", + "soroban-builtin-sdk-macros 22.0.0", + "soroban-env-common 22.0.0", + "soroban-wasmi 0.36.0-soroban.22.0.0", "static_assertions", - "stellar-strkey", + "stellar-strkey 0.0.9", "wasmparser", ] [[package]] name = "soroban-env-macros" -version = "21.1.0" +version = "21.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06d0581e3aba14892ee0ce63788f3d3e5d9eb1ab18906a9b7c66d77dae9e9fea" +checksum = "3e16b761459fdf3c4b62b24df3941498d14e5246e6fadfb4774ed8114d243aa4" +dependencies = [ + "itertools 0.11.0", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr 21.2.0", + "syn 2.0.39", +] + +[[package]] +name = "soroban-env-macros" +version = "22.0.0" +source = "git+https://github.com/stellar/rs-soroban-env?rev=0497816694bef2b103494c8c61b7c8a06a72c7d3#0497816694bef2b103494c8c61b7c8a06a72c7d3" dependencies = [ - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "serde", "serde_json", - "stellar-xdr", - "syn", + "stellar-xdr 22.0.0", + "syn 2.0.39", ] [[package]] name = "soroban-simulation" -version = "21.1.0" +version = "21.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a21415ec408b0632763e7a6302a041701e06b1491dbe14fc0b8a8a1a927980" +checksum = "b5869ccbe217b54b2dfc762db9cb8bf1ae28f056a88e75acdab6e8418df8dcdc" +dependencies = [ + "anyhow", + "rand", + "soroban-env-host 21.2.0", + "static_assertions", + "thiserror", +] + +[[package]] +name = "soroban-simulation" +version = "22.0.0" +source = "git+https://github.com/stellar/rs-soroban-env?rev=0497816694bef2b103494c8c61b7c8a06a72c7d3#0497816694bef2b103494c8c61b7c8a06a72c7d3" dependencies = [ "anyhow", "rand", - "soroban-env-host", + "soroban-env-host 22.0.0", "static_assertions", "thiserror", ] @@ -1044,7 +1326,23 @@ dependencies = [ "smallvec", "spin", "wasmi_arena", - "wasmi_core", + "wasmi_core 0.13.0", + "wasmparser-nostd", +] + +[[package]] +name = "soroban-wasmi" +version = "0.36.0-soroban.22.0.0" +source = "git+https://github.com/stellar/wasmi?rev=122a74a7c491929e5ac9de876099154ef7c06d06#122a74a7c491929e5ac9de876099154ef7c06d06" +dependencies = [ + "arrayvec", + "multi-stash", + "num-derive", + "num-traits", + "smallvec", + "spin", + "wasmi_collections", + "wasmi_core 0.36.0-soroban.22.0.0", "wasmparser-nostd", ] @@ -1081,11 +1379,35 @@ dependencies = [ "thiserror", ] +[[package]] +name = "stellar-strkey" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3aa3ed00e70082cb43febc1c2afa5056b9bb3e348bbb43d0cd0aa88a611144" +dependencies = [ + "crate-git-revision", + "data-encoding", + "thiserror", +] + [[package]] name = "stellar-xdr" -version = "21.1.0" +version = "21.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec43c9c5ae7ec7b6ac9e263b6d5b9e3781aa05ba3a1c05f6e70701c5c6600665" +checksum = "2675a71212ed39a806e415b0dbf4702879ff288ec7f5ee996dda42a135512b50" +dependencies = [ + "arbitrary", + "base64 0.13.1", + "crate-git-revision", + "escape-bytes", + "hex", + "stellar-strkey 0.0.8", +] + +[[package]] +name = "stellar-xdr" +version = "22.0.0" +source = "git+https://github.com/stellar/rs-stellar-xdr.git?rev=b5516843b6379e4e29520bf2ba156484f62edc46#b5516843b6379e4e29520bf2ba156484f62edc46" dependencies = [ "arbitrary", "base64 0.13.1", @@ -1094,7 +1416,18 @@ dependencies = [ "hex", "serde", "serde_with", - "stellar-strkey", + "stellar-strkey 0.0.9", +] + +[[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.3", + "serde", ] [[package]] @@ -1109,6 +1442,17 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.39" @@ -1137,7 +1481,7 @@ checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -1216,7 +1560,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.39", "wasm-bindgen-shared", ] @@ -1238,7 +1582,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1255,6 +1599,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" +[[package]] +name = "wasmi_collections" +version = "0.36.0-soroban.22.0.0" +source = "git+https://github.com/stellar/wasmi?rev=122a74a7c491929e5ac9de876099154ef7c06d06#122a74a7c491929e5ac9de876099154ef7c06d06" +dependencies = [ + "ahash", + "hashbrown 0.14.3", + "string-interner", +] + [[package]] name = "wasmi_core" version = "0.13.0" @@ -1267,6 +1621,17 @@ dependencies = [ "paste", ] +[[package]] +name = "wasmi_core" +version = "0.36.0-soroban.22.0.0" +source = "git+https://github.com/stellar/wasmi?rev=122a74a7c491929e5ac9de876099154ef7c06d06#122a74a7c491929e5ac9de876099154ef7c06d06" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + [[package]] name = "wasmparser" version = "0.116.1" @@ -1279,9 +1644,9 @@ dependencies = [ [[package]] name = "wasmparser-nostd" -version = "0.100.1" +version = "0.100.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" dependencies = [ "indexmap-nostd", ] @@ -1370,8 +1735,27 @@ dependencies = [ "rand", "serde_json", "sha2", - "soroban-env-host", - "stellar-xdr", + "stellar-xdr 22.0.0", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", ] [[package]] @@ -1379,3 +1763,17 @@ name = "zeroize" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] diff --git a/Cargo.toml b/Cargo.toml index bdd76994..1fa82bf8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,14 +9,30 @@ members = [ [workspace.package] rust-version = "1.74.0" -[workspace.dependencies.soroban-env-host] -version = "=21.1.0" +[workspace.dependencies.soroban-env-host-prev] +package = "soroban-env-host" +version = "=21.2.0" -[workspace.dependencies.soroban-simulation] -version = "=21.1.0" +[workspace.dependencies.soroban-env-host-curr] +package = "soroban-env-host" +version = "=22.0.0" +git = "https://github.com/stellar/rs-soroban-env" +rev = "0497816694bef2b103494c8c61b7c8a06a72c7d3" + +[workspace.dependencies.soroban-simulation-prev] +package = "soroban-simulation" +version = "=21.2.0" + +[workspace.dependencies.soroban-simulation-curr] +package = "soroban-simulation" +version = "=22.0.0" +git = "https://github.com/stellar/rs-soroban-env" +rev = "0497816694bef2b103494c8c61b7c8a06a72c7d3" [workspace.dependencies.stellar-xdr] -version = "=21.1.0" +version = "=22.0.0" +git = "https://github.com/stellar/rs-stellar-xdr.git" +rev = "b5516843b6379e4e29520bf2ba156484f62edc46" features = [ "serde" ] [workspace.dependencies] diff --git a/cmd/soroban-rpc/internal/config/main.go b/cmd/soroban-rpc/internal/config/main.go index 4282d824..26a38802 100644 --- a/cmd/soroban-rpc/internal/config/main.go +++ b/cmd/soroban-rpc/internal/config/main.go @@ -26,7 +26,6 @@ type Config struct { CoreRequestTimeout time.Duration DefaultEventsLimit uint DefaultTransactionsLimit uint - EventLedgerRetentionWindow uint32 FriendbotURL string HistoryArchiveURLs []string HistoryArchiveUserAgent string @@ -42,7 +41,6 @@ type Config struct { PreflightEnableDebug bool SQLiteDBPath string HistoryRetentionWindow uint32 - TransactionLedgerRetentionWindow uint32 SorobanFeeStatsLedgerRetentionWindow uint32 ClassicFeeStatsLedgerRetentionWindow uint32 RequestBacklogGlobalQueueLimit uint @@ -115,13 +113,6 @@ func (cfg *Config) SetValues(lookupEnv func(string) (string, bool)) error { } } - // Set to the maximum as a compromise until we deprecate the transaction/event flags - cfg.HistoryRetentionWindow = max( - cfg.HistoryRetentionWindow, - cfg.EventLedgerRetentionWindow, - cfg.TransactionLedgerRetentionWindow, - ) - return nil } diff --git a/cmd/soroban-rpc/internal/config/options.go b/cmd/soroban-rpc/internal/config/options.go index b04793a5..5fde5f6f 100644 --- a/cmd/soroban-rpc/internal/config/options.go +++ b/cmd/soroban-rpc/internal/config/options.go @@ -218,32 +218,10 @@ func (cfg *Config) options() Options { Name: "history-retention-window", Usage: fmt.Sprintf( "configures history retention window for transactions and events, expressed in number of ledgers,"+ - " the default value is %d which corresponds to about 24 hours of history", - OneDayOfLedgers), + " the default value is %d which corresponds to about 7 days of history", + SevenDayOfLedgers), ConfigKey: &cfg.HistoryRetentionWindow, - DefaultValue: uint32(OneDayOfLedgers), - Validate: positive, - }, - // TODO: remove - { - Name: "event-retention-window", - Usage: fmt.Sprintf( - "(Deprecated, overidden by history-retention-window) configures the event retention window expressed in number of ledgers,"+ - " the default value is %d which corresponds to about 24 hours of history", - OneDayOfLedgers), - ConfigKey: &cfg.EventLedgerRetentionWindow, - DefaultValue: uint32(OneDayOfLedgers), - Validate: positive, - }, - // TODO: remove - { - Name: "transaction-retention-window", - Usage: fmt.Sprintf( - "(Deprecated, overidden by history-retention-window) configures the transaction retention window expressed in number of ledgers,"+ - " the default value is %d which corresponds to about 24 hours of history", - OneDayOfLedgers), - ConfigKey: &cfg.TransactionLedgerRetentionWindow, - DefaultValue: uint32(OneDayOfLedgers), + DefaultValue: uint32(SevenDayOfLedgers), Validate: positive, }, { diff --git a/cmd/soroban-rpc/internal/config/toml_test.go b/cmd/soroban-rpc/internal/config/toml_test.go index b2d2d01d..2e71fd87 100644 --- a/cmd/soroban-rpc/internal/config/toml_test.go +++ b/cmd/soroban-rpc/internal/config/toml_test.go @@ -93,9 +93,9 @@ func TestBasicTomlWriting(t *testing.T) { // comment when outputting multi-line comments, which go-toml does *not* do // by default. assert.Contains(t, out, - `# (Deprecated, overidden by history-retention-window) configures the event -# retention window expressed in number of ledgers, the default value is 17280 -# which corresponds to about 24 hours of history`) + `# configures history retention window for transactions and events, expressed in +# number of ledgers, the default value is 120960 which corresponds to about 7 +# days of history`) } func TestRoundTrip(t *testing.T) { diff --git a/cmd/soroban-rpc/internal/db/db.go b/cmd/soroban-rpc/internal/db/db.go index 81a297fe..63b7af59 100644 --- a/cmd/soroban-rpc/internal/db/db.go +++ b/cmd/soroban-rpc/internal/db/db.go @@ -172,11 +172,11 @@ type ReadWriterMetrics struct { } type readWriter struct { - log *log.Entry - db *DB - maxBatchSize int - ledgerRetentionWindow uint32 - passphrase string + log *log.Entry + db *DB + maxBatchSize int + historyRetentionWindow uint32 + passphrase string metrics ReadWriterMetrics } @@ -190,7 +190,7 @@ func NewReadWriter( db *DB, daemon interfaces.Daemon, maxBatchSize int, - ledgerRetentionWindow uint32, + historyRetentionWindow uint32, networkPassphrase string, ) ReadWriter { // a metric for measuring latency of transaction store operations @@ -212,11 +212,11 @@ func NewReadWriter( daemon.MetricsRegistry().MustRegister(txDurationMetric, txCountMetric) return &readWriter{ - log: log, - db: db, - maxBatchSize: maxBatchSize, - ledgerRetentionWindow: ledgerRetentionWindow, - passphrase: networkPassphrase, + log: log, + db: db, + maxBatchSize: maxBatchSize, + historyRetentionWindow: historyRetentionWindow, + passphrase: networkPassphrase, metrics: ReadWriterMetrics{ TxIngestDuration: txDurationMetric.With(prometheus.Labels{"operation": "ingest"}), TxCount: txCountMetric, @@ -243,10 +243,10 @@ func (rw *readWriter) NewTx(ctx context.Context) (WriteTx, error) { _, err := db.ExecRaw(ctx, "PRAGMA wal_checkpoint(TRUNCATE)") return err }, - tx: txSession, - stmtCache: stmtCache, - ledgerRetentionWindow: rw.ledgerRetentionWindow, - ledgerWriter: ledgerWriter{stmtCache: stmtCache}, + tx: txSession, + stmtCache: stmtCache, + historyRetentionWindow: rw.historyRetentionWindow, + ledgerWriter: ledgerWriter{stmtCache: stmtCache}, ledgerEntryWriter: ledgerEntryWriter{ stmtCache: stmtCache, buffer: xdr.NewEncodingBuffer(), @@ -275,15 +275,15 @@ func (rw *readWriter) NewTx(ctx context.Context) (WriteTx, error) { } type writeTx struct { - globalCache *dbCache - postCommit func() error - tx db.SessionInterface - stmtCache *sq.StmtCache - ledgerEntryWriter ledgerEntryWriter - ledgerWriter ledgerWriter - txWriter transactionHandler - eventWriter eventHandler - ledgerRetentionWindow uint32 + globalCache *dbCache + postCommit func() error + tx db.SessionInterface + stmtCache *sq.StmtCache + ledgerEntryWriter ledgerEntryWriter + ledgerWriter ledgerWriter + txWriter transactionHandler + eventWriter eventHandler + historyRetentionWindow uint32 } func (w writeTx) LedgerEntryWriter() LedgerEntryWriter { @@ -310,14 +310,14 @@ func (w writeTx) Commit(ledgerCloseMeta xdr.LedgerCloseMeta) error { return err } - if err := w.ledgerWriter.trimLedgers(ledgerSeq, w.ledgerRetentionWindow); err != nil { + if err := w.ledgerWriter.trimLedgers(ledgerSeq, w.historyRetentionWindow); err != nil { return err } - if err := w.txWriter.trimTransactions(ledgerSeq, w.ledgerRetentionWindow); err != nil { + if err := w.txWriter.trimTransactions(ledgerSeq, w.historyRetentionWindow); err != nil { return err } - if err := w.eventWriter.trimEvents(ledgerSeq, w.ledgerRetentionWindow); err != nil { + if err := w.eventWriter.trimEvents(ledgerSeq, w.historyRetentionWindow); err != nil { return err } diff --git a/cmd/soroban-rpc/internal/integrationtest/infrastructure/test.go b/cmd/soroban-rpc/internal/integrationtest/infrastructure/test.go index 0a7b91ee..a800bd0e 100644 --- a/cmd/soroban-rpc/internal/integrationtest/infrastructure/test.go +++ b/cmd/soroban-rpc/internal/integrationtest/infrastructure/test.go @@ -35,7 +35,7 @@ import ( const ( StandaloneNetworkPassphrase = "Standalone Network ; February 2017" - MaxSupportedProtocolVersion = 21 + MaxSupportedProtocolVersion = 22 FriendbotURL = "http://localhost:8000/friendbot" // Needed when Core is run with ARTIFICIALLY_ACCELERATE_TIME_FOR_TESTING=true checkpointFrequency = 8 diff --git a/cmd/soroban-rpc/internal/integrationtest/simulate_transaction_test.go b/cmd/soroban-rpc/internal/integrationtest/simulate_transaction_test.go index eab1ea43..5afc9a66 100644 --- a/cmd/soroban-rpc/internal/integrationtest/simulate_transaction_test.go +++ b/cmd/soroban-rpc/internal/integrationtest/simulate_transaction_test.go @@ -32,8 +32,6 @@ func TestSimulateTransactionSucceeds(t *testing.T) { contractHashBytes := xdr.ScBytes(contractHash[:]) expectedXdr := xdr.ScVal{Type: xdr.ScValTypeScvBytes, Bytes: &contractHashBytes} require.Greater(t, result.LatestLedger, uint32(0)) - require.Greater(t, result.Cost.CPUInstructions, uint64(0)) - require.Greater(t, result.Cost.MemoryBytes, uint64(0)) expectedTransactionData := xdr.SorobanTransactionData{ Resources: xdr.SorobanResources{ diff --git a/cmd/soroban-rpc/internal/methods/get_transaction.go b/cmd/soroban-rpc/internal/methods/get_transaction.go index 556f23cd..e6882b96 100644 --- a/cmd/soroban-rpc/internal/methods/get_transaction.go +++ b/cmd/soroban-rpc/internal/methods/get_transaction.go @@ -4,7 +4,6 @@ import ( "context" "encoding/base64" "encoding/hex" - "encoding/json" "errors" "fmt" @@ -30,8 +29,6 @@ const ( // GetTransactionResponse is the response for the Soroban-RPC getTransaction() endpoint type GetTransactionResponse struct { - // Status is one of: TransactionSuccess, TransactionNotFound, or TransactionFailed. - Status string `json:"status"` // LatestLedger is the latest ledger stored in Soroban-RPC. LatestLedger uint32 `json:"latestLedger"` // LatestLedgerCloseTime is the unix timestamp of when the latest ledger was closed. @@ -41,32 +38,8 @@ type GetTransactionResponse struct { // LatestLedgerCloseTime is the unix timestamp of when the oldest ledger was closed. OldestLedgerCloseTime int64 `json:"oldestLedgerCloseTime,string"` - // The fields below are only present if Status is not TransactionNotFound. - - // ApplicationOrder is the index of the transaction among all the transactions - // for that ledger. - ApplicationOrder int32 `json:"applicationOrder,omitempty"` - // FeeBump indicates whether the transaction is a feebump transaction - FeeBump bool `json:"feeBump,omitempty"` - // EnvelopeXDR is the TransactionEnvelope XDR value. - EnvelopeXDR string `json:"envelopeXdr,omitempty"` - EnvelopeJSON json.RawMessage `json:"envelopeJson,omitempty"` - // ResultXDR is the TransactionResult XDR value. - ResultXDR string `json:"resultXdr,omitempty"` - ResultJSON json.RawMessage `json:"resultJson,omitempty"` - // ResultMetaXDR is the TransactionMeta XDR value. - ResultMetaXDR string `json:"resultMetaXdr,omitempty"` - ResultMetaJSON json.RawMessage `json:"resultMetaJson,omitempty"` - - // Ledger is the sequence of the ledger which included the transaction. - Ledger uint32 `json:"ledger,omitempty"` - // LedgerCloseTime is the unix timestamp of when the transaction was included in the ledger. - LedgerCloseTime int64 `json:"createdAt,string,omitempty"` - - // DiagnosticEventsXDR is present only if Status is equal to TransactionFailed. - // DiagnosticEventsXDR is a base64-encoded slice of xdr.DiagnosticEvent - DiagnosticEventsXDR []string `json:"diagnosticEventsXdr,omitempty"` - DiagnosticEventsJSON []json.RawMessage `json:"diagnosticEventsJson,omitempty"` + // Many of the fields below are only present if Status is not TransactionNotFound. + TransactionInfo } type GetTransactionRequest struct { diff --git a/cmd/soroban-rpc/internal/methods/get_transaction_test.go b/cmd/soroban-rpc/internal/methods/get_transaction_test.go index 1c308e58..38e6d5a3 100644 --- a/cmd/soroban-rpc/internal/methods/get_transaction_test.go +++ b/cmd/soroban-rpc/internal/methods/get_transaction_test.go @@ -35,7 +35,11 @@ func TestGetTransaction(t *testing.T) { hash := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" tx, err := GetTransaction(ctx, log, store, ledgerReader, GetTransactionRequest{hash, ""}) require.NoError(t, err) - require.Equal(t, GetTransactionResponse{Status: TransactionStatusNotFound}, tx) + require.Equal(t, GetTransactionResponse{ + TransactionInfo: TransactionInfo{ + Status: TransactionStatusNotFound, + }, + }, tx) meta := txMeta(1, true) require.NoError(t, store.InsertTransactions(meta)) @@ -52,19 +56,21 @@ func TestGetTransaction(t *testing.T) { expectedTxMeta, err := xdr.MarshalBase64(meta.V1.TxProcessing[0].TxApplyProcessing) require.NoError(t, err) require.Equal(t, GetTransactionResponse{ - Status: TransactionStatusSuccess, LatestLedger: 101, LatestLedgerCloseTime: 2625, OldestLedger: 101, OldestLedgerCloseTime: 2625, - ApplicationOrder: 1, - FeeBump: false, - EnvelopeXDR: expectedEnvelope, - ResultXDR: expectedTxResult, - ResultMetaXDR: expectedTxMeta, - Ledger: 101, - LedgerCloseTime: 2625, - DiagnosticEventsXDR: []string{}, + TransactionInfo: TransactionInfo{ + Status: TransactionStatusSuccess, + ApplicationOrder: 1, + FeeBump: false, + EnvelopeXDR: expectedEnvelope, + ResultXDR: expectedTxResult, + ResultMetaXDR: expectedTxMeta, + Ledger: 101, + LedgerCloseTime: 2625, + DiagnosticEventsXDR: []string{}, + }, }, tx) // ingest another (failed) transaction @@ -75,19 +81,21 @@ func TestGetTransaction(t *testing.T) { tx, err = GetTransaction(ctx, log, store, ledgerReader, GetTransactionRequest{hash, ""}) require.NoError(t, err) require.Equal(t, GetTransactionResponse{ - Status: TransactionStatusSuccess, LatestLedger: 102, LatestLedgerCloseTime: 2650, OldestLedger: 101, OldestLedgerCloseTime: 2625, - ApplicationOrder: 1, - FeeBump: false, - EnvelopeXDR: expectedEnvelope, - ResultXDR: expectedTxResult, - ResultMetaXDR: expectedTxMeta, - Ledger: 101, - LedgerCloseTime: 2625, - DiagnosticEventsXDR: []string{}, + TransactionInfo: TransactionInfo{ + Status: TransactionStatusSuccess, + ApplicationOrder: 1, + FeeBump: false, + EnvelopeXDR: expectedEnvelope, + ResultXDR: expectedTxResult, + ResultMetaXDR: expectedTxMeta, + Ledger: 101, + LedgerCloseTime: 2625, + DiagnosticEventsXDR: []string{}, + }, }, tx) // the new transaction should also be there @@ -104,19 +112,21 @@ func TestGetTransaction(t *testing.T) { tx, err = GetTransaction(ctx, log, store, ledgerReader, GetTransactionRequest{hash, ""}) require.NoError(t, err) require.Equal(t, GetTransactionResponse{ - Status: TransactionStatusFailed, LatestLedger: 102, LatestLedgerCloseTime: 2650, OldestLedger: 101, OldestLedgerCloseTime: 2625, - ApplicationOrder: 1, - FeeBump: false, - EnvelopeXDR: expectedEnvelope, - ResultXDR: expectedTxResult, - ResultMetaXDR: expectedTxMeta, - Ledger: 102, - LedgerCloseTime: 2650, - DiagnosticEventsXDR: []string{}, + TransactionInfo: TransactionInfo{ + Status: TransactionStatusFailed, + ApplicationOrder: 1, + FeeBump: false, + EnvelopeXDR: expectedEnvelope, + ResultXDR: expectedTxResult, + ResultMetaXDR: expectedTxMeta, + Ledger: 102, + LedgerCloseTime: 2650, + DiagnosticEventsXDR: []string{}, + }, }, tx) // Test Txn with events @@ -141,19 +151,21 @@ func TestGetTransaction(t *testing.T) { tx, err = GetTransaction(ctx, log, store, ledgerReader, GetTransactionRequest{hash, ""}) require.NoError(t, err) require.Equal(t, GetTransactionResponse{ - Status: TransactionStatusSuccess, + TransactionInfo: TransactionInfo{ + Status: TransactionStatusSuccess, + ApplicationOrder: 1, + FeeBump: false, + EnvelopeXDR: expectedEnvelope, + ResultXDR: expectedTxResult, + ResultMetaXDR: expectedTxMeta, + Ledger: 103, + LedgerCloseTime: 2675, + DiagnosticEventsXDR: []string{expectedEventsMeta}, + }, LatestLedger: 103, LatestLedgerCloseTime: 2675, OldestLedger: 101, OldestLedgerCloseTime: 2625, - ApplicationOrder: 1, - FeeBump: false, - EnvelopeXDR: expectedEnvelope, - ResultXDR: expectedTxResult, - ResultMetaXDR: expectedTxMeta, - Ledger: 103, - LedgerCloseTime: 2675, - DiagnosticEventsXDR: []string{expectedEventsMeta}, }, tx) } diff --git a/cmd/soroban-rpc/internal/methods/get_transactions.go b/cmd/soroban-rpc/internal/methods/get_transactions.go index d32fb61d..8c3f10a0 100644 --- a/cmd/soroban-rpc/internal/methods/get_transactions.go +++ b/cmd/soroban-rpc/internal/methods/get_transactions.go @@ -56,7 +56,7 @@ func (req GetTransactionsRequest) isValid(maxLimit uint, ledgerRange ledgerbucke } type TransactionInfo struct { - // Status is one of: TransactionSuccess, TransactionFailed. + // Status is one of: TransactionSuccess, TransactionFailed, TransactionNotFound. Status string `json:"status"` // ApplicationOrder is the index of the transaction among all the transactions // for that ledger. @@ -79,7 +79,7 @@ type TransactionInfo struct { // Ledger is the sequence of the ledger which included the transaction. Ledger uint32 `json:"ledger"` // LedgerCloseTime is the unix timestamp of when the transaction was included in the ledger. - LedgerCloseTime int64 `json:"createdAt"` + LedgerCloseTime int64 `json:"createdAt,string"` } // GetTransactionsResponse encapsulates the response structure for getTransactions queries. diff --git a/cmd/soroban-rpc/internal/methods/get_version_info.go b/cmd/soroban-rpc/internal/methods/get_version_info.go index 2af129de..038bf54a 100644 --- a/cmd/soroban-rpc/internal/methods/get_version_info.go +++ b/cmd/soroban-rpc/internal/methods/get_version_info.go @@ -14,12 +14,11 @@ import ( ) type GetVersionInfoResponse struct { - Version string `json:"version"` - // TODO: casing to be fixed by https://github.com/stellar/soroban-rpc/pull/164 - CommitHash string `json:"commit_hash"` //nolint:tagliatelle - BuildTimestamp string `json:"build_time_stamp"` //nolint:tagliatelle - CaptiveCoreVersion string `json:"captive_core_version"` //nolint:tagliatelle - ProtocolVersion uint32 `json:"protocol_version"` //nolint:tagliatelle + Version string `json:"version"` + CommitHash string `json:"commitHash"` + BuildTimestamp string `json:"buildTimestamp"` + CaptiveCoreVersion string `json:"captiveCoreVersion"` + ProtocolVersion uint32 `json:"protocolVersion"` } func NewGetVersionInfoHandler( diff --git a/cmd/soroban-rpc/internal/methods/simulate_transaction.go b/cmd/soroban-rpc/internal/methods/simulate_transaction.go index b9968f61..7e8a3e6d 100644 --- a/cmd/soroban-rpc/internal/methods/simulate_transaction.go +++ b/cmd/soroban-rpc/internal/methods/simulate_transaction.go @@ -25,13 +25,8 @@ type SimulateTransactionRequest struct { Format string `json:"xdrFormat,omitempty"` } -type SimulateTransactionCost struct { - CPUInstructions uint64 `json:"cpuInsns,string"` - MemoryBytes uint64 `json:"memBytes,string"` -} - -// SimulateHostFunctionResult contains the simulation result of each HostFunction -// within the single InvokeHostFunctionOp allowed in a Transaction +// SimulateHostFunctionResult contains the simulation result of each HostFunction within the single +// InvokeHostFunctionOp allowed in a Transaction type SimulateHostFunctionResult struct { AuthXDR *[]string `json:"auth,omitempty"` AuthJSON []json.RawMessage `json:"authJson,omitempty"` @@ -191,7 +186,7 @@ func (l *LedgerEntryChange) jsonXdrDiff(diff preflight.XDRDiff, key xdr.LedgerKe } // LedgerEntryChange designates a change in a ledger entry. Before and After cannot be omitted at the same time. -// If Before is omitted, it constitutes a creation, if After is omitted, it constitutes a delation. +// If Before is omitted, it constitutes a creation, if After is omitted, it constitutes a deletion. type LedgerEntryChange struct { Type LedgerEntryChangeType `json:"type"` @@ -217,8 +212,6 @@ type SimulateTransactionResponse struct { MinResourceFee int64 `json:"minResourceFee,string,omitempty"` // an array of the individual host function call results Results []SimulateHostFunctionResult `json:"results,omitempty"` - // the effective cpu and memory cost of the invoked transaction execution. - Cost SimulateTransactionCost `json:"cost,omitempty"` // If present, it indicates that a prior RestoreFootprint is required RestorePreamble *RestorePreamble `json:"restorePreamble,omitempty"` // If present, it indicates how the state (ledger entries) will change as a result of the transaction execution. @@ -392,13 +385,9 @@ func NewSimulateTransactionHandler(logger *log.Entry, ledgerEntryReader db.Ledge } simResp := SimulateTransactionResponse{ - Error: result.Error, - Results: results, - MinResourceFee: result.MinFee, - Cost: SimulateTransactionCost{ - CPUInstructions: result.CPUInstructions, - MemoryBytes: result.MemoryBytes, - }, + Error: result.Error, + Results: results, + MinResourceFee: result.MinFee, LatestLedger: latestLedger, RestorePreamble: restorePreamble, StateChanges: stateChanges, diff --git a/cmd/soroban-rpc/lib/preflight/Cargo.toml b/cmd/soroban-rpc/lib/preflight/Cargo.toml index 28e05689..539cddb2 100644 --- a/cmd/soroban-rpc/lib/preflight/Cargo.toml +++ b/cmd/soroban-rpc/lib/preflight/Cargo.toml @@ -15,8 +15,10 @@ sha2 = { workspace = true } # we need the testutils feature in order to get backtraces in the preflight library # when soroban rpc is configured to run with --preflight-enable-debug -soroban-env-host = { workspace = true, features = ["recording_mode", "testutils", "unstable-next-api"]} -soroban-simulation = { workspace = true } +soroban-env-host-prev = { workspace = true, features = ["recording_mode", "testutils", "unstable-next-api"]} +soroban-simulation-prev = { workspace = true } +soroban-env-host-curr = { workspace = true, features = ["recording_mode", "testutils", "unstable-next-api"]} +soroban-simulation-curr = { workspace = true } anyhow = { workspace = true } rand = { workspace = true } diff --git a/cmd/soroban-rpc/lib/preflight/src/lib.rs b/cmd/soroban-rpc/lib/preflight/src/lib.rs index e5834403..fc7b0e35 100644 --- a/cmd/soroban-rpc/lib/preflight/src/lib.rs +++ b/cmd/soroban-rpc/lib/preflight/src/lib.rs @@ -4,37 +4,61 @@ extern crate ffi; extern crate libc; extern crate serde_json; extern crate sha2; -extern crate soroban_env_host; -extern crate soroban_simulation; -use anyhow::{anyhow, bail, Result}; -use sha2::{Digest, Sha256}; +pub(crate) use anyhow::{anyhow, bail, Result}; +pub(crate) use sha2::{Digest, Sha256}; // We really do need everything. #[allow(clippy::wildcard_imports)] use ffi::*; - -use soroban_env_host::storage::EntryWithLiveUntil; -use soroban_env_host::xdr::{ - AccountId, ExtendFootprintTtlOp, Hash, InvokeHostFunctionOp, LedgerEntry, LedgerEntryData, - LedgerFootprint, LedgerKey, LedgerKeyTtl, OperationBody, ReadXdr, ScErrorCode, ScErrorType, - SorobanTransactionData, TtlEntry, WriteXdr, -}; -use soroban_env_host::{HostError, LedgerInfo, DEFAULT_XDR_RW_LIMITS}; -use soroban_simulation::simulation::{ - simulate_extend_ttl_op, simulate_invoke_host_function_op, simulate_restore_op, - InvokeHostFunctionSimulationResult, LedgerEntryDiff, RestoreOpSimulationResult, - SimulationAdjustmentConfig, -}; -use soroban_simulation::{AutoRestoringSnapshotSource, NetworkConfig, SnapshotSourceWithArchive}; +extern crate soroban_env_host_curr; +extern crate soroban_env_host_prev; +extern crate soroban_simulation_curr; +extern crate soroban_simulation_prev; + +// We support two different versions of soroban simutlaneously, switching on the +// protocol version each supports. This is the exact same mechanism we use in +// stellar-core to switch soroban hosts on protocol boundaries, and allows +// synchronously cutting over between significantly different versions of the +// host (or VM) without having to do fine-grained versioning within the VM. +// +// The way it is _accomplished_ is by mounting the same adaptor code (in +// `shared.rs`) at two different paths in the module tree, and then providing +// each with a different binding for the soroban host and simulation code. Any +// function that mentions a type from the soroban host or simulation code must +// be placed in the `shared.rs` file. Code that is host-version-agnostic can +// continue to live in this file. +// +// This is a bit of a hack, but it works well enough for our purposes and works +// around the absence of parametric modules in the Rust language. + +#[path = "."] +mod curr { + pub(crate) use soroban_env_host_curr as soroban_env_host; + pub(crate) use soroban_simulation_curr as soroban_simulation; + #[allow(clippy::duplicate_mod)] + pub(crate) mod shared; + + pub(crate) const PROTOCOL: u32 = soroban_env_host::meta::INTERFACE_VERSION.protocol; +} + +#[path = "."] +mod prev { + pub(crate) use soroban_env_host_prev as soroban_env_host; + pub(crate) use soroban_simulation_prev as soroban_simulation; + #[allow(clippy::duplicate_mod)] + pub(crate) mod shared; + + pub(crate) const PROTOCOL: u32 = soroban_env_host::meta::get_ledger_protocol_version( + soroban_env_host::meta::INTERFACE_VERSION, + ); +} use std::cell::RefCell; -use std::convert::TryFrom; use std::ffi::CString; use std::mem; use std::panic; use std::ptr::null_mut; -use std::rc::Rc; #[repr(C)] #[derive(Copy, Clone)] @@ -47,20 +71,6 @@ pub struct CLedgerInfo { pub bucket_list_size: u64, } -fn fill_ledger_info(c_ledger_info: CLedgerInfo, network_config: &NetworkConfig) -> LedgerInfo { - let network_passphrase = unsafe { from_c_string(c_ledger_info.network_passphrase) }; - let mut ledger_info = LedgerInfo { - protocol_version: c_ledger_info.protocol_version, - sequence_number: c_ledger_info.sequence_number, - timestamp: c_ledger_info.timestamp, - network_id: Sha256::digest(network_passphrase).into(), - base_reserve: c_ledger_info.base_reserve, - ..Default::default() - }; - network_config.fill_config_fields_in_ledger_info(&mut ledger_info); - ledger_info -} - #[repr(C)] #[derive(Copy, Clone)] pub struct CXDRVector { @@ -149,55 +159,6 @@ impl Default for CPreflightResult { } } -impl CPreflightResult { - fn new_from_invoke_host_function( - invoke_hf_result: InvokeHostFunctionSimulationResult, - restore_preamble: Option, - error: String, - ) -> Self { - let mut result = Self { - error: string_to_c(error), - auth: xdr_vec_to_c(&invoke_hf_result.auth), - result: option_xdr_to_c(invoke_hf_result.invoke_result.ok().as_ref()), - min_fee: invoke_hf_result - .transaction_data - .as_ref() - .map_or_else(|| 0, |r| r.resource_fee), - transaction_data: option_xdr_to_c(invoke_hf_result.transaction_data.as_ref()), - // TODO: Diagnostic and contract events should be separated in the response - events: xdr_vec_to_c(&invoke_hf_result.diagnostic_events), - cpu_instructions: u64::from(invoke_hf_result.simulated_instructions), - memory_bytes: u64::from(invoke_hf_result.simulated_memory), - ledger_entry_diff: ledger_entry_diff_vec_to_c(&invoke_hf_result.modified_entries), - ..Default::default() - }; - if let Some(p) = restore_preamble { - result.pre_restore_min_fee = p.transaction_data.resource_fee; - result.pre_restore_transaction_data = xdr_to_c(&p.transaction_data); - }; - result - } - - fn new_from_transaction_data( - transaction_data: Option<&SorobanTransactionData>, - restore_preamble: Option<&RestoreOpSimulationResult>, - error: String, - ) -> Self { - let min_fee = transaction_data.map_or(0, |d| d.resource_fee); - let mut result = Self { - error: string_to_c(error), - transaction_data: option_xdr_to_c(transaction_data), - min_fee, - ..Default::default() - }; - if let Some(p) = restore_preamble { - result.pre_restore_min_fee = p.transaction_data.resource_fee; - result.pre_restore_transaction_data = xdr_to_c(&p.transaction_data); - }; - result - } -} - #[no_mangle] pub extern "C" fn preflight_invoke_hf_op( handle: libc::uintptr_t, // Go Handle to forward to SnapshotSourceGet and SnapshotSourceHas @@ -207,85 +168,32 @@ pub extern "C" fn preflight_invoke_hf_op( resource_config: CResourceConfig, enable_debug: bool, ) -> *mut CPreflightResult { + let proto = ledger_info.protocol_version; catch_preflight_panic(Box::new(move || { - preflight_invoke_hf_op_or_maybe_panic( - handle, - invoke_hf_op, - source_account, - ledger_info, - resource_config, - enable_debug, - ) + if proto <= prev::PROTOCOL { + prev::shared::preflight_invoke_hf_op_or_maybe_panic( + handle, + invoke_hf_op, + source_account, + ledger_info, + resource_config, + enable_debug, + ) + } else if proto == curr::PROTOCOL { + curr::shared::preflight_invoke_hf_op_or_maybe_panic( + handle, + invoke_hf_op, + source_account, + ledger_info, + resource_config, + enable_debug, + ) + } else { + bail!("unsupported protocol version: {}", proto) + } })) } -fn preflight_invoke_hf_op_or_maybe_panic( - handle: libc::uintptr_t, - invoke_hf_op: CXDR, // InvokeHostFunctionOp XDR in base64 - source_account: CXDR, // AccountId XDR in base64 - c_ledger_info: CLedgerInfo, - resource_config: CResourceConfig, - enable_debug: bool, -) -> Result { - let invoke_hf_op = - InvokeHostFunctionOp::from_xdr(unsafe { from_c_xdr(invoke_hf_op) }, DEFAULT_XDR_RW_LIMITS) - .unwrap(); - let source_account = - AccountId::from_xdr(unsafe { from_c_xdr(source_account) }, DEFAULT_XDR_RW_LIMITS).unwrap(); - - let go_storage = Rc::new(GoLedgerStorage::new(handle)); - let network_config = - NetworkConfig::load_from_snapshot(go_storage.as_ref(), c_ledger_info.bucket_list_size)?; - let ledger_info = fill_ledger_info(c_ledger_info, &network_config); - let auto_restore_snapshot = Rc::new(AutoRestoringSnapshotSource::new( - go_storage.clone(), - &ledger_info, - )?); - - let mut adjustment_config = SimulationAdjustmentConfig::default_adjustment(); - // It would be reasonable to extend `resource_config` to be compatible with `adjustment_config` - // in order to let the users customize the resource/fee adjustments in a more granular fashion. - - let instruction_leeway = u32::try_from(resource_config.instruction_leeway)?; - adjustment_config.instructions.additive_factor = adjustment_config - .instructions - .additive_factor - .max(instruction_leeway); - // Here we assume that no input auth means that the user requests the recording auth. - let auth_entries = if invoke_hf_op.auth.is_empty() { - None - } else { - Some(invoke_hf_op.auth.to_vec()) - }; - // Invoke the host function. The user errors should normally be captured in `invoke_hf_result.invoke_result` and - // this should return Err result for misconfigured ledger. - let invoke_hf_result = simulate_invoke_host_function_op( - auto_restore_snapshot.clone(), - &network_config, - &adjustment_config, - &ledger_info, - invoke_hf_op.host_function, - auth_entries, - &source_account, - rand::Rng::gen(&mut rand::thread_rng()), - enable_debug, - )?; - let maybe_restore_result = match &invoke_hf_result.invoke_result { - Ok(_) => auto_restore_snapshot.simulate_restore_keys_op( - &network_config, - &SimulationAdjustmentConfig::default_adjustment(), - &ledger_info, - ), - Err(e) => Err(e.clone().into()), - }; - let error_str = extract_error_string(&maybe_restore_result, go_storage.as_ref()); - Ok(CPreflightResult::new_from_invoke_host_function( - invoke_hf_result, - maybe_restore_result.unwrap_or(None), - error_str, - )) -} - #[no_mangle] pub extern "C" fn preflight_footprint_ttl_op( handle: libc::uintptr_t, // Go Handle to forward to SnapshotSourceGet and SnapshotSourceHas @@ -293,94 +201,26 @@ pub extern "C" fn preflight_footprint_ttl_op( footprint: CXDR, // LedgerFootprint XDR ledger_info: CLedgerInfo, ) -> *mut CPreflightResult { + let proto = ledger_info.protocol_version; catch_preflight_panic(Box::new(move || { - preflight_footprint_ttl_op_or_maybe_panic(handle, op_body, footprint, ledger_info) - })) -} - -fn preflight_footprint_ttl_op_or_maybe_panic( - handle: libc::uintptr_t, - op_body: CXDR, - footprint: CXDR, - c_ledger_info: CLedgerInfo, -) -> Result { - let op_body = OperationBody::from_xdr(unsafe { from_c_xdr(op_body) }, DEFAULT_XDR_RW_LIMITS)?; - let footprint = - LedgerFootprint::from_xdr(unsafe { from_c_xdr(footprint) }, DEFAULT_XDR_RW_LIMITS)?; - let go_storage = Rc::new(GoLedgerStorage::new(handle)); - let network_config = - NetworkConfig::load_from_snapshot(go_storage.as_ref(), c_ledger_info.bucket_list_size)?; - let ledger_info = fill_ledger_info(c_ledger_info, &network_config); - // TODO: It would make for a better UX if the user passed only the necessary fields for every operation. - // That would remove a possibility of providing bad operation body, or a possibility of filling wrong footprint - // field. - match op_body { - OperationBody::ExtendFootprintTtl(extend_op) => { - preflight_extend_ttl_op(&extend_op, footprint.read_only.as_slice(), &go_storage, &network_config, &ledger_info) - } - OperationBody::RestoreFootprint(_) => { - Ok(preflight_restore_op(footprint.read_write.as_slice(), &go_storage, &network_config, &ledger_info)) - } - _ => Err(anyhow!("encountered unsupported operation type: '{:?}', instead of 'ExtendFootprintTtl' or 'RestoreFootprint' operations.", - op_body.discriminant())) - } -} - -fn preflight_extend_ttl_op( - extend_op: &ExtendFootprintTtlOp, - keys_to_extend: &[LedgerKey], - go_storage: &Rc, - network_config: &NetworkConfig, - ledger_info: &LedgerInfo, -) -> Result { - let auto_restore_snapshot = AutoRestoringSnapshotSource::new(go_storage.clone(), ledger_info)?; - let simulation_result = simulate_extend_ttl_op( - &auto_restore_snapshot, - network_config, - &SimulationAdjustmentConfig::default_adjustment(), - ledger_info, - keys_to_extend, - extend_op.extend_to, - ); - let (maybe_transaction_data, maybe_restore_result) = match simulation_result { - Ok(r) => ( - Some(r.transaction_data), - auto_restore_snapshot.simulate_restore_keys_op( - network_config, - &SimulationAdjustmentConfig::default_adjustment(), + if proto <= prev::PROTOCOL { + prev::shared::preflight_footprint_ttl_op_or_maybe_panic( + handle, + op_body, + footprint, ledger_info, - ), - ), - Err(e) => (None, Err(e)), - }; - - let error_str = extract_error_string(&maybe_restore_result, go_storage); - Ok(CPreflightResult::new_from_transaction_data( - maybe_transaction_data.as_ref(), - maybe_restore_result.ok().flatten().as_ref(), - error_str, - )) -} - -fn preflight_restore_op( - keys_to_restore: &[LedgerKey], - go_storage: &Rc, - network_config: &NetworkConfig, - ledger_info: &LedgerInfo, -) -> CPreflightResult { - let simulation_result = simulate_restore_op( - go_storage.as_ref(), - network_config, - &SimulationAdjustmentConfig::default_adjustment(), - ledger_info, - keys_to_restore, - ); - let error_str = extract_error_string(&simulation_result, go_storage.as_ref()); - CPreflightResult::new_from_transaction_data( - simulation_result.ok().map(|r| r.transaction_data).as_ref(), - None, - error_str, - ) + ) + } else if proto == curr::PROTOCOL { + curr::shared::preflight_footprint_ttl_op_or_maybe_panic( + handle, + op_body, + footprint, + ledger_info, + ) + } else { + bail!("unsupported protocol version: {}", proto) + } + })) } fn preflight_error(str: String) -> CPreflightResult { @@ -409,37 +249,6 @@ fn catch_preflight_panic(op: Box Result>) -> *mut Box::into_raw(Box::new(c_preflight_result)) } -// TODO: We could use something like https://github.com/sonos/ffi-convert-rs -// to replace all the free_* , *_to_c and from_c_* functions by implementations of CDrop, -// CReprOf and AsRust -fn xdr_to_c(v: &impl WriteXdr) -> CXDR { - let (xdr, len) = vec_to_c_array(v.to_xdr(DEFAULT_XDR_RW_LIMITS).unwrap()); - CXDR { xdr, len } -} - -fn option_xdr_to_c(v: Option<&impl WriteXdr>) -> CXDR { - v.map_or( - CXDR { - xdr: null_mut(), - len: 0, - }, - xdr_to_c, - ) -} - -fn ledger_entry_diff_to_c(v: &LedgerEntryDiff) -> CXDRDiff { - CXDRDiff { - before: option_xdr_to_c(v.state_before.as_ref()), - after: option_xdr_to_c(v.state_after.as_ref()), - } -} - -fn xdr_vec_to_c(v: &[impl WriteXdr]) -> CXDRVector { - let c_v = v.iter().map(xdr_to_c).collect(); - let (array, len) = vec_to_c_array(c_v); - CXDRVector { array, len } -} - fn string_to_c(str: String) -> *mut libc::c_char { CString::new(str).unwrap().into_raw() } @@ -459,15 +268,6 @@ fn vec_to_c_array(mut v: Vec) -> (*mut T, libc::size_t) { (ptr, len) } -fn ledger_entry_diff_vec_to_c(modified_entries: &[LedgerEntryDiff]) -> CXDRDiffVector { - let c_diffs = modified_entries - .iter() - .map(ledger_entry_diff_to_c) - .collect(); - let (array, len) = vec_to_c_array(c_diffs); - CXDRDiffVector { array, len } -} - /// . /// /// # Safety @@ -557,71 +357,6 @@ impl GoLedgerStorage { unsafe { FreeGoXDR(res) }; Some(v) } - - // Gets a ledger entry by key, including the archived/removed entries. - // The failures of this function are not recoverable and should only happen when - // the underlying storage is somehow corrupted. - fn get_fallible(&self, key: &LedgerKey) -> Result> { - let mut key_xdr = key.to_xdr(DEFAULT_XDR_RW_LIMITS)?; - let Some(xdr) = self.get_xdr_internal(&mut key_xdr) else { - return Ok(None); - }; - - let live_until_ledger_seq = match key { - // TODO: it would probably be more efficient to do all of this in the Go side - // (e.g. it would allow us to query multiple entries at once) - LedgerKey::ContractData(_) | LedgerKey::ContractCode(_) => { - let key_hash: [u8; 32] = Sha256::digest(key_xdr).into(); - let ttl_key = LedgerKey::Ttl(LedgerKeyTtl { - key_hash: Hash(key_hash), - }); - let mut ttl_key_xdr = ttl_key.to_xdr(DEFAULT_XDR_RW_LIMITS)?; - let ttl_entry_xdr = self.get_xdr_internal(&mut ttl_key_xdr).ok_or_else(|| { - anyhow!( - "TTL entry is missing for an entry that should have TTL with key: '{key:?}'" - ) - })?; - let ttl_entry = LedgerEntry::from_xdr(ttl_entry_xdr, DEFAULT_XDR_RW_LIMITS)?; - let LedgerEntryData::Ttl(TtlEntry { - live_until_ledger_seq, - .. - }) = ttl_entry.data - else { - bail!( - "unexpected non-TTL entry '{:?}' has been fetched for TTL key '{:?}'", - ttl_entry, - ttl_key - ); - }; - Some(live_until_ledger_seq) - } - _ => None, - }; - - let entry = LedgerEntry::from_xdr(xdr, DEFAULT_XDR_RW_LIMITS)?; - Ok(Some((Rc::new(entry), live_until_ledger_seq))) - } -} - -impl SnapshotSourceWithArchive for GoLedgerStorage { - fn get_including_archived( - &self, - key: &Rc, - ) -> std::result::Result, HostError> { - let res = self.get_fallible(key.as_ref()); - match res { - Ok(res) => Ok(res), - Err(e) => { - // Store the internal error in the storage as the info won't be propagated from simulation. - if let Ok(mut err) = self.internal_error.try_borrow_mut() { - *err = Some(e); - } - // Errors that occur in storage are not recoverable, so we force host to halt by passing - // it an internal error. - Err((ScErrorType::Storage, ScErrorCode::InternalError).into()) - } - } - } } fn extract_error_string(simulation_result: &Result, go_storage: &GoLedgerStorage) -> String { diff --git a/cmd/soroban-rpc/lib/preflight/src/shared.rs b/cmd/soroban-rpc/lib/preflight/src/shared.rs new file mode 100644 index 00000000..5a8abd4c --- /dev/null +++ b/cmd/soroban-rpc/lib/preflight/src/shared.rs @@ -0,0 +1,377 @@ +// This file is included into the module graph as two different modules: +// +// - crate::prev::shared for the previous protocol +// - crate::curr::shared for the previous protocol +// +// This file is the `shared` part of that path, and there is a different binding +// for `soroban_env_host`` and `soroban_simulation`` in each of the two parent +// modules `crate::prev` and `crate::curr`, corresponding to two different +// releases of soroban. +// +// We therefore import the different bindings for anything we use from +// `soroban_env_host` or `soroban_simulation` from `super::` rather than +// `crate::`. + +use super::soroban_env_host::storage::EntryWithLiveUntil; +use super::soroban_env_host::xdr::{ + AccountId, ExtendFootprintTtlOp, Hash, InvokeHostFunctionOp, LedgerEntry, LedgerEntryData, + LedgerFootprint, LedgerKey, LedgerKeyTtl, OperationBody, ReadXdr, ScErrorCode, ScErrorType, + SorobanTransactionData, TtlEntry, WriteXdr, +}; +use super::soroban_env_host::{HostError, LedgerInfo, DEFAULT_XDR_RW_LIMITS}; +use super::soroban_simulation::simulation::{ + simulate_extend_ttl_op, simulate_invoke_host_function_op, simulate_restore_op, + InvokeHostFunctionSimulationResult, LedgerEntryDiff, RestoreOpSimulationResult, + SimulationAdjustmentConfig, +}; +use super::soroban_simulation::{ + AutoRestoringSnapshotSource, NetworkConfig, SnapshotSourceWithArchive, +}; + +// Any definition that doesn't mention a soroban type in its signature can be +// stored in the common grandparent module `crate` a.k.a. `lib.rs`. Both copies +// of the `shared` module import the same definitions for these. + +use crate::{ + anyhow, bail, extract_error_string, from_c_string, from_c_xdr, string_to_c, vec_to_c_array, + CLedgerInfo, CPreflightResult, CResourceConfig, CXDRDiff, CXDRDiffVector, CXDRVector, Digest, + GoLedgerStorage, Result, Sha256, CXDR, +}; +use std::convert::TryFrom; +use std::ptr::null_mut; +use std::rc::Rc; + +fn fill_ledger_info(c_ledger_info: CLedgerInfo, network_config: &NetworkConfig) -> LedgerInfo { + let network_passphrase = unsafe { from_c_string(c_ledger_info.network_passphrase) }; + let mut ledger_info = LedgerInfo { + protocol_version: c_ledger_info.protocol_version, + sequence_number: c_ledger_info.sequence_number, + timestamp: c_ledger_info.timestamp, + network_id: Sha256::digest(network_passphrase).into(), + base_reserve: c_ledger_info.base_reserve, + ..Default::default() + }; + network_config.fill_config_fields_in_ledger_info(&mut ledger_info); + ledger_info +} + +// This has to be a free function rather than a method on an impl because there +// are two copies of this file mounted in the module tree and we can't define a +// same-named method on a single Self-type twice. +fn new_cpreflight_result_from_invoke_host_function( + invoke_hf_result: InvokeHostFunctionSimulationResult, + restore_preamble: Option, + error: String, +) -> CPreflightResult { + let mut result = CPreflightResult { + error: string_to_c(error), + auth: xdr_vec_to_c(&invoke_hf_result.auth), + result: option_xdr_to_c(invoke_hf_result.invoke_result.ok().as_ref()), + min_fee: invoke_hf_result + .transaction_data + .as_ref() + .map_or_else(|| 0, |r| r.resource_fee), + transaction_data: option_xdr_to_c(invoke_hf_result.transaction_data.as_ref()), + // TODO: Diagnostic and contract events should be separated in the response + events: xdr_vec_to_c(&invoke_hf_result.diagnostic_events), + cpu_instructions: u64::from(invoke_hf_result.simulated_instructions), + memory_bytes: u64::from(invoke_hf_result.simulated_memory), + ledger_entry_diff: ledger_entry_diff_vec_to_c(&invoke_hf_result.modified_entries), + ..Default::default() + }; + if let Some(p) = restore_preamble { + result.pre_restore_min_fee = p.transaction_data.resource_fee; + result.pre_restore_transaction_data = xdr_to_c(&p.transaction_data); + }; + result +} + +// This has to be a free function rather than a method on an impl because there +// are two copies of this file mounted in the module tree and we can't define a +// same-named method on a single Self-type twice. +fn new_cpreflight_result_from_transaction_data( + transaction_data: Option<&SorobanTransactionData>, + restore_preamble: Option<&RestoreOpSimulationResult>, + error: String, +) -> CPreflightResult { + let min_fee = transaction_data.map_or(0, |d| d.resource_fee); + let mut result = CPreflightResult { + error: string_to_c(error), + transaction_data: option_xdr_to_c(transaction_data), + min_fee, + ..Default::default() + }; + if let Some(p) = restore_preamble { + result.pre_restore_min_fee = p.transaction_data.resource_fee; + result.pre_restore_transaction_data = xdr_to_c(&p.transaction_data); + }; + result +} + +pub(crate) fn preflight_invoke_hf_op_or_maybe_panic( + handle: libc::uintptr_t, + invoke_hf_op: CXDR, // InvokeHostFunctionOp XDR in base64 + source_account: CXDR, // AccountId XDR in base64 + c_ledger_info: CLedgerInfo, + resource_config: CResourceConfig, + enable_debug: bool, +) -> Result { + let invoke_hf_op = + InvokeHostFunctionOp::from_xdr(unsafe { from_c_xdr(invoke_hf_op) }, DEFAULT_XDR_RW_LIMITS) + .unwrap(); + let source_account = + AccountId::from_xdr(unsafe { from_c_xdr(source_account) }, DEFAULT_XDR_RW_LIMITS).unwrap(); + + let go_storage = Rc::new(GoLedgerStorage::new(handle)); + let network_config = + NetworkConfig::load_from_snapshot(go_storage.as_ref(), c_ledger_info.bucket_list_size)?; + let ledger_info = fill_ledger_info(c_ledger_info, &network_config); + let auto_restore_snapshot = Rc::new(AutoRestoringSnapshotSource::new( + go_storage.clone(), + &ledger_info, + )?); + + let mut adjustment_config = SimulationAdjustmentConfig::default_adjustment(); + // It would be reasonable to extend `resource_config` to be compatible with `adjustment_config` + // in order to let the users customize the resource/fee adjustments in a more granular fashion. + + let instruction_leeway = u32::try_from(resource_config.instruction_leeway)?; + adjustment_config.instructions.additive_factor = adjustment_config + .instructions + .additive_factor + .max(instruction_leeway); + // Here we assume that no input auth means that the user requests the recording auth. + let auth_entries = if invoke_hf_op.auth.is_empty() { + None + } else { + Some(invoke_hf_op.auth.to_vec()) + }; + // Invoke the host function. The user errors should normally be captured in `invoke_hf_result.invoke_result` and + // this should return Err result for misconfigured ledger. + let invoke_hf_result = simulate_invoke_host_function_op( + auto_restore_snapshot.clone(), + &network_config, + &adjustment_config, + &ledger_info, + invoke_hf_op.host_function, + auth_entries, + &source_account, + rand::Rng::gen(&mut rand::thread_rng()), + enable_debug, + )?; + let maybe_restore_result = match &invoke_hf_result.invoke_result { + Ok(_) => auto_restore_snapshot.simulate_restore_keys_op( + &network_config, + &SimulationAdjustmentConfig::default_adjustment(), + &ledger_info, + ), + Err(e) => Err(e.clone().into()), + }; + let error_str = extract_error_string(&maybe_restore_result, go_storage.as_ref()); + Ok(new_cpreflight_result_from_invoke_host_function( + invoke_hf_result, + maybe_restore_result.unwrap_or(None), + error_str, + )) +} + +pub(crate) fn preflight_footprint_ttl_op_or_maybe_panic( + handle: libc::uintptr_t, + op_body: CXDR, + footprint: CXDR, + c_ledger_info: CLedgerInfo, +) -> Result { + let op_body = OperationBody::from_xdr(unsafe { from_c_xdr(op_body) }, DEFAULT_XDR_RW_LIMITS)?; + let footprint = + LedgerFootprint::from_xdr(unsafe { from_c_xdr(footprint) }, DEFAULT_XDR_RW_LIMITS)?; + let go_storage = Rc::new(GoLedgerStorage::new(handle)); + let network_config = + NetworkConfig::load_from_snapshot(go_storage.as_ref(), c_ledger_info.bucket_list_size)?; + let ledger_info = fill_ledger_info(c_ledger_info, &network_config); + // TODO: It would make for a better UX if the user passed only the necessary fields for every operation. + // That would remove a possibility of providing bad operation body, or a possibility of filling wrong footprint + // field. + match op_body { + OperationBody::ExtendFootprintTtl(extend_op) => { + preflight_extend_ttl_op(&extend_op, footprint.read_only.as_slice(), &go_storage, &network_config, &ledger_info) + } + OperationBody::RestoreFootprint(_) => { + Ok(preflight_restore_op(footprint.read_write.as_slice(), &go_storage, &network_config, &ledger_info)) + } + _ => Err(anyhow!("encountered unsupported operation type: '{:?}', instead of 'ExtendFootprintTtl' or 'RestoreFootprint' operations.", + op_body.discriminant())) + } +} + +fn preflight_extend_ttl_op( + extend_op: &ExtendFootprintTtlOp, + keys_to_extend: &[LedgerKey], + go_storage: &Rc, + network_config: &NetworkConfig, + ledger_info: &LedgerInfo, +) -> Result { + let auto_restore_snapshot = AutoRestoringSnapshotSource::new(go_storage.clone(), ledger_info)?; + let simulation_result = simulate_extend_ttl_op( + &auto_restore_snapshot, + network_config, + &SimulationAdjustmentConfig::default_adjustment(), + ledger_info, + keys_to_extend, + extend_op.extend_to, + ); + let (maybe_transaction_data, maybe_restore_result) = match simulation_result { + Ok(r) => ( + Some(r.transaction_data), + auto_restore_snapshot.simulate_restore_keys_op( + network_config, + &SimulationAdjustmentConfig::default_adjustment(), + ledger_info, + ), + ), + Err(e) => (None, Err(e)), + }; + + let error_str = extract_error_string(&maybe_restore_result, go_storage); + Ok(new_cpreflight_result_from_transaction_data( + maybe_transaction_data.as_ref(), + maybe_restore_result.ok().flatten().as_ref(), + error_str, + )) +} + +fn preflight_restore_op( + keys_to_restore: &[LedgerKey], + go_storage: &Rc, + network_config: &NetworkConfig, + ledger_info: &LedgerInfo, +) -> CPreflightResult { + let simulation_result = simulate_restore_op( + go_storage.as_ref(), + network_config, + &SimulationAdjustmentConfig::default_adjustment(), + ledger_info, + keys_to_restore, + ); + let error_str = extract_error_string(&simulation_result, go_storage.as_ref()); + new_cpreflight_result_from_transaction_data( + simulation_result.ok().map(|r| r.transaction_data).as_ref(), + None, + error_str, + ) +} + +// TODO: We could use something like https://github.com/sonos/ffi-convert-rs +// to replace all the free_* , *_to_c and from_c_* functions by implementations of CDrop, +// CReprOf and AsRust +fn xdr_to_c(v: &impl WriteXdr) -> CXDR { + let (xdr, len) = vec_to_c_array(v.to_xdr(DEFAULT_XDR_RW_LIMITS).unwrap()); + CXDR { xdr, len } +} + +fn option_xdr_to_c(v: Option<&impl WriteXdr>) -> CXDR { + v.map_or( + CXDR { + xdr: null_mut(), + len: 0, + }, + xdr_to_c, + ) +} + +fn ledger_entry_diff_to_c(v: &LedgerEntryDiff) -> CXDRDiff { + CXDRDiff { + before: option_xdr_to_c(v.state_before.as_ref()), + after: option_xdr_to_c(v.state_after.as_ref()), + } +} + +fn xdr_vec_to_c(v: &[impl WriteXdr]) -> CXDRVector { + let c_v = v.iter().map(xdr_to_c).collect(); + let (array, len) = vec_to_c_array(c_v); + CXDRVector { array, len } +} + +fn ledger_entry_diff_vec_to_c(modified_entries: &[LedgerEntryDiff]) -> CXDRDiffVector { + let c_diffs = modified_entries + .iter() + .map(ledger_entry_diff_to_c) + .collect(); + let (array, len) = vec_to_c_array(c_diffs); + CXDRDiffVector { array, len } +} + +// Gets a ledger entry by key, including the archived/removed entries. +// The failures of this function are not recoverable and should only happen when +// the underlying storage is somehow corrupted. +// +// This has to be a free function rather than a method on an impl because there +// are two copies of this file mounted in the module tree and we can't define a +// same-named method on a single Self-type twice. +fn get_fallible_from_go_ledger_storage( + storage: &GoLedgerStorage, + key: &LedgerKey, +) -> Result> { + let mut key_xdr = key.to_xdr(DEFAULT_XDR_RW_LIMITS)?; + let Some(xdr) = storage.get_xdr_internal(&mut key_xdr) else { + return Ok(None); + }; + + let live_until_ledger_seq = match key { + // TODO: it would probably be more efficient to do all of this in the Go side + // (e.g. it would allow us to query multiple entries at once) + LedgerKey::ContractData(_) | LedgerKey::ContractCode(_) => { + let key_hash: [u8; 32] = Sha256::digest(key_xdr).into(); + let ttl_key = LedgerKey::Ttl(LedgerKeyTtl { + key_hash: Hash(key_hash), + }); + let mut ttl_key_xdr = ttl_key.to_xdr(DEFAULT_XDR_RW_LIMITS)?; + let ttl_entry_xdr = storage.get_xdr_internal(&mut ttl_key_xdr).ok_or_else(|| { + anyhow!( + "TTL entry is missing for an entry that should have TTL with key: '{key:?}'" + ) + })?; + let ttl_entry = LedgerEntry::from_xdr(ttl_entry_xdr, DEFAULT_XDR_RW_LIMITS)?; + let LedgerEntryData::Ttl(TtlEntry { + live_until_ledger_seq, + .. + }) = ttl_entry.data + else { + bail!( + "unexpected non-TTL entry '{:?}' has been fetched for TTL key '{:?}'", + ttl_entry, + ttl_key + ); + }; + Some(live_until_ledger_seq) + } + _ => None, + }; + + let entry = LedgerEntry::from_xdr(xdr, DEFAULT_XDR_RW_LIMITS)?; + Ok(Some((Rc::new(entry), live_until_ledger_seq))) +} + +// We can do an impl here because the two `SnapshotSourceWithArchive` traits +// originate in _separate crates_ and so are considered distinct. So rustc sees +// `GoLedgerStorage` impl'ing two different traits that just happen to have the +// same name. +impl SnapshotSourceWithArchive for GoLedgerStorage { + fn get_including_archived( + &self, + key: &Rc, + ) -> std::result::Result, HostError> { + let res = get_fallible_from_go_ledger_storage(self, key.as_ref()); + match res { + Ok(res) => Ok(res), + Err(e) => { + // Store the internal error in the storage as the info won't be propagated from simulation. + if let Ok(mut err) = self.internal_error.try_borrow_mut() { + *err = Some(e); + } + // Errors that occur in storage are not recoverable, so we force host to halt by passing + // it an internal error. + Err((ScErrorType::Storage, ScErrorCode::InternalError).into()) + } + } + } +} diff --git a/cmd/soroban-rpc/lib/xdr2json/Cargo.toml b/cmd/soroban-rpc/lib/xdr2json/Cargo.toml index e0119838..8288d2fb 100644 --- a/cmd/soroban-rpc/lib/xdr2json/Cargo.toml +++ b/cmd/soroban-rpc/lib/xdr2json/Cargo.toml @@ -15,5 +15,4 @@ anyhow = { workspace = true } serde_json = { workspace = true } rand = { workspace = true } -stellar-xdr = { workspace = true } -soroban-env-host = { workspace = true, features = ["unstable-next-api"]} \ No newline at end of file +stellar-xdr = { workspace = true } \ No newline at end of file diff --git a/cmd/soroban-rpc/lib/xdr2json/src/lib.rs b/cmd/soroban-rpc/lib/xdr2json/src/lib.rs index e1f524be..c9c151e1 100644 --- a/cmd/soroban-rpc/lib/xdr2json/src/lib.rs +++ b/cmd/soroban-rpc/lib/xdr2json/src/lib.rs @@ -1,16 +1,30 @@ extern crate anyhow; extern crate ffi; -extern crate soroban_env_host; extern crate stellar_xdr; use std::{panic, str::FromStr}; +use stellar_xdr::curr as xdr; use anyhow::Result; // We really do need everything. #[allow(clippy::wildcard_imports)] use ffi::*; -use soroban_env_host::{xdr, DEFAULT_XDR_RW_LIMITS}; + +// This is the same limit as the soroban serialization limit +// but we redefine it here for two reasons: +// +// 1. To depend only on the XDR crate, not the soroban host. +// 2. To allow customizing it here, since this function may +// serialize many XDR types that are larger than the types +// soroban allows serializing (eg. transaction sets or ledger +// entries or whatever). Soroban is conservative and stops +// at 32MiB. + +const DEFAULT_XDR_RW_LIMITS: xdr::Limits = xdr::Limits { + depth: 500, + len: 32 * 1024 * 1024, +}; #[repr(C)] pub struct ConversionResult { diff --git a/go.mod b/go.mod index 17b93fe7..e2d415f0 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 - github.com/stellar/go v0.0.0-20240729151841-8b1dba46985c + github.com/stellar/go v0.0.0-20240924182550-69667b25baf4 github.com/stretchr/testify v1.9.0 ) diff --git a/go.sum b/go.sum index b36601aa..8bc56e76 100644 --- a/go.sum +++ b/go.sum @@ -342,8 +342,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= -github.com/stellar/go v0.0.0-20240729151841-8b1dba46985c h1:8+XiaqgCaFWUjMKpQ37OJzY8LZ6ie8BpLxq/wNQZ2eM= -github.com/stellar/go v0.0.0-20240729151841-8b1dba46985c/go.mod h1:rrFK7a8i2h9xad9HTfnSN/dTNEqXVHKAbkFeR7UxAgs= +github.com/stellar/go v0.0.0-20240924182550-69667b25baf4 h1:Kd4ivg3hCG8AfFQpxjUjhEXKc40Ux+piUWL03dBB/sw= +github.com/stellar/go v0.0.0-20240924182550-69667b25baf4/go.mod h1:rrFK7a8i2h9xad9HTfnSN/dTNEqXVHKAbkFeR7UxAgs= github.com/stellar/go-xdr v0.0.0-20231122183749-b53fb00bcac2 h1:OzCVd0SV5qE3ZcDeSFCmOWLZfEWZ3Oe8KtmSOYKEVWE= github.com/stellar/go-xdr v0.0.0-20231122183749-b53fb00bcac2/go.mod h1:yoxyU/M8nl9LKeWIoBrbDPQ7Cy+4jxRcWcOayZ4BMps= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/scripts/check-dependencies.bash b/scripts/check-dependencies.bash index 56f9510a..651e9278 100755 --- a/scripts/check-dependencies.bash +++ b/scripts/check-dependencies.bash @@ -9,20 +9,33 @@ fi CURL="curl -sL --fail-with-body" -if ! CARGO_OUTPUT=$(cargo tree -p soroban-env-host 2>&1); then - echo "The project depends on multiple versions of the soroban-env-host Rust library, please unify them." - echo "Make sure the soroban-sdk dependency indirectly points to the same soroban-env-host dependency imported explicitly." - echo - echo "This is soroban-env-host version imported by soroban-sdk:" - cargo tree --depth 1 -p soroban-sdk | grep env-host - echo - echo - echo - echo "Full error:" - echo $CARGO_OUTPUT +# PROTOS is in ascending order so the last iteration of the PROTOS-based loops +# will end up with the highest protocol value used for recording any state +# variables. +PROTOS=$($SED -n ':pkg; /"soroban-env-host"/ {n; /version/ { s/[^0-9]*\([0-9]\+\).*/\1/ p; b pkg;}}' Cargo.toml | sort -n | tr '\n' ' ') +if [ -z "$PROTOS" ]; then + echo "Cannot find soroban-env-host dependencies in Cargo.toml" exit 1 +else + echo "Found supported protocols: $PROTOS" fi +for PROTO in $PROTOS +do + if ! CARGO_OUTPUT=$(cargo tree -p soroban-env-host@$PROTO 2>&1); then + echo "The project depends on multiple versions of the soroban-env-host@$PROTO Rust library, please unify them." + echo "Make sure the soroban-sdk dependency indirectly points to the same soroban-env-host@$PROTO dependency imported explicitly." + echo + echo "This is soroban-env-host version imported by soroban-sdk:" + cargo tree --depth 1 -p soroban-sdk | grep env-host + echo + echo + echo + echo "Full error:" + echo $CARGO_OUTPUT + exit 1 + fi +done # revision of the https://github.com/stellar/rs-stellar-xdr library used by the Rust code RS_STELLAR_XDR_REVISION="" @@ -42,25 +55,28 @@ function stellar_xdr_version_from_rust_dep_tree { echo $LINE | $SED -n 's/.*stellar-xdr \(v\)\{0,1\}\([^ ]*\).*/\2/p' } -if CARGO_OUTPUT=$(cargo tree --depth 0 -p stellar-xdr 2>&1); then - RS_STELLAR_XDR_REVISION=$(echo "$CARGO_OUTPUT" | stellar_xdr_version_from_rust_dep_tree) - if [ ${#RS_STELLAR_XDR_REVISION} -eq 40 ]; then - # revision is a git hash - STELLAR_XDR_REVISION_FROM_RUST=$($CURL https://raw.githubusercontent.com/stellar/rs-stellar-xdr/${RS_STELLAR_XDR_REVISION}/xdr/curr-version) +for PROTO in $PROTOS +do + if CARGO_OUTPUT=$(cargo tree --depth 0 -p stellar-xdr@$PROTO 2>&1); then + RS_STELLAR_XDR_REVISION=$(echo "$CARGO_OUTPUT" | stellar_xdr_version_from_rust_dep_tree) + if [ ${#RS_STELLAR_XDR_REVISION} -eq 40 ]; then + # revision is a git hash + STELLAR_XDR_REVISION_FROM_RUST=$($CURL https://raw.githubusercontent.com/stellar/rs-stellar-xdr/${RS_STELLAR_XDR_REVISION}/xdr/curr-version) + else + # revision is a crate version + CARGO_SRC_BASE_DIR=$(realpath ${CARGO_HOME:-$HOME/.cargo}/registry/src/index*) + STELLAR_XDR_REVISION_FROM_RUST=$(cat "${CARGO_SRC_BASE_DIR}/stellar-xdr-${RS_STELLAR_XDR_REVISION}/xdr/curr-version") + fi else - # revision is a crate version - CARGO_SRC_BASE_DIR=$(realpath ${CARGO_HOME:-$HOME/.cargo}/registry/src/index*) - STELLAR_XDR_REVISION_FROM_RUST=$(cat "${CARGO_SRC_BASE_DIR}/stellar-xdr-${RS_STELLAR_XDR_REVISION}/xdr/curr-version") + echo "The project depends on multiple versions of the Rust rs-stellar-xdr@$PROTO library" + echo "Make sure a single version of stellar-xdr@$PROTO is used" + echo + echo + echo + echo "Full error:" + echo $CARGO_OUTPUT fi -else - echo "The project depends on multiple versions of the Rust rs-stellar-xdr library" - echo "Make sure a single version of stellar-xdr is used" - echo - echo - echo - echo "Full error:" - echo $CARGO_OUTPUT -fi +done # Now, lets compare the Rust and Go XDR revisions # TODO: The sed extraction below won't work for version tags @@ -98,7 +114,9 @@ for P in $PROTOCOL_VERSIONS; do # We obtain it from src/rust/src/host-dep-tree-curr.txt but Alternatively/in addition we could: # * Check the rs-stellar-xdr revision of host-dep-tree-prev.txt # * Check the stellar-xdr revision - CORE_HOST_DEP_TREE_CURR=$($CURL https://raw.githubusercontent.com/stellar/stellar-core/${CORE_CONTAINER_REVISION}/src/rust/src/host-dep-tree-curr.txt) + + # FIXME: we shouldn't hardcode the protocol number in the file being checked + CORE_HOST_DEP_TREE_CURR=$($CURL https://raw.githubusercontent.com/stellar/stellar-core/${CORE_CONTAINER_REVISION}/src/rust/src/dep-trees/p22-expect.txt) RS_STELLAR_XDR_REVISION_FROM_CORE=$(echo "$CORE_HOST_DEP_TREE_CURR" | stellar_xdr_version_from_rust_dep_tree) @@ -110,5 +128,3 @@ for P in $PROTOCOL_VERSIONS; do fi done - -