diff --git a/.gitignore b/.gitignore
index 4ceeefe9bc..11cc731ece 100644
--- a/.gitignore
+++ b/.gitignore
@@ -62,6 +62,7 @@ compile_commands.json
/autom4te.cache
/build
/src/stellar-core
+/src/fuzz_*
/buckets
/compile
/config.h
diff --git a/Builds/VisualStudio/stellar-core.vcxproj b/Builds/VisualStudio/stellar-core.vcxproj
index fd0f8b738e..9156417a08 100644
--- a/Builds/VisualStudio/stellar-core.vcxproj
+++ b/Builds/VisualStudio/stellar-core.vcxproj
@@ -670,7 +670,13 @@ exit /b 0
-
+
+
+
+
+
+
+
@@ -874,7 +880,6 @@ exit /b 0
-
@@ -1119,7 +1124,12 @@ exit /b 0
-
+
+
+
+
+
+
@@ -1236,7 +1246,6 @@ exit /b 0
-
diff --git a/Builds/VisualStudio/stellar-core.vcxproj.filters b/Builds/VisualStudio/stellar-core.vcxproj.filters
index b1a00f3ee0..83a73fd04a 100644
--- a/Builds/VisualStudio/stellar-core.vcxproj.filters
+++ b/Builds/VisualStudio/stellar-core.vcxproj.filters
@@ -94,6 +94,12 @@
{632e2a38-59c9-4c6f-8ae3-7e302688207e}
+
+ {a1b2c3d4-e5f6-4a5b-9c8d-7e6f5a4b3c2d}
+
+
+ {b2c3d4e5-f6a7-5b6c-0d9e-8f7a6b5c4d3e}
+
{eab77361-734d-4f9c-89bd-0a429d67a4d6}
@@ -462,8 +468,26 @@
work\tests
-
- test
+
+ test\fuzz
+
+
+ test\fuzz
+
+
+ test\fuzz
+
+
+ test\fuzz
+
+
+ test\fuzz\targets
+
+
+ test\fuzz\targets
+
+
+ test\fuzz\targets
test
@@ -486,9 +510,6 @@
util\tests
-
- test
-
util
@@ -1685,8 +1706,23 @@
main
-
- test
+
+ test\fuzz
+
+
+ test\fuzz
+
+
+ test\fuzz
+
+
+ test\fuzz\targets
+
+
+ test\fuzz\targets
+
+
+ test\fuzz\targets
test
@@ -1712,9 +1748,6 @@
util
-
- test
-
lib\util
diff --git a/Cargo.lock b/Cargo.lock
index 98d9b480ad..26371478ae 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -19,9 +19,9 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "ahash"
-version = "0.8.11"
+version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"once_cell",
@@ -31,9 +31,9 @@ dependencies = [
[[package]]
name = "aho-corasick"
-version = "1.0.2"
+version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
@@ -46,9 +46,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "arbitrary"
-version = "1.3.2"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
+checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
dependencies = [
"derive_arbitrary",
]
@@ -194,7 +194,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60"
dependencies = [
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -220,7 +220,7 @@ dependencies = [
"num-traits",
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -295,7 +295,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -326,9 +326,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "autocfg"
-version = "1.1.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "backtrace"
@@ -372,9 +372,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
-version = "1.8.0"
+version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
+checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]]
name = "batsat"
@@ -402,21 +402,25 @@ dependencies = [
[[package]]
name = "bumpalo"
-version = "3.19.0"
+version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
+checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
[[package]]
name = "cc"
-version = "1.0.98"
+version = "1.2.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
+checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29"
+dependencies = [
+ "find-msvc-tools",
+ "shlex",
+]
[[package]]
name = "cfg-if"
-version = "1.0.0"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_eval"
@@ -426,7 +430,7 @@ checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -437,18 +441,18 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "cpp_demangle"
-version = "0.5.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dcc405d55da54ad965aff198909afdcc8aeefc8ac6ba26d6abd19aa8aeacb2a"
+checksum = "0667304c32ea56cb4cd6d2d7c0cfe9a2f8041229db8c033af7f8d69492429def"
dependencies = [
"cfg-if",
]
[[package]]
name = "cpufeatures"
-version = "0.2.12"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
@@ -510,7 +514,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -539,14 +543,14 @@ checksum = "a26acccf6f445af85ea056362561a24ef56cdc15fcc685f03aec50b9c702cb6d"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
name = "data-encoding"
-version = "2.6.0"
+version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
+checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
[[package]]
name = "der"
@@ -571,13 +575,13 @@ dependencies = [
[[package]]
name = "derive_arbitrary"
-version = "1.3.2"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
+checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -645,14 +649,14 @@ dependencies = [
"enum-ordinalize",
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
name = "either"
-version = "1.8.1"
+version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "elliptic-curve"
@@ -689,14 +693,14 @@ checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
name = "equivalent"
-version = "1.0.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "escape-bytes"
@@ -706,9 +710,9 @@ checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2"
[[package]]
name = "ethnum"
-version = "1.5.0"
+version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c"
+checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b"
[[package]]
name = "ff"
@@ -726,12 +730,24 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+[[package]]
+name = "flagset"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe"
+
[[package]]
name = "fnv"
version = "1.0.7"
@@ -740,22 +756,24 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "generator"
-version = "0.8.3"
+version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbb949699c3e4df3a183b1d2142cb24277057055ed23c68ed58894f76c517223"
+checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9"
dependencies = [
+ "cc",
"cfg-if",
"libc",
"log",
"rustversion",
- "windows",
+ "windows-link",
+ "windows-result",
]
[[package]]
name = "generic-array"
-version = "0.14.7"
+version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
dependencies = [
"typenum",
"version_check",
@@ -764,9 +782,9 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.11"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"js-sys",
@@ -801,12 +819,6 @@ dependencies = [
"ahash",
]
-[[package]]
-name = "hashbrown"
-version = "0.14.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12"
-
[[package]]
name = "hashbrown"
version = "0.15.5"
@@ -816,6 +828,12 @@ dependencies = [
"allocator-api2",
]
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+
[[package]]
name = "hex"
version = "0.4.3"
@@ -839,12 +857,12 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.0.2"
+version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
+checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [
"equivalent",
- "hashbrown 0.14.1",
+ "hashbrown 0.16.1",
]
[[package]]
@@ -882,15 +900,15 @@ dependencies = [
[[package]]
name = "itoa"
-version = "1.0.6"
+version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
name = "js-sys"
-version = "0.3.77"
+version = "0.3.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
dependencies = [
"once_cell",
"wasm-bindgen",
@@ -919,9 +937,9 @@ dependencies = [
[[package]]
name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "leb128"
@@ -931,21 +949,21 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
-version = "0.2.179"
+version = "0.2.180"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f"
+checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
[[package]]
name = "libm"
-version = "0.2.15"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
+checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
[[package]]
name = "link-cplusplus"
-version = "1.0.9"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9"
+checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82"
dependencies = [
"cc",
]
@@ -971,18 +989,18 @@ dependencies = [
[[package]]
name = "matchers"
-version = "0.1.0"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
- "regex-automata 0.1.10",
+ "regex-automata",
]
[[package]]
name = "memchr"
-version = "2.5.0"
+version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "miniz_oxide"
@@ -995,34 +1013,32 @@ dependencies = [
[[package]]
name = "nu-ansi-term"
-version = "0.46.0"
+version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
- "overload",
- "winapi",
+ "windows-sys",
]
[[package]]
name = "num-bigint"
-version = "0.4.4"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
- "autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-derive"
-version = "0.4.1"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712"
+checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -1036,9 +1052,9 @@ dependencies = [
[[package]]
name = "num-traits"
-version = "0.2.17"
+version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
@@ -1054,15 +1070,9 @@ dependencies = [
[[package]]
name = "once_cell"
-version = "1.19.0"
+version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
-
-[[package]]
-name = "overload"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "p256"
@@ -1094,9 +1104,9 @@ dependencies = [
[[package]]
name = "pin-project-lite"
-version = "0.2.10"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pkcs8"
@@ -1110,9 +1120,12 @@ dependencies = [
[[package]]
name = "ppv-lite86"
-version = "0.2.17"
+version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
[[package]]
name = "primeorder"
@@ -1125,18 +1138,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.69"
+version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.33"
+version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
dependencies = [
"proc-macro2",
]
@@ -1171,49 +1184,22 @@ dependencies = [
"getrandom",
]
-[[package]]
-name = "regex"
-version = "1.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata 0.3.3",
- "regex-syntax 0.7.4",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
-dependencies = [
- "regex-syntax 0.6.29",
-]
-
[[package]]
name = "regex-automata"
-version = "0.3.3"
+version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310"
+checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
- "regex-syntax 0.7.4",
+ "regex-syntax",
]
[[package]]
name = "regex-syntax"
-version = "0.6.29"
+version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
-
-[[package]]
-name = "regex-syntax"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
+checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "rfc6979"
@@ -1227,9 +1213,9 @@ dependencies = [
[[package]]
name = "rustc-demangle"
-version = "0.1.26"
+version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
+checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
[[package]]
name = "rustc-simple-version"
@@ -1248,15 +1234,9 @@ dependencies = [
[[package]]
name = "rustversion"
-version = "1.0.14"
+version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
-
-[[package]]
-name = "ryu"
-version = "1.0.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "scoped-tls"
@@ -1279,39 +1259,51 @@ dependencies = [
[[package]]
name = "semver"
-version = "1.0.17"
+version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
+checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "serde"
-version = "1.0.192"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.192"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
name = "serde_json"
-version = "1.0.108"
+version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
- "ryu",
+ "memchr",
"serde",
+ "serde_core",
+ "zmij",
]
[[package]]
@@ -1337,13 +1329,19 @@ dependencies = [
[[package]]
name = "sharded-slab"
-version = "0.1.4"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
[[package]]
name = "signature"
version = "2.2.0"
@@ -1356,9 +1354,9 @@ dependencies = [
[[package]]
name = "smallvec"
-version = "1.13.2"
+version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "soroban-builtin-sdk-macros"
@@ -1368,7 +1366,7 @@ dependencies = [
"itertools 0.11.0",
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -1379,7 +1377,7 @@ dependencies = [
"itertools 0.10.5",
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -1390,7 +1388,7 @@ dependencies = [
"itertools 0.10.5",
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -1401,7 +1399,7 @@ dependencies = [
"itertools 0.10.5",
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -1412,18 +1410,18 @@ dependencies = [
"itertools 0.10.5",
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
name = "soroban-builtin-sdk-macros"
-version = "25.0.1"
-source = "git+https://github.com/stellar/rs-soroban-env?rev=e527f43cde68e0d17cd6d7d8e162d1944ba5a251#e527f43cde68e0d17cd6d7d8e162d1944ba5a251"
+version = "26.0.0-rc.1"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=81c917a39f4417a8e414761d21a5bd650417a997#81c917a39f4417a8e414761d21a5bd650417a997"
dependencies = [
"itertools 0.13.0",
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -1508,17 +1506,18 @@ dependencies = [
[[package]]
name = "soroban-env-common"
-version = "25.0.1"
-source = "git+https://github.com/stellar/rs-soroban-env?rev=e527f43cde68e0d17cd6d7d8e162d1944ba5a251#e527f43cde68e0d17cd6d7d8e162d1944ba5a251"
+version = "26.0.0-rc.1"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=81c917a39f4417a8e414761d21a5bd650417a997#81c917a39f4417a8e414761d21a5bd650417a997"
dependencies = [
+ "arbitrary",
"crate-git-revision",
"ethnum",
"num-derive",
"num-traits",
- "soroban-env-macros 25.0.1",
+ "soroban-env-macros 26.0.0-rc.1",
"soroban-wasmi",
"static_assertions",
- "stellar-xdr 25.0.0",
+ "stellar-xdr 25.0.0 (git+https://github.com/stellar/rs-stellar-xdr?rev=c458ac6bb943c271332cb8cf274b492433cb763f)",
"wasmparser",
]
@@ -1696,8 +1695,8 @@ dependencies = [
[[package]]
name = "soroban-env-host"
-version = "25.0.1"
-source = "git+https://github.com/stellar/rs-soroban-env?rev=e527f43cde68e0d17cd6d7d8e162d1944ba5a251#e527f43cde68e0d17cd6d7d8e162d1944ba5a251"
+version = "26.0.0-rc.1"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=81c917a39f4417a8e414761d21a5bd650417a997#81c917a39f4417a8e414761d21a5bd650417a997"
dependencies = [
"ark-bls12-381 0.5.0",
"ark-bn254 0.5.0",
@@ -1722,8 +1721,8 @@ dependencies = [
"sec1",
"sha2",
"sha3",
- "soroban-builtin-sdk-macros 25.0.1",
- "soroban-env-common 25.0.1",
+ "soroban-builtin-sdk-macros 26.0.0-rc.1",
+ "soroban-env-common 26.0.0-rc.1",
"soroban-wasmi",
"static_assertions",
"stellar-strkey 0.0.13",
@@ -1741,7 +1740,7 @@ dependencies = [
"serde",
"serde_json",
"stellar-xdr 21.2.0",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -1755,7 +1754,7 @@ dependencies = [
"serde",
"serde_json",
"stellar-xdr 22.0.0",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -1769,7 +1768,7 @@ dependencies = [
"serde",
"serde_json",
"stellar-xdr 23.0.0",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -1783,7 +1782,7 @@ dependencies = [
"serde",
"serde_json",
"stellar-xdr 24.0.0",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
@@ -1797,40 +1796,52 @@ dependencies = [
"serde",
"serde_json",
"stellar-xdr 24.0.1",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
name = "soroban-env-macros"
-version = "25.0.1"
-source = "git+https://github.com/stellar/rs-soroban-env?rev=e527f43cde68e0d17cd6d7d8e162d1944ba5a251#e527f43cde68e0d17cd6d7d8e162d1944ba5a251"
+version = "26.0.0-rc.1"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=81c917a39f4417a8e414761d21a5bd650417a997#81c917a39f4417a8e414761d21a5bd650417a997"
dependencies = [
"itertools 0.13.0",
"proc-macro2",
"quote",
"serde",
"serde_json",
- "stellar-xdr 25.0.0",
- "syn 2.0.39",
+ "stellar-xdr 25.0.0 (git+https://github.com/stellar/rs-stellar-xdr?rev=c458ac6bb943c271332cb8cf274b492433cb763f)",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "soroban-fuzz-targets"
+version = "26.0.0-rc.1"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=81c917a39f4417a8e414761d21a5bd650417a997#81c917a39f4417a8e414761d21a5bd650417a997"
+dependencies = [
+ "arbitrary",
+ "soroban-env-host 26.0.0-rc.1",
+ "soroban-synth-wasm",
+ "soroban-wasmi",
+ "wasm-smith",
]
[[package]]
name = "soroban-synth-wasm"
-version = "23.0.0"
-source = "git+https://github.com/stellar/rs-soroban-env?rev=688bc34e6cd15c71742139e625268c7f30f55a92#688bc34e6cd15c71742139e625268c7f30f55a92"
+version = "26.0.0-rc.1"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=81c917a39f4417a8e414761d21a5bd650417a997#81c917a39f4417a8e414761d21a5bd650417a997"
dependencies = [
"arbitrary",
- "soroban-env-common 23.0.0",
- "soroban-env-macros 23.0.0",
- "stellar-xdr 23.0.0",
+ "soroban-env-common 26.0.0-rc.1",
+ "soroban-env-macros 26.0.0-rc.1",
+ "stellar-xdr 25.0.0 (git+https://github.com/stellar/rs-stellar-xdr?rev=c458ac6bb943c271332cb8cf274b492433cb763f)",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "soroban-test-wasms"
-version = "23.0.2"
-source = "git+https://github.com/stellar/rs-soroban-env?rev=30249f0549141dbe7fdce84b6401a4235dbad64f#30249f0549141dbe7fdce84b6401a4235dbad64f"
+version = "26.0.0-rc.1"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=81c917a39f4417a8e414761d21a5bd650417a997#81c917a39f4417a8e414761d21a5bd650417a997"
[[package]]
name = "soroban-wasmi"
@@ -1883,7 +1894,8 @@ dependencies = [
"soroban-env-host 23.0.0",
"soroban-env-host 24.0.0",
"soroban-env-host 25.0.0",
- "soroban-env-host 25.0.1",
+ "soroban-env-host 26.0.0-rc.1",
+ "soroban-fuzz-targets",
"soroban-synth-wasm",
"soroban-test-wasms",
"stellar-quorum-analyzer",
@@ -1900,7 +1912,7 @@ dependencies = [
"log",
"petgraph",
"stellar-strkey 0.0.13",
- "stellar-xdr 25.0.0",
+ "stellar-xdr 25.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -2014,6 +2026,21 @@ version = "25.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10d20dafed80076b227d4b17c0c508a4bbc4d5e4c3d4c1de7cd42242df4b1eaf"
dependencies = [
+ "cfg_eval",
+ "crate-git-revision",
+ "escape-bytes",
+ "ethnum",
+ "hex",
+ "sha2",
+ "stellar-strkey 0.0.13",
+]
+
+[[package]]
+name = "stellar-xdr"
+version = "25.0.0"
+source = "git+https://github.com/stellar/rs-stellar-xdr?rev=c458ac6bb943c271332cb8cf274b492433cb763f#c458ac6bb943c271332cb8cf274b492433cb763f"
+dependencies = [
+ "arbitrary",
"base64 0.22.1",
"cfg_eval",
"crate-git-revision",
@@ -2043,9 +2070,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.39"
+version = "2.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
+checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
dependencies = [
"proc-macro2",
"quote",
@@ -2054,50 +2081,48 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "1.0.40"
+version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.40"
+version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
name = "thread_local"
-version = "1.1.7"
+version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
- "once_cell",
]
[[package]]
name = "tracing"
-version = "0.1.37"
+version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
- "cfg-if",
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
-version = "0.1.31"
+version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
"valuable",
@@ -2105,25 +2130,25 @@ dependencies = [
[[package]]
name = "tracing-log"
-version = "0.1.3"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
- "lazy_static",
"log",
+ "once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
-version = "0.3.17"
+version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
+checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
- "regex",
+ "regex-automata",
"sharded-slab",
"smallvec",
"thread_local",
@@ -2154,21 +2179,21 @@ dependencies = [
[[package]]
name = "typenum"
-version = "1.18.0"
+version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
+checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "unicode-ident"
-version = "1.0.9"
+version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "valuable"
-version = "0.1.0"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "version_check"
@@ -2178,40 +2203,28 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
+version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasm-bindgen"
-version = "0.2.100"
+version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
dependencies = [
"cfg-if",
"once_cell",
+ "rustversion",
"wasm-bindgen-macro",
-]
-
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
-dependencies = [
- "bumpalo",
- "log",
- "proc-macro2",
- "quote",
- "syn 2.0.39",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.100"
+version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -2219,33 +2232,46 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.100"
+version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
dependencies = [
+ "bumpalo",
"proc-macro2",
"quote",
- "syn 2.0.39",
- "wasm-bindgen-backend",
+ "syn 2.0.114",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.100"
+version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-encoder"
-version = "0.36.2"
+version = "0.38.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f"
+dependencies = [
+ "leb128",
+]
+
+[[package]]
+name = "wasm-smith"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421"
+checksum = "58273756970c82a1769b11e13a05bd21f95a767e4a2fab03afd0cff0085ae89d"
dependencies = [
+ "arbitrary",
+ "flagset",
+ "indexmap",
"leb128",
+ "wasm-encoder",
]
[[package]]
@@ -2283,73 +2309,6 @@ dependencies = [
"indexmap-nostd",
]
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
-name = "windows"
-version = "0.58.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
-dependencies = [
- "windows-core",
- "windows-targets",
-]
-
-[[package]]
-name = "windows-core"
-version = "0.58.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
-dependencies = [
- "windows-implement",
- "windows-interface",
- "windows-result",
- "windows-strings",
- "windows-targets",
-]
-
-[[package]]
-name = "windows-implement"
-version = "0.58.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.39",
-]
-
-[[package]]
-name = "windows-interface"
-version = "0.58.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.39",
-]
-
[[package]]
name = "windows-link"
version = "0.2.1"
@@ -2358,123 +2317,64 @@ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-result"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
-dependencies = [
- "windows-targets",
-]
-
-[[package]]
-name = "windows-strings"
-version = "0.1.0"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
dependencies = [
- "windows-result",
- "windows-targets",
+ "windows-link",
]
[[package]]
-name = "windows-targets"
-version = "0.52.6"
+name = "windows-sys"
+version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows-link",
]
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
-
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
-
[[package]]
name = "zerocopy"
-version = "0.7.35"
+version = "0.8.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+checksum = "7456cf00f0685ad319c5b1693f291a650eaf345e941d082fc4e03df8a03996ac"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
-version = "0.7.35"
+version = "0.8.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+checksum = "1328722bbf2115db7e19d69ebcc15e795719e2d66b60827c6a69a117365e37a0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
[[package]]
name = "zeroize"
-version = "1.8.1"
+version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
dependencies = [
"zeroize_derive",
]
[[package]]
name = "zeroize_derive"
-version = "1.4.2"
+version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
+checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.114",
]
+
+[[package]]
+name = "zmij"
+version = "1.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445"
diff --git a/Makefile.am b/Makefile.am
index 1b67b1c147..e9808eb90b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -21,10 +21,7 @@ format: always
cd src && $(MAKE) $(AM_MAKEFLAGS) $@
endif # USE_CLANG_FORMAT
-if USE_AFL_FUZZ
-fuzz-testcases fuzz fuzz-clean: all
- cd src && $(MAKE) $(AM_MAKEFLAGS) $@
-endif # USE_AFL_FUZZ
+
if USE_TRACY_GUI
TRACY_GUI_DIR=$(top_srcdir)/lib/tracy/profiler
diff --git a/build-fuzz.sh b/build-fuzz.sh
new file mode 100755
index 0000000000..bf7974bd99
--- /dev/null
+++ b/build-fuzz.sh
@@ -0,0 +1,92 @@
+#!/bin/bash
+# Copyright 2026 Stellar Development Foundation and contributors. Licensed
+# under the Apache License, Version 2.0. See the COPYING file at the root
+# of this distribution or at http://www.apache.org/licenses/LICENSE-2.0
+
+# Build script for oss-fuzz integration.
+#
+# This script builds fuzz targets for use with libfuzzer, honggfuzz, or AFL++.
+# It follows the oss-fuzz build conventions:
+# - Uses environment variables: $CC, $CXX, $CFLAGS, $CXXFLAGS, $LIB_FUZZING_ENGINE, $OUT
+# - Produces binaries in $OUT
+# - Creates seed corpus archives as fuzz__seed_corpus.zip
+#
+# For local testing with libfuzzer:
+# export CC=clang
+# export CXX=clang++
+# export CFLAGS="-g -fsanitize=fuzzer-no-link,address"
+# export CXXFLAGS="-g -fsanitize=fuzzer-no-link,address"
+# export LIB_FUZZING_ENGINE="-fsanitize=fuzzer"
+# export OUT=./fuzz-out
+# ./build-fuzz.sh
+#
+# For honggfuzz (local):
+# export CC=hfuzz-clang
+# export CXX=hfuzz-clang++
+# export LIB_FUZZING_ENGINE="" # honggfuzz provides its own
+# export OUT=./fuzz-out
+# ./build-fuzz.sh
+#
+# Outputs:
+# $OUT/fuzz_tx - Transaction application fuzzer
+# $OUT/fuzz_overlay - Overlay message fuzzer
+# $OUT/fuzz_tx_seed_corpus.zip - Seed corpus for tx fuzzer
+# $OUT/fuzz_overlay_seed_corpus.zip - Seed corpus for overlay fuzzer
+
+set -eux
+
+# Get source directory (where this script lives)
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+SRC="${SRC:-$SCRIPT_DIR}"
+
+# Create output directory
+: "${OUT:=./fuzz-out}"
+mkdir -p "$OUT"
+
+# Change to source directory
+cd "$SRC"
+
+# Run autogen if configure doesn't exist
+if [ ! -f configure ]; then
+ ./autogen.sh
+fi
+
+# Configure with fuzzing support
+# The --enable-fuzz flag:
+# - Adds FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION define
+# - Sets FUZZING_LIBS from LIB_FUZZING_ENGINE
+# - Enables building of fuzz_* targets
+./configure \
+ --enable-fuzz \
+ --disable-tests \
+ --without-postgres
+
+# Build fuzz targets using automake rules
+# This builds fuzz_tx, fuzz_overlay, and Soroban targets with proper FUZZ_TARGET_NAME defines
+make -j"$(nproc)" fuzz-targets
+
+# Install fuzz targets to $OUT
+# Install all fuzz targets (C++ and Soroban)
+for target in tx overlay soroban_expr soroban_wasmi; do
+ if [ -f "src/fuzz_$target" ]; then
+ cp "src/fuzz_$target" "$OUT/"
+ fi
+done
+
+# Generate and package seed corpus (if stellar-core supports it)
+# For now, create empty corpus directories
+mkdir -p corpus/tx corpus/overlay corpus/soroban_expr corpus/soroban_wasmi
+
+# Create corpus archives (even if empty, oss-fuzz expects them)
+# Create corpus archives for all targets (even if empty, oss-fuzz expects them)
+for target in tx overlay soroban_expr soroban_wasmi; do
+ if [ -d "corpus/$target" ] && [ "$(ls -A "corpus/$target" 2>/dev/null)" ]; then
+ (cd "corpus/$target" && zip -q "$OUT/fuzz_${target}_seed_corpus.zip" *)
+ else
+ # Create empty zip if no corpus
+ touch empty && zip -q "$OUT/fuzz_${target}_seed_corpus.zip" empty && rm empty
+ fi
+done
+
+echo "Build complete. Fuzz targets in $OUT:"
+ls -la "$OUT"
diff --git a/configure.ac b/configure.ac
index 98a74ec27b..6e518ad99d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -233,36 +233,51 @@ AS_IF([test "x$enable_ccache" = "xyes"], [
esac
])
-# Permit user to enable AFL instrumentation
-AC_ARG_ENABLE([afl],
- AS_HELP_STRING([--enable-afl],
- [build with AFL (fuzzer) instrumentation]))
-AS_IF([test "x$enable_afl" = "xyes"], [
- AS_IF([test "x$sanitizeopts" != "x"], [
- AC_MSG_ERROR([AFL is presently incompatible with sanitizers])
+# Permit user to enable libfuzzer-compatible fuzzing (works with libfuzzer,
+# honggfuzz, AFL++, etc.)
+AC_ARG_ENABLE([fuzz],
+ AS_HELP_STRING([--enable-fuzz],
+ [build with libfuzzer-compatible fuzzing support]))
+AS_IF([test "x$enable_fuzz" = "xyes"], [
+ # Add fuzzing-specific compile-time define. This is the standard define used
+ # by oss-fuzz and enables fuzzing-specific code paths.
+ CXXFLAGS="$CXXFLAGS -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION"
+ CFLAGS="$CFLAGS -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION"
+
+ # LIB_FUZZING_ENGINE must be set to link against a fuzzing engine runtime.
+ # OSS-Fuzz always sets this. For local builds with libfuzzer, you also need
+ # to add instrumentation flags to CXXFLAGS:
+ #
+ # libfuzzer:
+ # CXXFLAGS="-O2 -g -fsanitize=fuzzer-no-link" LIB_FUZZING_ENGINE="-fsanitize=fuzzer" \
+ # ./configure --enable-fuzz
+ #
+ # honggfuzz:
+ # CC=hfuzz-clang CXX=hfuzz-clang++ \
+ # LIB_FUZZING_ENGINE="/path/to/honggfuzz/libhfuzz/libhfuzz.a" \
+ # ./configure --enable-fuzz
+ #
+ # AFL++:
+ # CC=afl-clang-fast CXX=afl-clang-fast++ \
+ # LIB_FUZZING_ENGINE="/path/to/AFLplusplus/libAFLDriver.a" \
+ # ./configure --enable-fuzz
+ #
+ # OSS-Fuzz sets CXXFLAGS and LIB_FUZZING_ENGINE automatically.
+ AS_IF([test -z "$LIB_FUZZING_ENGINE"], [
+ AC_MSG_NOTICE([LIB_FUZZING_ENGINE must be set to link against a fuzzing engine runtime.])
+ AC_MSG_NOTICE([For builds with libfuzzer:])
+ AC_MSG_NOTICE([ CXXFLAGS="-O2 -g -fsanitize=fuzzer-no-link" LIB_FUZZING_ENGINE="-fsanitize=fuzzer" ./configure --enable-fuzz])
+ AC_MSG_NOTICE([For honggfuzz:])
+ AC_MSG_NOTICE([ CC=hfuzz-clang CXX=hfuzz-clang++ LIB_FUZZING_ENGINE="/path/to/honggfuzz/libhfuzz/libhfuzz.a" ./configure --enable-fuzz])
+ AC_MSG_NOTICE([For AFL++:])
+ AC_MSG_NOTICE([ CC=afl-clang-fast CXX=afl-clang-fast++ LIB_FUZZING_ENGINE="/path/to/AFLplusplus/libAFLDriver.a" ./configure --enable-fuzz])
+ AC_MSG_NOTICE([OSS-Fuzz sets CXXFLAGS and LIB_FUZZING_ENGINE automatically.])
+ AC_MSG_ERROR([--enable-fuzz requires LIB_FUZZING_ENGINE to be set])
])
- AS_IF([test "x$enable_ccache" = "xyes"], [
- AC_MSG_ERROR([AFL is presently incompatible with ccache])
- ])
- AC_CHECK_PROGS([AFL_FUZZ], [afl-fuzz])
- AS_CASE(["$CC"],
- [clang*], [AC_CHECK_PROGS([AFL_CLANG], [afl-clang-fast])
- AC_CHECK_PROGS([AFL_CLANGPP], [afl-clang-fast++])
- CC="afl-clang-fast"
- # below we hard code -std=c++20 since updates to AX_CXX_COMPILE_STDCXX append it to
- # CXX, not to CXXFLAGS and thus when setting CXX we override this. For a more detailed explanation
- # see: https://github.com/stellar/docker-stellar-core/pull/66#issuecomment-521886881
- CXX="afl-clang-fast++ -std=c++20 -DAFL_LLVM_MODE=1"],
- [gcc*], [AC_CHECK_PROGS([AFL_GCC], [afl-gcc])
- AC_CHECK_PROGS([AFL_GPP], [afl-g++])
- CC="afl-gcc"
- # below we hard code -std=c++20 since updates to AX_CXX_COMPILE_STDCXX append it to
- # CXX, not to CXXFLAGS and thus when setting CXX we override this. For a more detailed explanation
- # see: https://github.com/stellar/docker-stellar-core/pull/66#issuecomment-521886881
- CXX="afl-g++ -std=c++20"],
- [AC_MSG_ERROR([Don't know how to instrument CC=$CC with AFL])])
+ AC_MSG_NOTICE([Using LIB_FUZZING_ENGINE=$LIB_FUZZING_ENGINE])
+ AC_SUBST([LIB_FUZZING_ENGINE])
])
-AM_CONDITIONAL([USE_AFL_FUZZ], [test "x$enable_afl" == "xyes"])
+AM_CONDITIONAL([ENABLE_FUZZ], [test "x$enable_fuzz" = "xyes"])
# prefer 20 as it's the one we use
AC_CHECK_PROGS(CLANG_FORMAT, [clang-format-20 clang-format])
@@ -480,8 +495,8 @@ esc() {
echo $out
}
-# explicitly propagate CFLAGS, CXXFLAGS and LDFLAGS in case they got modified by global options
-ac_configure_args="$ac_configure_args $(esc "CC=$CC" "CXX=$CXX" "CFLAGS=$CFLAGS" "CXXFLAGS=$CXXFLAGS" "LDFLAGS=$LDFLAGS")"
+# explicitly propagate CFLAGS, CXXFLAGS, LDFLAGS and LIB_FUZZING_ENGINE in case they got modified by global options
+ac_configure_args="$ac_configure_args $(esc "CC=$CC" "CXX=$CXX" "CFLAGS=$CFLAGS" "CXXFLAGS=$CXXFLAGS" "LDFLAGS=$LDFLAGS" "LIB_FUZZING_ENGINE=$LIB_FUZZING_ENGINE")"
AC_CONFIG_HEADERS(config.h)
AC_CONFIG_FILES(lib/Makefile src/Makefile Makefile)
diff --git a/deny.toml b/deny.toml
index 24276c0a22..6341f4eb28 100644
--- a/deny.toml
+++ b/deny.toml
@@ -121,6 +121,7 @@ allow = [
"BSD-3-Clause",
"Apache-2.0 WITH LLVM-exception",
"Unicode-DFS-2016",
+ "Unicode-3.0",
# "MPL-2.0"
]
# The confidence threshold for detecting a license from license text.
diff --git a/docs/fuzzing.md b/docs/fuzzing.md
index cdd9f8eda8..b3d4a19c1f 100644
--- a/docs/fuzzing.md
+++ b/docs/fuzzing.md
@@ -2,217 +2,120 @@
title: Fuzzing
---
-This is a little howto on using the [AFL][0] (["American Fuzzy Lop"][1])
-fuzzer with stellar-core. Support for this is still preliminary but it has
-already shaken a couple bugs out and will no doubt find more the further down
-this road we go, so you're encouraged to give this a try.
-
-## Theory of operation
-
-AFL is a black-box fuzzer -- it knows nothing much about the thing it's fuzzing
--- that uses an evolutionary algorithm to breed malicious input under a bunch of
-heuristic mutations (bit flips and such). It thus takes an initial interesting
-corpus of inputs to the victim program, which it expands as it finds inputs that
-take the program into new portions of its control-flow space and/or trigger crashes.
-It writes elements of its corpus to disk in an output directory any time it finds
-one that crashes the program or causes the program to hang in a new control-flow path.
-
-It identifies control-flow paths by instrumenting basic blocks and storing an
-in-memory, very dense set of counters associated with control-flow tuples. When
-the victim program takes a branch, it's instrumented to bump a counter based on
-the (src, dst) pair of PC addresses making up a branch-edge, and the set of
-branch-tuple counts produces a control-flow "signature" for a run, which is used
-to differentiate runs / explore the control-flow space.
-
-Every run of the program starts anew and is fed a small binary input from the
-corpus. This initial corpus is the result of minimizing (using [afl-cmin][3])
-a much larger, randomly generated series of inputs, taking the smallest subset
-that results in unique control-flow tuples.
-
-To make this all work the program has to (a) run quickly, (b) take small inputs
-in binary form and (c) be guided toward interesting edge cases just enough. We
-currently have two fuzz modes, `tx` and `overlay`, and they **do not** perform the
-above equally well. For starters, we have enabled [llvm_mode][9]'s persistent mode
-for a modest 10x-100x improvement in execution/sec for *both* fuzz modes. Thus both
-perform well in area (a) and with simple modifications we made to stellar-core, (b)
-too. However, we've spent more energy with (c) for the `tx` fuzz mode, modifying
-stellar-core such that it is more deterministic -- caches are cleared and randomness
-seeded -- and it skips wasteful and inconsequential processes -- anything related to
-signatures. For both modes, there is still much room for improvement in regards to
-(a) and (c), one example being an isolation of the subsystem.
-
-
-## Installing AFL
-
-### Packages
-
-Pre-packaged versions of AFL may be available.
-
-For example, Ubuntu provides `AFL++` that can be installed with
-```
- apt-get install afl++
+This document describes the fuzzing infrastructure in stellar-core.
+
+## Concepts
+
+### Fuzz Drivers and Fuzz Targets
+
+Fuzzing involves two key components:
+
+- **Fuzz Driver**: A fuzzing engine that generates and mutates test inputs, tracks code coverage, and orchestrates the fuzzing process. Examples include libFuzzer, AFL++, and honggfuzz.
+
+- **Fuzz Target**: A function that takes arbitrary input data and exercises some code path in the software being tested. The fuzz driver repeatedly calls the fuzz target with different inputs, looking for crashes, hangs, or other anomalous behavior.
+
+stellar-core has multiple fuzz targets, each of which implements `stellar::FuzzTarget`. Some targets exercise code directly in stellar-core, while others pass through to fuzz targets defined in Soroban.
+
+### Instrumentation
+
+All the fuzzers we work with are so-called **greybox** fuzzers. This means the fuzzer has _some_ insight into the behaviour of the program. It is not just looking for crashes; it's also watching to see which inputs _exercise new paths_ in the control flow.
+
+The fuzzer does this by using instrumentation that is injected into the binary at compile time. Specifically: it instruments every branch instruction in the program to increment a counter, and all the counters are hashed together during the run to tell the fuzzer if it hit a new control flow path.
+
+This means that you have to compile core -- all of it, and soroban too -- with **instrumentation turned on** if you want the fuzzer to see anything. If instrumentation is turned off the fuzzer will be flying blind and will not
+be able to work effectively.
+
+## Architecture
+
+### LibFuzzer-based Persistent Fuzzing API
+
+Our fuzzing setup is based on the libFuzzer standard persistent entrypoint: `LLVMFuzzerTestOneInput`:
+
+```c
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
```
-### From source
+We provide an implementation of this (in `FuzzMain.cpp`) that is multiply compiled into separate binaries, each hard-wired to call a specific fuzz target, for each of our fuzz targets.
+
+This API allows us to work with multiple fuzz drivers (libFuzzer, AFL++, honggfuzz, etc.) that all support this interface.
+
+### Selection of fuzz drivers
+
+We support 3 fuzz drivers:
-Go to the [AFL repo][0], download the [tarball][2], unpack and run `make`, `make -C llvm_mode`
-then `sudo make install`. This will install both `afl-fuzz`, the fuzzer itself, and `afl-gcc`,
-`afl-clang`, and `afl-clang-fast`, which are compiler-wrappers that instrument binaries they
-compile with the fuzzer's branch-tracking machinery (the later being necessary for `llvm_mode`
-improvements described above).
+1. **libFuzzer** (Clang's built-in fuzzer):
+ ```bash
+ CXXFLAGS="-O2 -g -fsanitize=fuzzer-no-link" \
+ LIB_FUZZING_ENGINE="-fsanitize=fuzzer" \
+ ./configure --enable-fuzz
+ ```
-note: you may have to provide a default clang/clang++, this can be done in many ways.
+2. **AFL++** (American Fuzzy Lop):
+ ```bash
+ CC=afl-clang-fast \
+ CXX=afl-clang-fast++ \
+ LIB_FUZZING_ENGINE="/path/to/AFLplusplus/libAFLDriver.a" \
+ ./configure --enable-fuzz
+ ```
-For example, this makes clang-12 the default:
+3. **honggfuzz**:
+ ```bash
+ CC=hfuzz-clang \
+ CXX=hfuzz-clang++ \
+ LIB_FUZZING_ENGINE="/path/to/honggfuzz/libhfuzz/libhfuzz.a" \
+ ./configure --enable-fuzz
+ ```
+
+Note that 3 separate things are happening here in configure:
+
+ - The `--enable-fuzz` configure option enables building the fuzz targets.
+ - The `LIB_FUZZING_ENGINE` variable specifies the library that provides a `main()` containing the fuzz driver that will call the fuzz target harness back.
+ - The `CC`, `CXX`, and/or `CXXFLAGS` alter compilation to inject the fuzzer's greybox / coverage instrumentation into the compiled binary.
+
+You need to do **all 3 of these** to get fuzzing to work.
+
+### Binary Organization
+
+Our fuzz targets are compiled into separate binaries, each with the fuzz driver supplying `main()`, us supplying `LLVMFuzzerTestOneInput`, and then everything else identical from one binary to the other. For example the `tx` fuzz target is compiled into a binary called `fuzz_tx`. To fuzz, you just run it; it has the fuzz driver linked into it.
+
+This organization is dictated by [oss-fuzz](https://github.com/google/oss-fuzz), Google's continuous fuzzing service, which expects this structure for integration.
+
+Each fuzz target binary can be run standalone with any compatible fuzz driver, or used directly for reproducing and debugging crashes.
+
+## Testing Integration
+
+### Corpus-based Unit Tests
+
+Fuzz targets are reused as unit tests. The unit test suite runs each fuzz target against a retained corpus of interesting test cases. This ensures that:
+
+1. Previously discovered edge cases continue to be tested
+2. Regressions in fuzz target behavior are caught early
+3. The corpus grows over time as new interesting inputs are found
+
+## Command-line Tools
+
+### Checking a Testcase: `fuzz-one`
+
+stellar-core provides a one-shot `fuzz-one` command to check a given fuzz testcase:
```
-sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-12 81 --slave /usr/bin/clang++ clang++ /usr/bin/clang++-12 --slave /usr/share/man/man1/clang.1.gz clang.1.gz /usr/share/man/man1/clang-12.1.gz --slave /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-12 --slave /usr/bin/clang-format clang-format /usr/bin/clang-format-12 --slave /usr/bin/llvm-config llvm-config /usr/bin/llvm-config-12
+stellar-core fuzz-one --target
```
-## Building an instrumented stellar-core
+This is useful for:
+- Reproducing crashes found by fuzzers
+- Debugging specific inputs
+- Validating that a fix addresses a particular crash
-Start with a clean workspace, `make clean` or cleaner; then just do
+It is _not_ useful for exploring the state-space of the fuzz target -- i.e. for "real fuzzing" under greybox supervision. Use the separate per-target binaries linked to fuzz drivers for real fuzzing (`fuzz_tx`, `fuzz_overlay`, etc.)
-```
-# or any compile flags that you like
-export CFLAGS='-O3 -g1' ; export CXXFLAGS="$CFLAGS"
+### Generating Seed Corpus: `gen-fuzz`
-# or any compiler that you want
-export CC='clang' ; export CXX='clang++'
-./autogen.sh && ./configure --enable-extrachecks --disable-postgres --enable-afl && make
+The `gen-fuzz` command generates a seed corpus for the fuzzers:
+
+```
+stellar-core gen-fuzz --target --output-dir [--count ]
```
-make sure you have not enabled `asan` and `ccache`;
-the former is incompatible, and the latter doesn't interoperate with the
-compiler wrappers.
-
-
-## Running the fuzzer
-
-The simplest way is to set the environment variable `FUZZER_MODE` to `tx` or
-to `overlay` and then `make fuzz`; this will do the following:
-
- - Create a directory `fuzz-testcases` for storing the initial corpus input
- - Run `stellar-core gen-fuzz fuzz-testcases/fuzz$i.xdr` ten thousand times
- to produce some basic seed input for the corpus
- - Run `afl-cmin` on the generated `fuzz-testcases`, minimizing the corpus
- to a unique, interesting subset, which will be put into a directory named
- `min-testcases`
- - Create a directory `fuzz-findings` for storing crash-producing inputs
- - Run `afl-fuzz` on `stellar-core fuzz`, using those corpus directories
-
-If `stellar-core fuzz` (or `afl-fuzz`) produces output such as 'Warning: AFL++
-tools will need to set AFL_MAP_SIZE to 757616 to be able to run this
-instrumented program!', then you can set this environment variable to something
-sufficient, such as `export AFL_MAP_SIZE=786432`.
-
-You should get a nice old-school textmode TUI to monitor the fuzzer's progress;
-it might be partly hidden depending on the color scheme of your terminal, as it
-makes use of bold color highlighting. There are a lot of [interesting statistics][12]
-displayed here.
-
-When evaluating changes or posting a PR, include screenshots of the TUI
-after similar runtimes from before and after the changes. We've seen
-~15% variability in metrics such as "total paths" after 10 minutes, so
-you should choose a time significantly longer than that. The [user guide][13]
-documents interpretation of the many metrics. At a minimum, we're interested
-in:
-
-- Exec speed
-- Total paths
-- Stability
-- Whether the TUI displays any metrics in red (indicating values it considers
-undesirable)
-
-While it runs, it will write new crashes to files in `fuzz-findings`; before
-pouncing on these as definite evidence of a flaw, you should confirm the crash
-by feeding it to an instance of `stellar-core fuzz` run by hand, elsewhere (in
-particular, not fighting for control over tmpdirs with the fuzzer's
-`stellar-core` instances). Often a fuzzer "crash" is just the subprocess hitting
-a ulimit; by default we use an 250mb virtual-address ulimit, thus it is
-possible to exceed this. It is also useful to keep a separate build of
-`stellar-core` in a different directory with `--enable-asan`, or valgrind, in
-order to diagnose crashes. For more information on the [output][4] or [triaging
-crashes][5] click the hyperlinks.
-
-It is also possible to run fuzzers in parallel across a [local machine][6] or
-even across [separate machines][7] using ssh. This is a good way to significantly
-improve the executions/sec, as the master-slave setup here utilizes, by default,
-*dirty and quick mode*, a mode more *"akin to zzuf"* which skips deterministic
-mutations for all the slaves. Scripts for bootstrapping are generally easy to write.
-For a good place to start, check out some of the existing [AFL scripts and libraries][8]
-on Github.
-
-## Comparing changes against master
-
-Any changes to the fuzzer should be compared to master to make sure we aren't introducing
-undesirable behavior. A screenshot of the TUI mentioned above after running the fuzzer for
-similar times should be posted to the PR, but there is an important point to be aware
-of - you need to make sure to run the same testcases against master and the PR. Running
-`make fuzz` twice will use different runs of `gen-fuzz`, so comparisons will not be meaningful.
-
-What should be done instead is first create the `min-testcases` directory using `make fuzz` or
-`make fuzz-testcases` (the former will create the tests and run afl, while the latter will just create
-the tests). Then you can run `afl-fuzz -m 500 -M main -t 250 -i min-testcases -o fuzz-findings ./stellar-core fuzz --ll ERROR --process-id 0 --mode=${FUZZER_MODE} @@`
-with multiple versions of core, which will use the `min-testcases` directory previously created. If
-you are using two directories to test master vs a PR, then you'll need to copy `min-testcases` to the other directory.
-
-## Future directions
-
-Aside from "continuous fuzzing" and "fuzzing for a certain amount of time as
-part of staging-tests", here are some directions I think we should take fuzzing:
-
- - Enable AFL_HARDEN, adds some runtime memory checks that trap more errors.
-
- - Try AFL_NO_VAR_CHECK and see if it speeds things up.
-
- - Try limiting the instrumentation to `stellar-core` itself, not libsodium,
- soci, sqlite, medida, and so forth.
-
- - Measure and shave-down the startup path for fuzzing so it's as fast as
- possible. The fewer instructions there are from `main()` to "doing something
- with input", the better.
-
- - Try to use [LibFuzzer][11] or manual fork-mode to fork from an initialized state
- that is further along in memory; the difficult part is that `VirtualClock`
- and the associated IO loop is stateful and not friendly to forking, so
- we would need to tease apart portions of the program that can get their
- clock/IO service supplied late.
-
- - Consider using [DeepState][10], *"a framework that provides C and C++
- developers with a common interface to various symbolic execution and
- fuzzing engines*" for exposure to multiple fuzzing backends (with a single
- fuzz harness!).
-
- - Make startup-modes at different points in the process: instantiate an
- application and feed it transactions to run directly, not full
- `StellarMessage`s. Let the fuzzer generte bucket ledger entries, and try to
- apply them to the database as one would during catchup. This sort of thing.
-
- - Make a "postprocessor" using the AFL_POST_LIBRARY facility. This permits
- cleaning up or otherwise transforming fuzzer-generated data into data for
- the program, but it needs to be extremely robust.
-
- - Alternatively, make a "fuzzer-friendly" XDR message type that describes,
- using simple integer codes that the fuzzer will find it easy to perturb, a
- complete _test scenario_ or set of actions to apply to the program. In terms
- of setting up a network of nodes, a topology among them, a number of
- accounts and amounts, and a set of transactions to apply. Drive _that_ by
- the fuzzer, rather than single-action messages.
-
-
-[0]: https://github.com/google/afl
-[1]: http://rabbitbreeders.us/american-fuzzy-lop-rabbits
-[2]: https://github.com/google/AFL/releases
-[3]: https://github.com/google/AFL/blob/fb1f87177b78bc166dbbc6ffb8f3f8eb276c36cc/README.md#5-choosing-initial-test-cases
-[4]: https://github.com/google/afl#7-interpreting-output
-[5]: https://github.com/google/afl#10-crash-triage
-[6]: https://github.com/google/AFL/blob/master/docs/parallel_fuzzing.txt#L29
-[7]: https://github.com/google/AFL/blob/master/docs/parallel_fuzzing.txt#L89
-[8]: https://github.com/google/AFL/blob/master/docs/sister_projects.txt#L106
-[9]: https://github.com/google/AFL/tree/fb1f87177b78bc166dbbc6ffb8f3f8eb276c36cc/llvm_mode
-[10]: https://github.com/trailofbits/deepstate
-[11]: https://llvm.org/docs/LibFuzzer.html
-[12]: https://github.com/google/AFL/blob/master/docs/status_screen.txt
-[13]: https://afl-1.readthedocs.io/en/latest/user_guide.html
+A good seed corpus helps fuzzers explore interesting code paths more quickly by providing structurally valid inputs as starting points for mutation.
diff --git a/docs/software/commands.md b/docs/software/commands.md
index e380a31908..7ce043821a 100644
--- a/docs/software/commands.md
+++ b/docs/software/commands.md
@@ -110,8 +110,8 @@ Command options can only by placed after command.
* **dump-xdr **: Dumps the given XDR file and then exits.
* **dump-archival-stats**: Logs state archival statistics about the BucketList.
* **encode-asset**: Prints a base-64 encoded asset built from `--code ` and `--issuer `. Prints the native asset if neither `--code` nor `--issuer` is given.
-* **fuzz **: Run a single fuzz input and exit.
-* **gen-fuzz **: Generate a random fuzzer input file.
+* **fuzz-one **: Run a single fuzz input and exit. Requires `--target `.
+* **gen-fuzz**: Generate fuzz input files for a seed corpus. Requires `--target `, `--output-dir `, and optionally `--count ` (default: 100).
* **gen-seed**: Generate and print a random public/private key and then exit.
* **get-settings-upgrade-txs **: Generates the three transactions needed to propose
a Soroban Settings upgrade from scratch, as will as the XDR `ConfigUpgradeSetKey` to submit to the `upgrades` endpoint. The results will be dumped to standard output. is the key that will be used as the source account on the transactions.
diff --git a/make-mks b/make-mks
index b5ea6a40ff..3f89716286 100755
--- a/make-mks
+++ b/make-mks
@@ -31,10 +31,12 @@ message="# This file was generated by make-mks; don't edit it by hand."
(cd src
echo "$message"
echo "SRC_H_FILES" = $(getFiles | egrep -e '\.h$' -e '\.[ih]pp$' | egrep -v '(test|simulation)/' | tr "\n" " ")
- echo "SRC_CXX_FILES" = $(getFiles | egrep '\.cpp$' | egrep -v '(test|simulation)/' | tr "\n" " ")
+ # SRC_CXX_FILES excludes main/main.cpp - it's added explicitly to stellar_core_SOURCES
+ echo "SRC_CXX_FILES" = $(getFiles | egrep '\.cpp$' | egrep -v '(test|simulation)/' | egrep -v '^main/main\.cpp$' | tr "\n" " ")
echo "SRC_X_FILES" = $(getFiles | egrep '\.x$' | tr "\n" " ")
echo "SRC_TEST_H_FILES" = $(getFiles | egrep -e '\.h$' -e '\.[ih]pp$' | egrep '(test|simulation)/' | tr "\n" " ")
- echo "SRC_TEST_CXX_FILES" = $(getFiles | egrep '\.cpp$' | egrep '(test|simulation)/' | tr "\n" " ")
+ # SRC_TEST_CXX_FILES excludes FuzzMain.cpp - it's fuzz-target-specific, not part of unit tests
+ echo "SRC_TEST_CXX_FILES" = $(getFiles | egrep '\.cpp$' | egrep '(test|simulation)/' | egrep -v '^test/fuzz/FuzzMain\.cpp$' | tr "\n" " ")
echo "SRC_RUST_FILES" = $(getFiles | egrep '\.rs$' | tr "\n" " ")
) > src/src.mk
diff --git a/src/Makefile.am b/src/Makefile.am
index eaa9684a05..5ec662824e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -53,14 +53,42 @@ else
CARGO_FEATURE_TRACY =
endif
+# Generated sources that are built separately (not in SRC_CXX_FILES)
+# These are reused by both stellar-core and fuzz targets.
+GENERATED_VERSION_SOURCES = main/StellarCoreVersion.cpp main/XDRFilesSha256.cpp
+GENERATED_XDRQUERY_SOURCES = util/xdrquery/XDRQueryScanner.cpp util/xdrquery/XDRQueryParser.cpp
+GENERATED_RUST_SOURCES = rust/RustBridge.cpp
+
+# main/main.cpp is excluded from SRC_CXX_FILES (via make-mks) so fuzz targets
+# don't get the stellar-core main(). We add it back here for stellar-core only.
+MAIN_FILES = $(GENERATED_VERSION_SOURCES) main/main.cpp
+
if BUILD_TESTS
-stellar_core_SOURCES = main/StellarCoreVersion.cpp main/XDRFilesSha256.cpp $(SRC_CXX_FILES) $(SRC_TEST_CXX_FILES)
+stellar_core_SOURCES = $(MAIN_FILES) $(SRC_CXX_FILES) $(SRC_TEST_CXX_FILES)
CARGO_FEATURE_TESTUTILS = --features testutils
+# Pass testutils to soroban max protocol so fuzz targets can be smoke tested
+SOROBAN_MAX_PROTOCOL_TESTUTILS_FLAGS = --features testutils
else # !BUILD_TESTS
-stellar_core_SOURCES = main/StellarCoreVersion.cpp main/XDRFilesSha256.cpp $(SRC_CXX_FILES)
+stellar_core_SOURCES = $(MAIN_FILES) $(SRC_CXX_FILES)
CARGO_FEATURE_TESTUTILS =
+SOROBAN_MAX_PROTOCOL_TESTUTILS_FLAGS =
endif # !BUILD_TESTS
+# ENABLE_FUZZ controls fuzz *instrumentation* and fuzz binary building.
+# The fuzz *targets* themselves (the logic to test) are available under
+# testutils, so they can be smoke-tested in normal BUILD_TESTS builds.
+# ENABLE_FUZZ adds:
+# - FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION define (bypasses signature checks, etc)
+# - Building fuzz_* binaries
+# - Linking against LIB_FUZZING_ENGINE
+if ENABLE_FUZZ
+# Keep the fuzz feature for any instrumentation-specific code, though currently
+# it just enables testutils in the soroban crate.
+CARGO_FEATURE_FUZZ = --features fuzz
+else
+CARGO_FEATURE_FUZZ =
+endif
+
if ENABLE_NEXT_PROTOCOL_VERSION_UNSAFE_FOR_PRODUCTION
CARGO_FEATURE_NEXT = --features next
@@ -85,7 +113,7 @@ TEST_FILES = $(TESTDATA_DIR)/stellar-core_example.cfg $(TESTDATA_DIR)/stellar-co
$(TESTDATA_DIR)/stellar-history.testnet.6714239.networkPassphrase.json \
$(TESTDATA_DIR)/stellar-history.testnet.6714239.networkPassphrase.v2.json
-BUILT_SOURCES = $(SRC_X_FILES:.x=.h) main/StellarCoreVersion.cpp main/XDRFilesSha256.cpp $(TEST_FILES)
+BUILT_SOURCES = $(SRC_X_FILES:.x=.h) $(GENERATED_VERSION_SOURCES) $(TEST_FILES)
$(SRC_X_FILES:.x=.h): $(XDRC)
SUFFIXES = .x .h .rs
@@ -105,8 +133,8 @@ util/xdrquery/XDRQueryParser.cpp: util/xdrquery/XDRQueryParser.yy
util/xdrquery/XDRQueryParser.h: util/xdrquery/XDRQueryParser.cpp
touch $@
-BUILT_SOURCES += util/xdrquery/XDRQueryScanner.cpp util/xdrquery/XDRQueryParser.h util/xdrquery/XDRQueryParser.cpp
-stellar_core_SOURCES += util/xdrquery/XDRQueryScanner.cpp util/xdrquery/XDRQueryParser.h util/xdrquery/XDRQueryParser.cpp
+BUILT_SOURCES += $(GENERATED_XDRQUERY_SOURCES) util/xdrquery/XDRQueryParser.h
+stellar_core_SOURCES += $(GENERATED_XDRQUERY_SOURCES) util/xdrquery/XDRQueryParser.h
# Old automakes have buggy dependency tracking for conditional generated
# sources. We work around this here by making rust/RustBridge.{cpp,h} generated
@@ -115,8 +143,8 @@ stellar_core_SOURCES += util/xdrquery/XDRQueryScanner.cpp util/xdrquery/XDRQuery
# names of depfiles from the Makefile itself, we can't use any variables in the
# SOURCES addition we're doing here, have to list unadorned paths.
-BUILT_SOURCES += rust/RustBridge.h rust/RustBridge.cpp
-stellar_core_SOURCES += rust/RustBridge.h rust/RustBridge.cpp
+BUILT_SOURCES += rust/RustBridge.h $(GENERATED_RUST_SOURCES)
+stellar_core_SOURCES += rust/RustBridge.h $(GENERATED_RUST_SOURCES)
if UNIFIED_RUST
RUST_TOOLCHAIN_CHANNEL=nightly
@@ -293,7 +321,7 @@ $(LIBRUST_STELLAR_CORE): $(RUST_HOST_DEPFILES) $(SRC_RUST_FILES) Makefile $(RUST
$(RUST_PROFILE_ARG) \
--locked \
--target-dir $(abspath $(RUST_TARGET_DIR)) \
- $(CARGO_FEATURE_TRACY) $(CARGO_FEATURE_NEXT) $(CARGO_FEATURE_TESTUTILS)
+ $(CARGO_FEATURE_TRACY) $(CARGO_FEATURE_NEXT) $(CARGO_FEATURE_TESTUTILS) $(CARGO_FEATURE_FUZZ)
ranlib $@
else # !UNIFIED_RUST
@@ -350,7 +378,7 @@ $(SOROBAN_LIBS_STAMP): $(wildcard rust/soroban/p*/Cargo.lock) Makefile $(RUST_DE
FEATURE_FLAGS="" \
;; \
$(SOROBAN_MAX_PROTOCOL)) \
- FEATURE_FLAGS="$(CARGO_FEATURE_TRACY) $(SOROBAN_FEATURE_NEXT)" \
+ FEATURE_FLAGS="$(CARGO_FEATURE_TRACY) $(SOROBAN_FEATURE_NEXT) $(SOROBAN_MAX_PROTOCOL_TESTUTILS_FLAGS)" \
;; \
*) \
FEATURE_FLAGS="$(CARGO_FEATURE_TRACY)" \
@@ -396,7 +424,7 @@ $(LIBRUST_STELLAR_CORE): $(RUST_HOST_DEPFILES) $(SRC_RUST_FILES) Makefile $(SORO
$(RUST_PROFILE_ARG) \
--locked \
--target-dir $(abspath $(RUST_TARGET_DIR)) \
- $(CARGO_FEATURE_TRACY) $(CARGO_FEATURE_NEXT) $(CARGO_FEATURE_TESTUTILS) \
+ $(CARGO_FEATURE_TRACY) $(CARGO_FEATURE_NEXT) $(CARGO_FEATURE_TESTUTILS) $(CARGO_FEATURE_FUZZ) \
-- \
$(ALL_SOROBAN_EXTERN_ARGS) \
$(ALL_SOROBAN_DEPEND_ARGS)
@@ -443,32 +471,150 @@ if USE_CLANG_FORMAT
endif # USE_CLANG_FORMAT
cd $(srcdir) && $(CARGO) fmt --all
-if USE_AFL_FUZZ
-FUZZER_MODE ?= overlay
-
-fuzz-testcases: stellar-core
- mkdir -p fuzz-testcases
- for i in `seq 1 10000`; do \
- ./stellar-core gen-fuzz --mode=${FUZZER_MODE} fuzz-testcases/fuzz$$i.xdr ; \
+# Unified fuzzing infrastructure (libfuzzer-compatible)
+# These targets build separate binaries for each fuzz target that can be
+# run with libfuzzer, honggfuzz, or AFL++.
+#
+# To build: LIB_FUZZING_ENGINE="-fsanitize=fuzzer" ./configure --enable-fuzz && make fuzz-targets
+# To run: ./fuzz_tx corpus/tx/
+#
+# Naming follows oss-fuzz conventions:
+# - Binary: fuzz_
+# - Corpus: fuzz__seed_corpus.zip
+# - Dictionary: fuzz_.dict
+if ENABLE_FUZZ
+
+# ============================================================================
+# Fuzz Build Configuration
+# ============================================================================
+
+# FuzzMain.cpp is the only file that needs FUZZ_TARGET_NAME, so we compile it
+# separately for each target. All other sources go into a shared library.
+# SRC_TEST_CXX_FILES already excludes FuzzMain.cpp (see make-mks)
+FUZZ_MAIN = test/fuzz/FuzzMain.cpp
+
+# Common sources for the fuzz library (everything except FuzzMain.cpp)
+FUZZ_COMMON_SOURCES = \
+ $(SRC_CXX_FILES) \
+ $(SRC_TEST_CXX_FILES) \
+ $(GENERATED_VERSION_SOURCES) \
+ $(GENERATED_XDRQUERY_SOURCES) \
+ $(GENERATED_RUST_SOURCES)
+
+# Common compiler flags for fuzz targets
+FUZZ_CXXFLAGS = $(AM_CPPFLAGS) $(AM_CXXFLAGS) -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -DBUILD_TESTS=1
+
+# Convenience library containing all common fuzz code (compiled ONCE)
+# This avoids recompiling hundreds of .o files for each fuzz target.
+noinst_LIBRARIES = libfuzz_common.a
+libfuzz_common_a_SOURCES = $(FUZZ_COMMON_SOURCES)
+libfuzz_common_a_CXXFLAGS = $(FUZZ_CXXFLAGS)
+
+# Common libraries for linking
+FUZZ_LIBS = libfuzz_common.a \
+ $(soci_LIBS) $(libmedida_LIBS) \
+ $(top_builddir)/lib/lib3rdparty.a $(sqlite3_LIBS) \
+ $(libpq_LIBS) $(xdrpp_LIBS) $(libsodium_LIBS) $(libunwind_LIBS) \
+ $(LIBRUST_STELLAR_CORE) -ldl
+
+# Link flags for fuzz targets.
+# We use --whole-archive to force inclusion of all registration objects from
+# libfuzz_common.a (otherwise static constructors for target registration are
+# discarded by the linker as "unreferenced")
+FUZZ_LINKER_FLAGS = -Wl,--whole-archive,libfuzz_common.a,--no-whole-archive $(LIB_FUZZING_ENGINE)
+
+# ============================================================================
+# Fuzz Target Infrastructure
+# ============================================================================
+#
+# To add a new fuzz target:
+# 1. Create src/test/fuzz/targets/FuzzTarget.cpp implementing FuzzTarget
+# 2. Add to FUZZ_TARGETS
+# 3. Add fuzz_ to noinst_PROGRAMS
+# 4. Add fuzz__{SOURCES,CXXFLAGS,LDADD,LDFLAGS} target definition
+# 5. Run ./make-mks && ./autogen.sh && ./configure --enable-fuzz
+#
+# The automake rules will automatically:
+# - Build fuzz_ binary
+# - Create seed corpus generation target
+# - Create regression test target
+
+# List of fuzz targets (lowercase names)
+FUZZ_TARGETS = tx overlay soroban_expr soroban_wasmi
+
+# Fuzz binaries are noinst because they're development tools
+noinst_PROGRAMS = fuzz_tx fuzz_overlay fuzz_soroban_expr fuzz_soroban_wasmi
+
+# Each fuzz target only compiles FuzzMain.cpp (with its specific -DFUZZ_TARGET_NAME)
+# and links against the shared libfuzz_common.a library.
+fuzz_tx_SOURCES = $(FUZZ_MAIN)
+fuzz_tx_CXXFLAGS = $(FUZZ_CXXFLAGS) -DFUZZ_TARGET_NAME=\"tx\"
+fuzz_tx_LDADD = $(FUZZ_LIBS)
+fuzz_tx_LDFLAGS = $(FUZZ_LINKER_FLAGS)
+
+fuzz_overlay_SOURCES = $(FUZZ_MAIN)
+fuzz_overlay_CXXFLAGS = $(FUZZ_CXXFLAGS) -DFUZZ_TARGET_NAME=\"overlay\"
+fuzz_overlay_LDADD = $(FUZZ_LIBS)
+fuzz_overlay_LDFLAGS = $(FUZZ_LINKER_FLAGS)
+
+# Soroban fuzz targets (require --features fuzz in Rust build)
+fuzz_soroban_expr_SOURCES = $(FUZZ_MAIN)
+fuzz_soroban_expr_CXXFLAGS = $(FUZZ_CXXFLAGS) -DFUZZ_TARGET_NAME=\"soroban_expr\"
+fuzz_soroban_expr_LDADD = $(FUZZ_LIBS)
+fuzz_soroban_expr_LDFLAGS = $(FUZZ_LINKER_FLAGS)
+
+fuzz_soroban_wasmi_SOURCES = $(FUZZ_MAIN)
+fuzz_soroban_wasmi_CXXFLAGS = $(FUZZ_CXXFLAGS) -DFUZZ_TARGET_NAME=\"soroban_wasmi\"
+fuzz_soroban_wasmi_LDADD = $(FUZZ_LIBS)
+fuzz_soroban_wasmi_LDFLAGS = $(FUZZ_LINKER_FLAGS)
+
+# ============================================================================
+# Phony Targets for Fuzzing Workflow
+# ============================================================================
+
+# Build all fuzz targets
+fuzz-targets: $(noinst_PROGRAMS)
+ @echo "Built fuzz targets: $(FUZZ_TARGETS)"
+
+# Generate seed corpus for all targets
+fuzz-corpus: stellar-core
+ @for target in $(FUZZ_TARGETS); do \
+ mkdir -p corpus/$$target; \
+ echo "Generating $$target seed corpus..."; \
+ ./stellar-core gen-fuzz --target=$$target --output-dir=corpus/$$target 2>/dev/null; \
+ echo "Generated seed files in corpus/$$target/"; \
done
- mkdir -p min-testcases
- afl-cmin -i fuzz-testcases -o min-testcases -m 500 -t 250 ./stellar-core fuzz --ll ERROR --mode=${FUZZER_MODE} @@
- rm -Rf fuzz-testcases
-# when running in parallel,
-# run the same command than below replacing `-M main` with `-S worker_N`
-# and `--process-id 0` with `--process-id N`
+# Run regression tests on all targets
+fuzz-check: stellar-core
+ ./stellar-core test [fuzz]
-fuzz: fuzz-testcases stellar-core
- mkdir -p fuzz-findings
- afl-fuzz -m 500 -M main -t 250 -i min-testcases -o fuzz-findings \
- ./stellar-core fuzz --ll ERROR --process-id 0 --mode=${FUZZER_MODE} @@
+# Package seed corpus as zip files (oss-fuzz convention)
+fuzz-corpus-zip: fuzz-corpus
+ @for target in $(FUZZ_TARGETS); do \
+ if [ -d corpus/$$target ]; then \
+ cd corpus && zip -q -r ../fuzz_$${target}_seed_corpus.zip $$target && cd ..; \
+ echo "Created fuzz_$${target}_seed_corpus.zip"; \
+ fi; \
+ done
+
+# Install fuzz targets to $OUT (for oss-fuzz build.sh)
+fuzz-install:
+ @if [ -z "$(OUT)" ]; then \
+ echo "Error: OUT not set. This target is for oss-fuzz builds."; \
+ exit 1; \
+ fi
+ @for target in $(FUZZ_TARGETS); do \
+ cp fuzz_$$target $(OUT)/fuzz_$$target; \
+ echo "Installed fuzz_$$target to $(OUT)"; \
+ if [ -f fuzz_$${target}_seed_corpus.zip ]; then \
+ cp fuzz_$${target}_seed_corpus.zip $(OUT)/; \
+ fi; \
+ done
-fuzz-clean: always
- rm -Rf fuzz-testcases fuzz-findings
+.PHONY: fuzz-targets fuzz-corpus fuzz-check fuzz-corpus-zip fuzz-install
-distclean-local: fuzz-clean
-endif # USE_AFL_FUZZ
+endif # ENABLE_FUZZ
clean-local:
rm -rf $(top_builddir)/target $(top_builddir)/src/rust/soroban/*/target $(RUST_DEP_TREE_STAMP) $(SOROBAN_LIBS_STAMP)
diff --git a/src/main/CommandLine.cpp b/src/main/CommandLine.cpp
index 844e9f73a0..4bf81bfe4f 100644
--- a/src/main/CommandLine.cpp
+++ b/src/main/CommandLine.cpp
@@ -47,13 +47,14 @@
#ifdef BUILD_TESTS
#include "simulation/ApplyLoad.h"
-#include "test/Fuzzer.h"
#include "test/TestUtils.h"
-#include "test/fuzz.h"
+#include "test/fuzz/FuzzTargetRegistry.h"
#include "test/test.h"
#endif
+#include
#include
+#include
#include
#include
#include
@@ -196,13 +197,6 @@ fileNameParser(std::string& string)
return requiredArgParser(string, "FILE-NAME");
}
-clara::Opt
-processIDParser(int& num)
-{
- return clara::Opt{num, "PROCESS-ID"}["--process-id"](
- "for spawning multiple instances in fuzzing parallelization");
-}
-
clara::Opt
outputFileParser(std::string& string)
{
@@ -1742,83 +1736,130 @@ runRebuildLedgerFromBuckets(CommandLineArgs const& args)
}
ParserWithValidation
-fuzzerModeParser(std::string& fuzzerModeArg, FuzzerMode& fuzzerMode)
+fuzzTargetParser(std::string& targetName)
{
- auto validateFuzzerMode = [&] {
- if (iequals(fuzzerModeArg, "overlay"))
+ auto validateTarget = [&] {
+ if (targetName.empty())
{
- fuzzerMode = FuzzerMode::OVERLAY;
- return "";
+ return "--target is required. Use 'stellar-core fuzz-list' to "
+ "see available targets.";
}
-
- if (iequals(fuzzerModeArg, "tx"))
+ // Validate against the registry
+ if (FuzzTargetRegistry::instance().findTarget(targetName))
{
- fuzzerMode = FuzzerMode::TRANSACTION;
return "";
}
-
- return "Unrecognized fuzz mode. Please select a valid mode.";
+ return "Unrecognized fuzz target. Use 'stellar-core fuzz-list' to see "
+ "available targets.";
};
- return {clara::Opt{fuzzerModeArg, "FUZZER-MODE"}["--mode"](
- "set the fuzzer mode. Expected modes: overlay, "
- "tx. Defaults to overlay."),
- validateFuzzerMode};
+ return {clara::Opt{targetName, "FUZZ-TARGET"}["--target"](
+ "set the fuzz target (required). Use 'stellar-core "
+ "fuzz-list' to see available targets."),
+ validateTarget};
}
int
runFuzz(CommandLineArgs const& args)
{
LogLevel logLevel{LogLevel::LVL_FATAL};
- std::vector metrics;
std::string fileName;
std::string outputFile;
- int processID = 0;
- bool consoleLog = false;
- FuzzerMode fuzzerMode{FuzzerMode::OVERLAY};
- std::string fuzzerModeArg = "overlay";
+ std::string targetName;
- return runWithHelp(args,
- {logLevelParser(logLevel), metricsParser(metrics),
- consoleParser(consoleLog), fileNameParser(fileName),
- outputFileParser(outputFile),
- processIDParser(processID),
- fuzzerModeParser(fuzzerModeArg, fuzzerMode)},
- [&] {
- Logging::setLogLevel(logLevel, nullptr);
- if (!outputFile.empty())
- {
- Logging::setLoggingToFile(outputFile);
- }
+ return runWithHelp(
+ args,
+ {logLevelParser(logLevel), fileNameParser(fileName),
+ outputFileParser(outputFile), fuzzTargetParser(targetName)},
+ [&] {
+ if (fileName.empty())
+ {
+ std::cerr << "Error: input file is required\n";
+ return 1;
+ }
- fuzz(fileName, metrics, processID, fuzzerMode);
- return 0;
- });
+ Logging::setLogLevel(logLevel, nullptr);
+ if (!outputFile.empty())
+ {
+ Logging::setLoggingToFile(outputFile);
+ }
+
+ auto target =
+ FuzzTargetRegistry::instance().createTarget(targetName);
+ target->initialize();
+
+ std::ifstream in(fileName, std::ios::binary);
+ std::vector data(target->maxInputSize());
+ in.read(reinterpret_cast(data.data()), data.size());
+ auto actual = in.gcount();
+ if (actual > 0)
+ {
+ data.resize(actual);
+ target->run(data.data(), data.size());
+ }
+
+ target->shutdown();
+ return 0;
+ });
+}
+
+int
+runFuzzList(CommandLineArgs const& args)
+{
+ // List all available fuzz targets from the registry
+ std::cout << "Available fuzz targets:\n";
+ for (auto const& target : FuzzTargetRegistry::instance().targets())
+ {
+ std::cout << " " << target.name << " - " << target.description << "\n";
+ }
+ std::cout << "\nUsage:\n";
+ std::cout << " stellar-core fuzz-one --target= \n";
+ std::cout << " stellar-core gen-fuzz --target= "
+ "--output-dir= [--count=]\n";
+ return 0;
}
int
runGenFuzz(CommandLineArgs const& args)
{
+ constexpr int DEFAULT_CORPUS_SIZE = 100;
LogLevel logLevel{LogLevel::LVL_FATAL};
- std::string fileName;
- std::string outputFile;
- FuzzerMode fuzzerMode{FuzzerMode::OVERLAY};
- std::string fuzzerModeArg = "overlay";
- int processID = 0;
+ std::string outputDir;
+ std::string targetName;
+ int count = DEFAULT_CORPUS_SIZE;
+
+ auto outputDirParser = [](std::string& dir) {
+ return clara::Opt{dir, "DIR"}["--output-dir"]["-o"](
+ "output directory for corpus files (required)");
+ };
+
+ auto countParser = [](int& c) {
+ return clara::Opt{c, "COUNT"}["--count"]["-n"](
+ "number of corpus files to generate (default: 100)");
+ };
return runWithHelp(
args,
- {logLevelParser(logLevel), fileNameParser(fileName),
- outputFileParser(outputFile),
- fuzzerModeParser(fuzzerModeArg, fuzzerMode)},
+ {logLevelParser(logLevel), outputDirParser(outputDir),
+ countParser(count), fuzzTargetParser(targetName)},
[&] {
- Logging::setLogLevel(logLevel, nullptr);
- if (!outputFile.empty())
+ if (outputDir.empty())
{
- Logging::setLoggingToFile(outputFile);
+ std::cerr << "Error: --output-dir is required\n";
+ return 1;
}
- FuzzUtils::createFuzzer(processID, fuzzerMode)->genFuzz(fileName);
+ Logging::setLogLevel(logLevel, nullptr);
+
+ // Create output directory if it doesn't exist
+ std::filesystem::create_directories(outputDir);
+
+ auto target =
+ FuzzTargetRegistry::instance().createTarget(targetName);
+ target->generateSeedCorpus(outputDir, count);
+
+ std::cout << "Generated " << count << " corpus files in "
+ << outputDir << "\n";
return 0;
});
}
@@ -2107,8 +2148,10 @@ handleCommandLine(int argc, char* const* argv)
{"rebuild-ledger-from-buckets",
"rebuild the current database ledger from the bucket list",
runRebuildLedgerFromBuckets},
- {"fuzz", "run a single fuzz input and exit", runFuzz},
- {"gen-fuzz", "generate a random fuzzer input file", runGenFuzz},
+ {"fuzz-one", "run a single fuzz input and exit", runFuzz},
+ {"gen-fuzz", "generate fuzz input files for a seed corpus",
+ runGenFuzz},
+ {"fuzz-list", "list available fuzz targets", runFuzzList},
{"test", "execute test suite", runTest},
{"apply-load", "run apply time load test", runApplyLoad},
{"pregenerate-loadgen-txs",
@@ -2126,7 +2169,7 @@ handleCommandLine(int argc, char* const* argv)
fmt::format(FMT_STRING("{0} {1}"), exeName, command->name());
auto args = CommandLineArgs{exeName, commandName, command->description(),
adjustedCommandLine.second};
- if (command->name() == "run" || command->name() == "fuzz" ||
+ if (command->name() == "run" || command->name() == "fuzz-one" ||
command->name() == "test")
{
// run outside of catch block so that we properly capture crashes
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index e69de29bb2..4d6c427034 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -0,0 +1,1995 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "ahash"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "arbitrary"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
+dependencies = [
+ "derive_arbitrary",
+]
+
+[[package]]
+name = "ark-bls12-381"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488"
+dependencies = [
+ "ark-ec",
+ "ark-ff",
+ "ark-serialize",
+ "ark-std",
+]
+
+[[package]]
+name = "ark-bn254"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f"
+dependencies = [
+ "ark-ec",
+ "ark-ff",
+ "ark-std",
+]
+
+[[package]]
+name = "ark-ec"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba"
+dependencies = [
+ "ark-ff",
+ "ark-poly",
+ "ark-serialize",
+ "ark-std",
+ "derivative",
+ "hashbrown 0.13.2",
+ "itertools 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 = "autocfg"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "base16ct"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
+
+[[package]]
+name = "base32"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa"
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "base64ct"
+version = "1.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
+
+[[package]]
+name = "batsat"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec82b6bbce8ea42f5003417b699267860a9f4dd869fc9ba8faceac761d5afed1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
+
+[[package]]
+name = "cc"
+version = "1.2.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932"
+dependencies = [
+ "find-msvc-tools",
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "cfg_eval"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crate-git-revision"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98"
+dependencies = [
+ "serde",
+ "serde_derive",
+ "serde_json",
+]
+
+[[package]]
+name = "crypto-bigint"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
+dependencies = [
+ "generic-array",
+ "rand_core",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "curve25519-dalek"
+version = "4.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "curve25519-dalek-derive",
+ "digest",
+ "fiat-crypto",
+ "rustc_version",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "curve25519-dalek-derive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "cxx"
+version = "1.0.97"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e88abab2f5abbe4c56e8f1fb431b784d710b709888f35755a160e62e33fe38e8"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.97"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d3816ed957c008ccd4728485511e3d9aaf7db419aa321e3d2c5a2f3411e36c8"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.97"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a26acccf6f445af85ea056362561a24ef56cdc15fcc685f03aec50b9c702cb6d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
+
+[[package]]
+name = "der"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
+dependencies = [
+ "const-oid",
+ "zeroize",
+]
+
+[[package]]
+name = "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.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "const-oid",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "downcast-rs"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
+
+[[package]]
+name = "ecdsa"
+version = "0.16.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
+dependencies = [
+ "der",
+ "digest",
+ "elliptic-curve",
+ "rfc6979",
+ "signature",
+]
+
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "pkcs8",
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "rand_core",
+ "serde",
+ "sha2",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "elliptic-curve"
+version = "0.13.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
+dependencies = [
+ "base16ct",
+ "crypto-bigint",
+ "digest",
+ "ff",
+ "generic-array",
+ "group",
+ "rand_core",
+ "sec1",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "escape-bytes"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2"
+
+[[package]]
+name = "ethnum"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b"
+
+[[package]]
+name = "ff"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
+dependencies = [
+ "rand_core",
+ "subtle",
+]
+
+[[package]]
+name = "fiat-crypto"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db"
+
+[[package]]
+name = "fixedbitset"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+
+[[package]]
+name = "generator"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "libc",
+ "log",
+ "rustversion",
+ "windows-link",
+ "windows-result",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
+dependencies = [
+ "typenum",
+ "version_check",
+ "zeroize",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "group"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
+dependencies = [
+ "ff",
+ "rand_core",
+ "subtle",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hex-literal"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
+
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.16.1",
+]
+
+[[package]]
+name = "indexmap-nostd"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590"
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
+
+[[package]]
+name = "js-sys"
+version = "0.3.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "k256"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b"
+dependencies = [
+ "cfg-if",
+ "ecdsa",
+ "elliptic-curve",
+ "sha2",
+]
+
+[[package]]
+name = "keccak"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
+dependencies = [
+ "cpufeatures",
+]
+
+[[package]]
+name = "lazy_static"
+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 = "libc"
+version = "0.2.180"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
+
+[[package]]
+name = "libm"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
+
+[[package]]
+name = "link-cplusplus"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "log"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
+
+[[package]]
+name = "loom"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
+dependencies = [
+ "cfg-if",
+ "generator",
+ "scoped-tls",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "matchers"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
+dependencies = [
+ "regex-automata",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.50.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-derive"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "p256"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
+dependencies = [
+ "ecdsa",
+ "elliptic-curve",
+ "primeorder",
+ "sha2",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "petgraph"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
+dependencies = [
+ "fixedbitset",
+ "indexmap",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "primeorder"
+version = "0.13.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
+dependencies = [
+ "elliptic-curve",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.105"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
+
+[[package]]
+name = "rfc6979"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
+dependencies = [
+ "hmac",
+ "subtle",
+]
+
+[[package]]
+name = "rustc-simple-version"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2c140994fc6f44f2a89068a073843e82e7b4905f569ced81540a2eab4d0d6ed"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
+name = "sec1"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
+dependencies = [
+ "base16ct",
+ "der",
+ "generic-array",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.149"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
+dependencies = [
+ "itoa",
+ "memchr",
+ "serde",
+ "serde_core",
+ "zmij",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sha3"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
+dependencies = [
+ "digest",
+ "keccak",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "digest",
+ "rand_core",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "soroban-builtin-sdk-macros"
+version = "21.2.2"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=7eeddd897cfb0f700f938b0c8d6f0541150d1fcb#7eeddd897cfb0f700f938b0c8d6f0541150d1fcb"
+dependencies = [
+ "itertools 0.11.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "soroban-builtin-sdk-macros"
+version = "22.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=1cd8b8dca9aeeca9ce45b129cd923992b32dc258#1cd8b8dca9aeeca9ce45b129cd923992b32dc258"
+dependencies = [
+ "itertools 0.10.5",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "soroban-builtin-sdk-macros"
+version = "23.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=688bc34e6cd15c71742139e625268c7f30f55a92#688bc34e6cd15c71742139e625268c7f30f55a92"
+dependencies = [
+ "itertools 0.10.5",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "soroban-builtin-sdk-macros"
+version = "24.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=a37eeda815e626f416eff13f2eacb32a8b0c3729#a37eeda815e626f416eff13f2eacb32a8b0c3729"
+dependencies = [
+ "itertools 0.10.5",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "soroban-builtin-sdk-macros"
+version = "25.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=0a0c2df704edeb3cdafabfcc759eddccd6775337#0a0c2df704edeb3cdafabfcc759eddccd6775337"
+dependencies = [
+ "itertools 0.10.5",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "soroban-env-common"
+version = "21.2.2"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=7eeddd897cfb0f700f938b0c8d6f0541150d1fcb#7eeddd897cfb0f700f938b0c8d6f0541150d1fcb"
+dependencies = [
+ "crate-git-revision",
+ "ethnum",
+ "num-derive",
+ "num-traits",
+ "soroban-env-macros 21.2.2",
+ "soroban-wasmi",
+ "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=1cd8b8dca9aeeca9ce45b129cd923992b32dc258#1cd8b8dca9aeeca9ce45b129cd923992b32dc258"
+dependencies = [
+ "crate-git-revision",
+ "ethnum",
+ "num-derive",
+ "num-traits",
+ "soroban-env-macros 22.0.0",
+ "soroban-wasmi",
+ "static_assertions",
+ "stellar-xdr 22.0.0",
+ "wasmparser",
+]
+
+[[package]]
+name = "soroban-env-common"
+version = "23.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=688bc34e6cd15c71742139e625268c7f30f55a92#688bc34e6cd15c71742139e625268c7f30f55a92"
+dependencies = [
+ "crate-git-revision",
+ "ethnum",
+ "num-derive",
+ "num-traits",
+ "soroban-env-macros 23.0.0",
+ "soroban-wasmi",
+ "static_assertions",
+ "stellar-xdr 23.0.0",
+ "wasmparser",
+]
+
+[[package]]
+name = "soroban-env-common"
+version = "24.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=a37eeda815e626f416eff13f2eacb32a8b0c3729#a37eeda815e626f416eff13f2eacb32a8b0c3729"
+dependencies = [
+ "crate-git-revision",
+ "ethnum",
+ "num-derive",
+ "num-traits",
+ "soroban-env-macros 24.0.0",
+ "soroban-wasmi",
+ "static_assertions",
+ "stellar-xdr 24.0.0",
+ "wasmparser",
+]
+
+[[package]]
+name = "soroban-env-common"
+version = "25.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=0a0c2df704edeb3cdafabfcc759eddccd6775337#0a0c2df704edeb3cdafabfcc759eddccd6775337"
+dependencies = [
+ "arbitrary",
+ "crate-git-revision",
+ "ethnum",
+ "num-derive",
+ "num-traits",
+ "soroban-env-macros 25.0.0",
+ "soroban-wasmi",
+ "static_assertions",
+ "stellar-xdr 24.0.1",
+ "wasmparser",
+]
+
+[[package]]
+name = "soroban-env-host"
+version = "21.2.2"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=7eeddd897cfb0f700f938b0c8d6f0541150d1fcb#7eeddd897cfb0f700f938b0c8d6f0541150d1fcb"
+dependencies = [
+ "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 21.2.2",
+ "soroban-env-common 21.2.2",
+ "soroban-wasmi",
+ "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=1cd8b8dca9aeeca9ce45b129cd923992b32dc258#1cd8b8dca9aeeca9ce45b129cd923992b32dc258"
+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",
+ "static_assertions",
+ "stellar-strkey 0.0.9",
+ "wasmparser",
+]
+
+[[package]]
+name = "soroban-env-host"
+version = "23.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=688bc34e6cd15c71742139e625268c7f30f55a92#688bc34e6cd15c71742139e625268c7f30f55a92"
+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 23.0.0",
+ "soroban-env-common 23.0.0",
+ "soroban-wasmi",
+ "static_assertions",
+ "stellar-strkey 0.0.13",
+ "wasmparser",
+]
+
+[[package]]
+name = "soroban-env-host"
+version = "24.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=a37eeda815e626f416eff13f2eacb32a8b0c3729#a37eeda815e626f416eff13f2eacb32a8b0c3729"
+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 24.0.0",
+ "soroban-env-common 24.0.0",
+ "soroban-wasmi",
+ "static_assertions",
+ "stellar-strkey 0.0.13",
+ "wasmparser",
+]
+
+[[package]]
+name = "soroban-env-host"
+version = "25.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=0a0c2df704edeb3cdafabfcc759eddccd6775337#0a0c2df704edeb3cdafabfcc759eddccd6775337"
+dependencies = [
+ "ark-bls12-381",
+ "ark-bn254",
+ "ark-ec",
+ "ark-ff",
+ "ark-serialize",
+ "curve25519-dalek",
+ "ecdsa",
+ "ed25519-dalek",
+ "elliptic-curve",
+ "generic-array",
+ "getrandom",
+ "hex-literal",
+ "hmac",
+ "k256",
+ "num-derive",
+ "num-integer",
+ "num-traits",
+ "p256",
+ "rand",
+ "rand_chacha",
+ "sec1",
+ "sha2",
+ "sha3",
+ "soroban-builtin-sdk-macros 25.0.0",
+ "soroban-env-common 25.0.0",
+ "soroban-wasmi",
+ "static_assertions",
+ "stellar-strkey 0.0.13",
+ "wasmparser",
+]
+
+[[package]]
+name = "soroban-env-macros"
+version = "21.2.2"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=7eeddd897cfb0f700f938b0c8d6f0541150d1fcb#7eeddd897cfb0f700f938b0c8d6f0541150d1fcb"
+dependencies = [
+ "itertools 0.11.0",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_json",
+ "stellar-xdr 21.2.0",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "soroban-env-macros"
+version = "22.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=1cd8b8dca9aeeca9ce45b129cd923992b32dc258#1cd8b8dca9aeeca9ce45b129cd923992b32dc258"
+dependencies = [
+ "itertools 0.10.5",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_json",
+ "stellar-xdr 22.0.0",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "soroban-env-macros"
+version = "23.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=688bc34e6cd15c71742139e625268c7f30f55a92#688bc34e6cd15c71742139e625268c7f30f55a92"
+dependencies = [
+ "itertools 0.10.5",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_json",
+ "stellar-xdr 23.0.0",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "soroban-env-macros"
+version = "24.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=a37eeda815e626f416eff13f2eacb32a8b0c3729#a37eeda815e626f416eff13f2eacb32a8b0c3729"
+dependencies = [
+ "itertools 0.10.5",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_json",
+ "stellar-xdr 24.0.0",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "soroban-env-macros"
+version = "25.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=0a0c2df704edeb3cdafabfcc759eddccd6775337#0a0c2df704edeb3cdafabfcc759eddccd6775337"
+dependencies = [
+ "itertools 0.10.5",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_json",
+ "stellar-xdr 24.0.1",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "soroban-synth-wasm"
+version = "23.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=688bc34e6cd15c71742139e625268c7f30f55a92#688bc34e6cd15c71742139e625268c7f30f55a92"
+dependencies = [
+ "arbitrary",
+ "soroban-env-common 23.0.0",
+ "soroban-env-macros 23.0.0",
+ "stellar-xdr 23.0.0",
+ "wasm-encoder",
+ "wasmparser",
+]
+
+[[package]]
+name = "soroban-synth-wasm"
+version = "25.0.0"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=0a0c2df704edeb3cdafabfcc759eddccd6775337#0a0c2df704edeb3cdafabfcc759eddccd6775337"
+dependencies = [
+ "arbitrary",
+ "soroban-env-common 25.0.0",
+ "soroban-env-macros 25.0.0",
+ "stellar-xdr 24.0.1",
+ "wasm-encoder",
+ "wasmparser",
+]
+
+[[package]]
+name = "soroban-test-wasms"
+version = "23.0.2"
+source = "git+https://github.com/stellar/rs-soroban-env?rev=30249f0549141dbe7fdce84b6401a4235dbad64f#30249f0549141dbe7fdce84b6401a4235dbad64f"
+
+[[package]]
+name = "soroban-wasmi"
+version = "0.31.1-soroban.20.0.1"
+source = "git+https://github.com/stellar/wasmi?rev=0ed3f3dee30dc41ebe21972399e0a73a41944aa0#0ed3f3dee30dc41ebe21972399e0a73a41944aa0"
+dependencies = [
+ "smallvec",
+ "spin",
+ "wasmi_arena",
+ "wasmi_core",
+ "wasmparser-nostd",
+]
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "spki"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "stellar-core"
+version = "0.1.0"
+dependencies = [
+ "base64 0.13.1",
+ "cxx",
+ "ed25519-dalek",
+ "itertools 0.10.5",
+ "log",
+ "rand",
+ "rustc-simple-version",
+ "soroban-env-host 21.2.2",
+ "soroban-env-host 22.0.0",
+ "soroban-env-host 23.0.0",
+ "soroban-env-host 24.0.0",
+ "soroban-env-host 25.0.0",
+ "soroban-synth-wasm 23.0.0",
+ "soroban-synth-wasm 25.0.0",
+ "soroban-test-wasms",
+ "soroban-wasmi",
+ "stellar-quorum-analyzer",
+ "tracy-client",
+]
+
+[[package]]
+name = "stellar-quorum-analyzer"
+version = "0.1.0"
+source = "git+https://github.com/stellar/stellar-quorum-analyzer?rev=de80f85128feb560a626c4d3abc9704d3d32e2a2#de80f85128feb560a626c4d3abc9704d3d32e2a2"
+dependencies = [
+ "batsat",
+ "itertools 0.10.5",
+ "log",
+ "petgraph",
+ "stellar-strkey 0.0.13",
+ "stellar-xdr 25.0.0-rc.1",
+]
+
+[[package]]
+name = "stellar-strkey"
+version = "0.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12d2bf45e114117ea91d820a846fd1afbe3ba7d717988fee094ce8227a3bf8bd"
+dependencies = [
+ "base32",
+ "crate-git-revision",
+ "thiserror",
+]
+
+[[package]]
+name = "stellar-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-strkey"
+version = "0.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee1832fb50c651ad10f734aaf5d31ca5acdfb197a6ecda64d93fcdb8885af913"
+dependencies = [
+ "crate-git-revision",
+ "data-encoding",
+]
+
+[[package]]
+name = "stellar-xdr"
+version = "21.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2675a71212ed39a806e415b0dbf4702879ff288ec7f5ee996dda42a135512b50"
+dependencies = [
+ "base64 0.13.1",
+ "crate-git-revision",
+ "escape-bytes",
+ "hex",
+ "stellar-strkey 0.0.8",
+]
+
+[[package]]
+name = "stellar-xdr"
+version = "22.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20c2130275cc730d042b3082f51145f0486f5a543d6d72fced02ed9048b82b57"
+dependencies = [
+ "base64 0.13.1",
+ "crate-git-revision",
+ "escape-bytes",
+ "hex",
+ "stellar-strkey 0.0.9",
+]
+
+[[package]]
+name = "stellar-xdr"
+version = "23.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89d2848e1694b0c8db81fd812bfab5ea71ee28073e09ccc45620ef3cf7a75a9b"
+dependencies = [
+ "base64 0.22.1",
+ "cfg_eval",
+ "crate-git-revision",
+ "escape-bytes",
+ "ethnum",
+ "hex",
+ "sha2",
+ "stellar-strkey 0.0.13",
+]
+
+[[package]]
+name = "stellar-xdr"
+version = "24.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ceec163a64a9ed03359dab3b73ca556251020edc0103bae7073a38eb1708ab5"
+dependencies = [
+ "base64 0.22.1",
+ "cfg_eval",
+ "crate-git-revision",
+ "escape-bytes",
+ "ethnum",
+ "hex",
+ "sha2",
+ "stellar-strkey 0.0.13",
+]
+
+[[package]]
+name = "stellar-xdr"
+version = "24.0.1"
+source = "git+https://github.com/stellar/rs-stellar-xdr?rev=89cc1cbadf1b9a16843826954dede7fec514d8e7#89cc1cbadf1b9a16843826954dede7fec514d8e7"
+dependencies = [
+ "arbitrary",
+ "base64 0.22.1",
+ "cfg_eval",
+ "crate-git-revision",
+ "escape-bytes",
+ "ethnum",
+ "hex",
+ "sha2",
+ "stellar-strkey 0.0.13",
+]
+
+[[package]]
+name = "stellar-xdr"
+version = "25.0.0-rc.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c30bd79b4a537c007b8be631e5147723fa50dcbde487a0684437e564afc81e"
+dependencies = [
+ "cfg_eval",
+ "crate-git-revision",
+ "escape-bytes",
+ "ethnum",
+ "hex",
+ "sha2",
+ "stellar-strkey 0.0.13",
+]
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
+dependencies = [
+ "pin-project-lite",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex-automata",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "tracy-client"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59fb931a64ff88984f86d3e9bcd1ae8843aa7fe44dd0f8097527bc172351741d"
+dependencies = [
+ "loom",
+ "once_cell",
+ "tracy-client-sys",
+]
+
+[[package]]
+name = "tracy-client-sys"
+version = "0.22.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d104d610dfa9dd154535102cc9c6164ae1fa37842bc2d9e83f9ac82b0ae0882"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "typenum"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
+
+[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "wasm-encoder"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "822b645bf4f2446b949776ffca47e2af60b167209ffb70814ef8779d299cd421"
+dependencies = [
+ "leb128",
+]
+
+[[package]]
+name = "wasmi_arena"
+version = "0.4.0"
+source = "git+https://github.com/stellar/wasmi?rev=0ed3f3dee30dc41ebe21972399e0a73a41944aa0#0ed3f3dee30dc41ebe21972399e0a73a41944aa0"
+
+[[package]]
+name = "wasmi_core"
+version = "0.13.0"
+source = "git+https://github.com/stellar/wasmi?rev=0ed3f3dee30dc41ebe21972399e0a73a41944aa0#0ed3f3dee30dc41ebe21972399e0a73a41944aa0"
+dependencies = [
+ "downcast-rs",
+ "libm",
+ "num-traits",
+ "paste",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.116.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50"
+dependencies = [
+ "indexmap",
+ "semver",
+]
+
+[[package]]
+name = "wasmparser-nostd"
+version = "0.100.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa"
+dependencies = [
+ "indexmap-nostd",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "zmij"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfcd145825aace48cff44a8844de64bf75feec3080e0aa5cdbde72961ae51a65"
diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml
index 3e43626a32..52df1c353c 100644
--- a/src/rust/Cargo.toml
+++ b/src/rust/Cargo.toml
@@ -110,10 +110,10 @@ tracy-client = { version = "=0.17.0", features = [
# unify them, perturbing the contents of the lockfile.
[dependencies.soroban-env-host-p26]
-version = "=25.0.1"
+version = "=26.0.0-rc.1"
git = "https://github.com/stellar/rs-soroban-env"
package = "soroban-env-host"
-rev = "e527f43cde68e0d17cd6d7d8e162d1944ba5a251"
+rev = "81c917a39f4417a8e414761d21a5bd650417a997"
optional = true
[dependencies.soroban-env-host-p25]
@@ -155,14 +155,20 @@ optional = true
# supported host, since test material usually just grows over time.
[dependencies.soroban-test-wasms]
-version = "=23.0.2"
+version = "=26.0.0-rc.1"
git = "https://github.com/stellar/rs-soroban-env"
-rev = "30249f0549141dbe7fdce84b6401a4235dbad64f"
+rev = "81c917a39f4417a8e414761d21a5bd650417a997"
[dependencies.soroban-synth-wasm]
-version = "=23.0.0"
+version = "=26.0.0-rc.1"
git = "https://github.com/stellar/rs-soroban-env"
-rev = "688bc34e6cd15c71742139e625268c7f30f55a92"
+rev = "81c917a39f4417a8e414761d21a5bd650417a997"
+
+[dependencies.soroban-fuzz-targets]
+version = "=26.0.0-rc.1"
+git = "https://github.com/stellar/rs-soroban-env"
+rev = "81c917a39f4417a8e414761d21a5bd650417a997"
+optional = true
[dependencies.stellar-quorum-analyzer]
version = "0.1.0"
@@ -188,8 +194,9 @@ tracy = ["dep:tracy-client"]
# staging work across intra-protocol releases.
next = []
-# the testutils feature turns on stuff that should only be available in a core
+# The testutils feature turns on stuff that should only be available in a core
# BUILD_TESTS build, such as the code to support transaction re-execution on
-# a secondary host. If necessary it can also turn on "testutils" features in
-# any of the hosts.
-testutils = []
+# a secondary host, AND the fuzz target entry points in soroban-env-host.
+# This allows fuzz smoke tests to run in normal test builds without requiring
+# the full fuzz instrumentation infrastructure.
+testutils = ["dep:soroban-fuzz-targets"]
diff --git a/src/rust/soroban/p26 b/src/rust/soroban/p26
index 60398e4cf3..81c917a39f 160000
--- a/src/rust/soroban/p26
+++ b/src/rust/soroban/p26
@@ -1 +1 @@
-Subproject commit 60398e4cf317eccc8ed32802104834bd1d994928
+Subproject commit 81c917a39f4417a8e414761d21a5bd650417a997
diff --git a/src/rust/src/bridge.rs b/src/rust/src/bridge.rs
index dd0935508f..68f97eb6a6 100644
--- a/src/rust/src/bridge.rs
+++ b/src/rust/src/bridge.rs
@@ -67,6 +67,14 @@ pub(crate) mod rust_bridge {
LVL_TRACE = 5,
}
+ #[namespace = "stellar"]
+ enum FuzzResultCode {
+ FUZZ_SUCCESS = 0,
+ FUZZ_TARGET_UNKNOWN = -1,
+ FUZZ_REJECTED = -2,
+ FUZZ_DISABLED = -3,
+ }
+
struct CxxLedgerInfo {
pub protocol_version: u32,
pub sequence_number: u32,
@@ -364,6 +372,10 @@ pub(crate) mod rust_bridge {
resource_limit: &QuorumCheckerResource,
resource_usage: &mut QuorumCheckerResource,
) -> Result;
+
+ // Soroban fuzzing support - always declared but only functional with --features fuzz.
+ // Panics on internal errors (which libfuzzer will catch as crashes).
+ fn run_soroban_fuzz_target(name: &str, data: &[u8]) -> FuzzResultCode;
}
// And the extern "C++" block declares C++ stuff we're going to import to
@@ -396,6 +408,7 @@ use crate::ed25519_verify::*;
use crate::i128::*;
use crate::log::*;
use crate::quorum_checker::*;
+use crate::soroban_fuzz::*;
use crate::soroban_invoke::*;
use crate::soroban_module_cache::*;
use crate::soroban_proto_all::*;
diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs
index 8babcd44e5..d6c3e587a0 100644
--- a/src/rust/src/lib.rs
+++ b/src/rust/src/lib.rs
@@ -40,6 +40,8 @@ mod soroban_test_wasm;
#[cfg(feature = "testutils")]
mod soroban_test_extra_protocol;
+mod soroban_fuzz;
+
use soroban_module_cache::SorobanModuleCache;
mod bridge;
diff --git a/src/rust/src/soroban_fuzz.rs b/src/rust/src/soroban_fuzz.rs
new file mode 100644
index 0000000000..57d44b16b5
--- /dev/null
+++ b/src/rust/src/soroban_fuzz.rs
@@ -0,0 +1,47 @@
+// Copyright 2026 Stellar Development Foundation and contributors. Licensed
+// under the Apache License, Version 2.0. See the COPYING file at the root
+// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0
+
+//! Soroban fuzz target integration for stellar-core.
+//!
+//! This module provides a bridge to the Soroban fuzz targets defined in the
+//! soroban-env-host crate. The actual fuzz target implementations live in
+//! soroban_env_host::fuzz; this module just provides the C++ bridge interface.
+//!
+//! Note: The fuzz module in soroban-env-host is available when the `testutils`
+//! feature is enabled. This allows fuzz smoke tests to run in normal BUILD_TESTS
+//! builds without requiring the full fuzz instrumentation infrastructure.
+
+use crate::rust_bridge::FuzzResultCode;
+
+// Stub implementation when testutils feature is disabled
+#[cfg(not(all(feature = "next", feature = "testutils")))]
+pub fn run_soroban_fuzz_target(_name: &str, _data: &[u8]) -> FuzzResultCode {
+ FuzzResultCode::FUZZ_DISABLED
+}
+
+// When testutils feature is enabled, import and use the actual implementations
+#[cfg(all(feature = "next", feature = "testutils"))]
+extern crate soroban_env_host_p26;
+
+#[cfg(all(feature = "next", feature = "testutils"))]
+use soroban_fuzz_targets::{self as fuzz, FuzzResult};
+
+/// Run a Soroban fuzz target with the given input bytes.
+/// Panics on internal errors (which is what the fuzzer wants to find!)
+#[cfg(all(feature = "next", feature = "testutils"))]
+pub fn run_soroban_fuzz_target(name: &str, data: &[u8]) -> FuzzResultCode {
+ let result = match name {
+ "soroban_expr" => fuzz::expr::run_fuzz_target(data),
+ "soroban_wasmi" => fuzz::wasmi::run_fuzz_target(data),
+ _ => return FuzzResultCode::FUZZ_TARGET_UNKNOWN,
+ };
+
+ match result {
+ FuzzResult::Ok => FuzzResultCode::FUZZ_SUCCESS,
+ FuzzResult::Reject => FuzzResultCode::FUZZ_REJECTED,
+ FuzzResult::InternalError => {
+ panic!("fuzz target {name} had internal error");
+ }
+ }
+}
diff --git a/src/test/FuzzerImpl.cpp b/src/test/FuzzerImpl.cpp
deleted file mode 100644
index 3b5ab2fd5a..0000000000
--- a/src/test/FuzzerImpl.cpp
+++ /dev/null
@@ -1,2217 +0,0 @@
-// Copyright 2019 Stellar Development Foundation and contributors. Licensed
-// under the Apache License, Version 2.0. See the COPYING file at the root
-// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0
-
-#include "test/FuzzerImpl.h"
-#include "invariant/OrderBookIsNotCrossed.h"
-#include "ledger/LedgerTxn.h"
-#include "ledger/TrustLineWrapper.h"
-#include "ledger/test/LedgerTestUtils.h"
-#include "main/Application.h"
-#include "main/Config.h"
-#include "overlay/OverlayManager.h"
-#include "overlay/TCPPeer.h"
-#include "simulation/Simulation.h"
-#include "test/TestUtils.h"
-#include "test/TxTests.h"
-#include "test/fuzz.h"
-#include "test/test.h"
-#include "transactions/MutableTransactionResult.h"
-#include "transactions/OperationFrame.h"
-#include "transactions/SignatureChecker.h"
-#include "transactions/TransactionMeta.h"
-#include "transactions/TransactionUtils.h"
-#include "util/Logging.h"
-#include "util/Math.h"
-#include "util/XDRCereal.h"
-#include "util/types.h"
-#include "xdr/Stellar-ledger-entries.h"
-#include "xdr/Stellar-transaction.h"
-
-#include
-#include
-#include
-#include
-
-namespace stellar
-{
-namespace FuzzUtils
-{
-namespace
-{
-auto constexpr FUZZER_MAX_OPERATIONS = 5;
-auto constexpr INITIAL_ACCOUNT_BALANCE = 1'000'000LL; // reduced after setup
-auto constexpr INITIAL_ASSET_DISTRIBUTION = 1'000'000LL; // reduced after setup
-auto constexpr FUZZING_FEE = 1;
-auto constexpr FUZZING_RESERVE = 4;
-auto constexpr INITIAL_TRUST_LINE_LIMIT = 5 * INITIAL_ASSET_DISTRIBUTION;
-auto constexpr DEFAULT_NUM_TRANSACTIONS_TO_RESERVE_FEES_FOR = 10;
-auto constexpr MIN_ACCOUNT_BALANCE =
- FUZZING_FEE * DEFAULT_NUM_TRANSACTIONS_TO_RESERVE_FEES_FOR;
-
-// must be strictly less than 255
-uint8_t constexpr NUMBER_OF_PREGENERATED_ACCOUNTS = 5U;
-
-void
-setShortKey(uint256& ed25519, int i)
-{
- ed25519[0] = static_cast(i);
-}
-
-void
-setShortKey(PublicKey& pk, int i)
-{
- setShortKey(pk.ed25519(), i);
-}
-
-uint8_t
-getShortKey(uint256 const& ed25519)
-{
- return ed25519[0];
-}
-
-uint8_t
-getShortKey(PublicKey const& pk)
-{
- return getShortKey(pk.ed25519());
-}
-
-uint8_t constexpr NUMBER_OF_ASSET_ISSUER_BITS = 5;
-uint8_t constexpr NUMBER_OF_ASSET_CODE_BITS = 8 - NUMBER_OF_ASSET_ISSUER_BITS;
-uint8_t constexpr NUMBER_OF_ASSETS_TO_USE = 1 << NUMBER_OF_ASSET_CODE_BITS;
-uint8_t constexpr ENCODE_ASSET_CODE_MASK = NUMBER_OF_ASSETS_TO_USE - 1;
-
-uint8_t
-getShortKey(AssetCode4 const& code)
-{
- return code.data()[0] & ENCODE_ASSET_CODE_MASK;
-}
-
-uint8_t
-getShortKey(AssetCode12 const& code)
-{
- return code.data()[0] & ENCODE_ASSET_CODE_MASK;
-}
-
-uint8_t
-decodeAssetIssuer(uint8_t byte)
-{
- return byte >> NUMBER_OF_ASSET_CODE_BITS;
-}
-
-uint8_t
-decodeAssetCodeDigit(uint8_t byte)
-{
- return byte & ENCODE_ASSET_CODE_MASK;
-}
-
-uint8_t
-getShortKey(Asset const& asset)
-{
- // This encoding does _not_ make compacting a left-inverse of unpack. We
- // could make it so, but it's not necessary -- compacting, which alone uses
- // this function, is operating on a randomly-generated Asset anyway.
- switch (asset.type())
- {
- case ASSET_TYPE_NATIVE:
- return 0;
- case ASSET_TYPE_CREDIT_ALPHANUM4:
- return getShortKey(asset.alphaNum4().issuer);
- case ASSET_TYPE_CREDIT_ALPHANUM12:
- return getShortKey(asset.alphaNum12().issuer);
- default:
- throw std::runtime_error("Invalid Asset type");
- }
-}
-
-uint8_t
-getShortKey(AssetCode const& code)
-{
- switch (code.type())
- {
- case ASSET_TYPE_NATIVE:
- return 0;
- case ASSET_TYPE_CREDIT_ALPHANUM4:
- return getShortKey(code.assetCode4());
- case ASSET_TYPE_CREDIT_ALPHANUM12:
- return getShortKey(code.assetCode12());
- default:
- throw std::runtime_error("Invalid AssetCode type");
- }
-}
-
-uint8_t
-getShortKey(ClaimableBalanceID const& balanceID)
-{
- return balanceID.v0()[0];
-}
-
-uint8_t
-getShortKey(LedgerKey const& key)
-{
- switch (key.type())
- {
- case ACCOUNT:
- return getShortKey(key.account().accountID);
- case OFFER:
- return getShortKey(key.offer().sellerID);
- case TRUSTLINE:
- return getShortKey(key.trustLine().accountID);
- case DATA:
- return getShortKey(key.data().accountID);
- case CLAIMABLE_BALANCE:
- return getShortKey(key.claimableBalance().balanceID);
- case LIQUIDITY_POOL:
- return getShortKey(key.liquidityPool().liquidityPoolID);
- case CONFIG_SETTING:
- return static_cast(key.configSetting().configSettingID);
- case CONTRACT_DATA:
- switch (key.contractData().contract.type())
- {
- case SC_ADDRESS_TYPE_ACCOUNT:
- return getShortKey(key.contractData().contract.accountId());
- case SC_ADDRESS_TYPE_CONTRACT:
- return key.contractData().contract.contractId().at(0);
- case SC_ADDRESS_TYPE_CLAIMABLE_BALANCE:
- return getShortKey(
- key.contractData().contract.claimableBalanceId());
- case SC_ADDRESS_TYPE_LIQUIDITY_POOL:
- return getShortKey(key.contractData().contract.liquidityPoolId());
- case SC_ADDRESS_TYPE_MUXED_ACCOUNT:
- return getShortKey(
- key.contractData().contract.muxedAccount().ed25519);
- }
- case CONTRACT_CODE:
- return key.contractCode().hash.at(0);
- case TTL:
- return getShortKey(key.ttl().keyHash);
- }
- throw std::runtime_error("Unknown key type");
-}
-
-// Sets "code" to a 4-byte alphanumeric AssetCode "Ast".
-void
-setAssetCode4(AssetCode4& code, int digit)
-{
- static_assert(
- FuzzUtils::NUMBER_OF_ASSETS_TO_USE <= 10,
- "asset code generation supports only single-digit asset numbers");
- assert(digit < FuzzUtils::NUMBER_OF_ASSETS_TO_USE);
- strToAssetCode(code, "Ast" + std::to_string(digit));
-}
-
-// For digit == 0, returns native Asset.
-// For digit != 0, returns an Asset with a 4-byte alphanumeric code "Ast"
-// and an issuer with the given public key.
-Asset
-makeAsset(int issuer, int digit)
-{
- Asset asset;
- if (digit == 0)
- {
- asset.type(ASSET_TYPE_NATIVE);
- }
- else
- {
- asset.type(ASSET_TYPE_CREDIT_ALPHANUM4);
- setAssetCode4(asset.alphaNum4().assetCode, digit);
- setShortKey(asset.alphaNum4().issuer, issuer);
- }
- return asset;
-}
-
-Asset
-makeAsset(uint8_t byte)
-{
- return makeAsset(decodeAssetIssuer(byte), decodeAssetCodeDigit(byte));
-}
-
-AssetCode
-makeAssetCode(uint8_t byte)
-{
- AssetCode code;
- auto digit = decodeAssetCodeDigit(byte);
- if (digit == 0)
- {
- code.type(ASSET_TYPE_NATIVE);
- }
- else
- {
- code.type(ASSET_TYPE_CREDIT_ALPHANUM4);
- setAssetCode4(code.assetCode4(), digit);
- }
- return code;
-}
-
-void
-generateStoredLedgerKeys(StoredLedgerKeys::iterator begin,
- StoredLedgerKeys::iterator end)
-{
- if (std::distance(begin, end) <= NUM_UNVALIDATED_LEDGER_KEYS)
- {
- throw std::runtime_error("No room for unvalidated ledger keys");
- }
-
- auto const firstUnvalidatedLedgerKey = end - NUM_UNVALIDATED_LEDGER_KEYS;
-
- // Generate valid ledger entry keys.
- std::generate(begin, firstUnvalidatedLedgerKey, []() {
- return LedgerEntryKey(LedgerTestUtils::generateValidLedgerEntry());
- });
-
- // Generate unvalidated ledger entry keys.
- std::generate(firstUnvalidatedLedgerKey, end, []() {
- size_t const entrySize = 3;
- return autocheck::generator()(entrySize);
- });
-}
-
-void
-setShortKey(std::array const& storedKeys,
- LedgerKey& key, uint8_t byte)
-{
- key = storedKeys[byte % NUM_STORED_LEDGER_KEYS];
-}
-
-void
-setShortKey(FuzzUtils::StoredPoolIDs const& storedPoolIDs, PoolID& key,
- uint8_t byte)
-{
- key = storedPoolIDs[byte % NUM_STORED_POOL_IDS];
-}
-
-SequenceNumber
-getSequenceNumber(AbstractLedgerTxn& ltx, PublicKey const& sourceAccountID)
-{
- auto account = loadAccount(ltx, sourceAccountID);
- return account.current().data.account().seqNum;
-}
-
-// Append "newOp" to "ops", optionally after enclosing it in a sandwich of
-// begin/end-sponsoring-future-reserves.
-void
-emplaceConditionallySponsored(xdr::xvector& ops,
- Operation const& newOp, bool isSponsored,
- int sponsorShortKey,
- PublicKey const& sponsoredKey)
-{
- if (isSponsored)
- {
- PublicKey sponsorKey;
- FuzzUtils::setShortKey(sponsorKey, sponsorShortKey);
-
- auto beginSponsoringOp =
- txtest::beginSponsoringFutureReserves(sponsoredKey);
- beginSponsoringOp.sourceAccount.activate() = toMuxedAccount(sponsorKey);
- ops.emplace_back(beginSponsoringOp);
- }
-
- ops.emplace_back(newOp);
-
- if (isSponsored)
- {
- auto endSponsoringOp = txtest::endSponsoringFutureReserves();
- endSponsoringOp.sourceAccount.activate() = toMuxedAccount(sponsoredKey);
- ops.emplace_back(endSponsoringOp);
- }
-}
-} // namespace
-} // namespace FuzzUtils
-}
-
-namespace xdr
-{
-/*
- the xdr_fuzzer_compactor/xdr_fuzzer_unpacker helper structs
- are based on xdr_get/xdr_put (marshallers for xdr) and make the following
- adjustments:
- * use a binary representation as compact as possible, so that fuzzers have
- less data to fuzz
- * shorten 64 and 32 bits values into respectively 16 and 8 bits
- * in particular, discriminant values are 8 bits instead of 32
- * shorten byte arrays
- * static arrays of size N bytes are shorten to 1 byte
- * non empty variable size arrays are shortened to 1 byte
- * remaps complex types
- * PublicKey is mapped to 8 bits
- * use the lowest overhead possible binary form
- * no alignment requirement
- * does not adjust endianness
- * implementation defined behavior (generation and fuzzing must be
- from the same build, on the same arch)
-*/
-struct xdr_fuzzer_compactor
-{
- std::uint8_t* const mStart;
- std::uint8_t* mCur;
- std::uint8_t* const mEnd;
-
- xdr_fuzzer_compactor(void* start, void* end)
- : mStart(reinterpret_cast(start))
- , mCur(reinterpret_cast(start))
- , mEnd(reinterpret_cast(end))
- {
- assert(mStart <= mEnd);
- }
- xdr_fuzzer_compactor(msg_ptr& m) : xdr_fuzzer_compactor(m->data(), m->end())
- {
- }
-
- void
- put_bytes(void const* buf, size_t len)
- {
- if (len != 0)
- {
- std::memcpy(mCur, buf, len);
- mCur += len;
- }
- }
-
- void
- check(std::size_t n) const
- {
- if (n > std::size_t(reinterpret_cast(mEnd) -
- reinterpret_cast(mCur)))
- throw xdr_overflow(
- "insufficient buffer space in xdr_fuzzer_compactor");
- }
-
- uint32_t
- size() const
- {
- auto s = std::size_t(reinterpret_cast(mCur) -
- reinterpret_cast(mStart));
- return static_cast(s);
- }
-
- template
- typename std::enable_if::uint_type>::value>::type
- operator()(T t)
- {
- // convert uint32 -> 1 byte
- check(1);
- auto v = xdr_traits::to_uint(t);
- uint8_t b = static_cast(v & 0xFF);
- put_bytes(&b, 1);
- }
-
- template
- typename std::enable_if::uint_type>::value>::type
- operator()(T t)
- {
- // convert uint64 -> 2 bytes
- check(2);
- uint16_t v = static_cast(xdr_traits::to_uint(t) & 0xFFFF);
- put_bytes(&v, 2);
- }
-
- template
- typename std::enable_if::is_bytes>::type
- operator()(T const& t)
- {
- // convert array -> 0/1 byte
- uint8_t s2 = t.empty() ? 0 : 1;
- if (xdr_traits::variable_nelem)
- {
- check(1 + s2);
- put_bytes(&s2, 1);
- }
- else
- {
- check(s2);
- }
- put_bytes(t.data(), s2);
- }
-
- template
- typename std::enable_if<
- (!std::is_same::value &&
- !std::is_same::value &&
- !std::is_same::value &&
- !std::is_same::value &&
- !std::is_same::value &&
- !std::is_same::value) &&
- (xdr_traits::is_class || xdr_traits::is_container)>::type
- operator()(T const& t)
- {
- xdr_traits::save(*this, t);
- }
-
- template
- typename std::enable_if::value>::type
- operator()(T const& pk)
- {
- // convert public key 1 byte
- check(1);
- auto b = stellar::FuzzUtils::getShortKey(pk.ed25519());
- put_bytes(&b, 1);
- }
- template
- typename std::enable_if::value>::type
- operator()(T const& m)
- {
- // convert MuxedAccount -> 1 byte (same than an AccountID)
- auto const& ed25519 = (m.type() == stellar::KEY_TYPE_ED25519)
- ? m.ed25519()
- : m.med25519().ed25519;
- check(1);
- auto b = stellar::FuzzUtils::getShortKey(ed25519);
- put_bytes(&b, 1);
- }
-
- template
- typename std::enable_if::value>::type
- operator()(T const& asset)
- {
- // Convert Asset to 1 byte.
- check(1);
- auto b = stellar::FuzzUtils::getShortKey(asset);
- put_bytes(&b, 1);
- }
-
- template
- typename std::enable_if::value>::type
- operator()(T const& code)
- {
- // Convert AssetCode to 1 byte.
- check(1);
- auto b = stellar::FuzzUtils::getShortKey(code);
- put_bytes(&b, 1);
- }
-
- template
- typename std::enable_if<
- std::is_same::value>::type
- operator()(T const& balanceID)
- {
- // Convert ClaimableBalanceID to 1 byte for indexing into an array of
- // LedgerKeys that have been mentioned in the XDR of fuzzer operations.
- check(1);
- auto b = stellar::FuzzUtils::getShortKey(balanceID);
- put_bytes(&b, 1);
- }
-
- template
- typename std::enable_if::value>::type
- operator()(T const& key)
- {
- // Convert LedgerKey to 1 byte for indexing into an array of LedgerKeys
- // that have been mentioned in the XDR of fuzzer operations.
- check(1);
- auto b = stellar::FuzzUtils::getShortKey(key);
- put_bytes(&b, 1);
- }
-};
-
-template
-static opaque_vec<>
-xdr_to_fuzzer_opaque(Args const&... args)
-{
- opaque_vec<> m(opaque_vec<>::size_type{xdr_argpack_size(args...)});
- xdr_fuzzer_compactor p(m.data(), m.data() + m.size());
- xdr_argpack_archive(p, args...);
- m.resize(p.size());
- return m;
-}
-
-struct xdr_fuzzer_unpacker
-{
- stellar::FuzzUtils::StoredLedgerKeys mStoredLedgerKeys;
- stellar::FuzzUtils::StoredPoolIDs mStoredPoolIDs;
- std::uint8_t const* mCur;
- std::uint8_t const* const mEnd;
-
- xdr_fuzzer_unpacker(
- stellar::FuzzUtils::StoredLedgerKeys const& storedLedgerKeys,
- stellar::FuzzUtils::StoredPoolIDs const& storedPoolIDs,
- void const* start, void const* end)
- : mStoredLedgerKeys(storedLedgerKeys)
- , mStoredPoolIDs(storedPoolIDs)
- , mCur(reinterpret_cast(start))
- , mEnd(reinterpret_cast(end))
- {
- assert(mCur <= mEnd);
- }
- xdr_fuzzer_unpacker(
- stellar::FuzzUtils::StoredLedgerKeys const& storedLedgerKeys,
- stellar::FuzzUtils::StoredPoolIDs const& storedPoolIDs,
- msg_ptr const& m)
- : xdr_fuzzer_unpacker(storedLedgerKeys, storedPoolIDs, m->data(),
- m->end())
- {
- }
-
- void
- get_bytes(void* buf, size_t len)
- {
- if (len != 0)
- {
- std::memcpy(buf, mCur, len);
- mCur += len;
- }
- }
-
- uint8_t
- get_byte()
- {
- uint8_t b;
- get_bytes(&b, 1);
- return b;
- }
-
- void
- check(std::size_t n) const
- {
- if (n > std::size_t(reinterpret_cast(mCur) -
- reinterpret_cast(mEnd)))
- throw xdr_overflow(
- "insufficient buffer space in xdr_fuzzer_unpacker");
- }
-
- template
- T
- get32()
- {
- // 1 byte --> uint32
- check(1);
- uint32_t w = get_byte();
- if (w == UINT8_MAX)
- {
- return std::numeric_limits::max();
- }
- else if (w == UINT8_MAX - 1)
- {
- auto maxT = std::numeric_limits::max();
- return xdr_traits::from_uint(maxT - 1);
- }
-
- return xdr_traits::from_uint(w);
- }
-
- template
- T
- get64()
- {
- // 2 bytes --> uint64 **with** "sign extension"
- check(2);
- // load into a 16 signed
- int16_t w;
- get_bytes(&w, 2);
- // extend to 64 bit
- int64_t ww = w;
- if (ww == INT16_MAX)
- {
- return std::numeric_limits::max();
- }
- else if (ww == INT16_MAX - 1)
- {
- return std::numeric_limits::max() - 1;
- }
-
- return xdr_traits::from_uint(ww);
- }
-
- template
- typename std::enable_if::uint_type>::value>::type
- operator()(T& t)
- {
- t = get32();
- }
-
- template
- typename std::enable_if::uint_type>::value>::type
- operator()(T& t)
- {
- t = get64();
- }
-
- template
- typename std::enable_if::is_bytes>::type
- operator()(T& t)
- {
- std::uint32_t s2 = 0;
- if (xdr_traits::variable_nelem)
- {
- check(1);
- s2 = get_byte();
- check(s2);
- // only accept small vectors
- if (s2 > 1)
- {
- throw xdr_overflow("large vector in xdr_fuzzer_unpacker");
- }
- t.resize(s2);
- }
- else
- {
- if (!t.empty())
- {
- s2 = 1;
- }
- check(s2);
- }
- get_bytes(t.data(), s2);
- }
-
- template
- typename std::enable_if<
- (!std::is_same::value &&
- !std::is_same::value &&
- !std::is_same::value &&
- !std::is_same::value &&
- !std::is_same::value &&
- !std::is_same::value &&
- !std::is_same::value &&
- !std::is_same::value) &&
- (xdr_traits::is_class || xdr_traits::is_container)>::type
- operator()(T& t)
- {
- xdr_traits::load(*this, t);
- }
-
- template
- typename std::enable_if::value>::type
- operator()(T& pk)
- {
- // 1 byte --> AccountID
- check(1);
- std::uint8_t v = get_byte();
- stellar::FuzzUtils::setShortKey(pk, v);
- }
- template
- typename std::enable_if::value>::type
- operator()(T& m)
- {
- // convert 1 byte --> MuxedAccount (regular AccountID)
- check(1);
- std::uint8_t v = get_byte();
- stellar::FuzzUtils::setShortKey(m.ed25519(), v);
- }
-
- template
- typename std::enable_if::value>::type
- operator()(T& asset)
- {
- // 1 byte --> Asset
- check(1);
- std::uint8_t v = get_byte();
- asset = stellar::FuzzUtils::makeAsset(v);
- }
-
- template
- typename std::enable_if::value>::type
- operator()(T& code)
- {
- // 1 byte --> AssetCode
- check(1);
- std::uint8_t v = get_byte();
- code = stellar::FuzzUtils::makeAssetCode(v);
- }
-
- template
- typename std::enable_if<
- std::is_same::value>::type
- operator()(T& balanceID)
- {
- check(1);
- std::uint8_t v = get_byte();
- stellar::LedgerKey key;
- stellar::FuzzUtils::setShortKey(mStoredLedgerKeys, key, v);
- // If this one byte indexes a stored LedgerKey for a ClaimableBalanceID,
- // use that; otherwise just use the byte itself as the balance ID.
- if (key.type() == stellar::CLAIMABLE_BALANCE)
- {
- balanceID = key.claimableBalance().balanceID;
- }
- else
- {
- balanceID.type(stellar::CLAIMABLE_BALANCE_ID_TYPE_V0);
- balanceID.v0()[0] = v;
- }
- }
-
- // PoolID is just an opaque vector of size 32, so we have to specialize the
- // deposit and withdraw ops instead
- template
- typename std::enable_if<
- std::is_same::value>::type
- operator()(T& depositOp)
- {
- check(1);
- auto v = get_byte();
- stellar::FuzzUtils::setShortKey(mStoredPoolIDs,
- depositOp.liquidityPoolID, v);
-
- depositOp.maxAmountA = get64();
- depositOp.maxAmountB = get64();
-
- auto minN = get32();
- auto minD = get32();
- auto maxN = get32();
- auto maxD = get32();
-
- depositOp.minPrice = stellar::Price{minN, minD};
- depositOp.maxPrice = stellar::Price{maxN, maxD};
- }
-
- template
- typename std::enable_if<
- std::is_same::value>::type
- operator()(T& withdrawOp)
- {
- check(1);
- auto v = get_byte();
- stellar::FuzzUtils::setShortKey(mStoredPoolIDs,
- withdrawOp.liquidityPoolID, v);
-
- withdrawOp.amount = get64();
- withdrawOp.minAmountA = get64();
- withdrawOp.minAmountB = get64();
- }
-
- template
- typename std::enable_if::value>::type
- operator()(T& key)
- {
- check(1);
- std::uint8_t v = get_byte();
- stellar::FuzzUtils::setShortKey(mStoredLedgerKeys, key, v);
- }
-
- void
- done()
- {
- if (mCur != mEnd)
- {
- throw xdr_bad_message_size("trailing data in xdr_fuzzer_unpacker");
- }
- }
-};
-
-template
-static auto
-xdr_from_fuzzer_opaque(
- stellar::FuzzUtils::StoredLedgerKeys const& storedLedgerKeys,
- stellar::FuzzUtils::StoredPoolIDs const& storedPoolIDs, Bytes const& m,
- Args&... args) -> decltype(detail::bytes_to_void(m))
-{
- xdr_fuzzer_unpacker g(storedLedgerKeys, storedPoolIDs, m.data(),
- m.data() + m.size());
- xdr_argpack_archive(g, args...);
- g.done();
-}
-
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-template <>
-void
-generator_t::operator()(stellar::PublicKey& t) const
-{
- // Generate account IDs in a somewhat larger range than the number of
- // accounts created during setup, so that the fuzzer can generate some
- // unused accounts (and also generate operation sequences in which it
- // creates a new account and then uses it in a later operation).
- uint8_t constexpr NUMBER_OF_ACCOUNT_IDS_TO_GENERATE = 32;
- static_assert(NUMBER_OF_ACCOUNT_IDS_TO_GENERATE >
- stellar::FuzzUtils::NUMBER_OF_PREGENERATED_ACCOUNTS,
- "Range of generated accounts too small");
- stellar::FuzzUtils::setShortKey(
- t.ed25519(), static_cast(stellar::rand_uniform(
- 0, NUMBER_OF_ACCOUNT_IDS_TO_GENERATE - 1)));
-}
-
-static int RECURSION_COUNT = 0;
-static int const RECURSION_LIMIT = 50;
-
-template <>
-void
-generator_t::operator()(stellar::SCVal& val) const
-{
- if (++RECURSION_COUNT > RECURSION_LIMIT)
- {
- stellar::SCVal v;
- val = v;
- return;
- }
- auto const& vals = stellar::SCVal::_xdr_case_values();
- stellar::SCValType v;
-
- uint32_t n = 0;
- (*this)(n);
- v = vals[n % vals.size()];
-
- val._xdr_discriminant(v, false);
- val._xdr_with_mem_ptr(field_archiver, v, *this, val, nullptr);
-}
-
-template <>
-void
-generator_t::operator()(
- stellar::SorobanAuthorizedInvocation& auth) const
-{
- if (++RECURSION_COUNT > RECURSION_LIMIT)
- {
- stellar::SorobanAuthorizedInvocation a;
- auth = a;
- return;
- }
-
- xdr_traits::load(*this, auth);
-}
-
-#endif // FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-}
-
-namespace stellar
-{
-// creates a generic configuration with settings rigged to maximize
-// determinism
-static Config
-getFuzzConfig(int instanceNumber)
-{
- Config cfg = getTestConfig(instanceNumber);
- cfg.MANUAL_CLOSE = true;
- cfg.CATCHUP_COMPLETE = false;
- cfg.CATCHUP_RECENT = 0;
- cfg.ARTIFICIALLY_GENERATE_LOAD_FOR_TESTING = false;
- cfg.ARTIFICIALLY_SET_CLOSE_TIME_FOR_TESTING = UINT32_MAX;
- cfg.HTTP_PORT = 0;
- cfg.WORKER_THREADS = 2;
- cfg.QUORUM_INTERSECTION_CHECKER = false;
- cfg.PREFERRED_PEERS_ONLY = false;
- cfg.RUN_STANDALONE = true;
- cfg.TESTING_UPGRADE_DESIRED_FEE = FuzzUtils::FUZZING_FEE;
- cfg.TESTING_UPGRADE_RESERVE = FuzzUtils::FUZZING_RESERVE;
-
- return cfg;
-}
-
-static void
-resetTxInternalState(Application& app)
-{
- reinitializeAllGlobalStateWithSeed(1);
-// reset caches to clear persistent state
-#ifdef BUILD_TESTS
- app.getLedgerTxnRoot().resetForFuzzer();
- app.getInvariantManager().resetForFuzzer();
-#endif // BUILD_TESTS
-}
-
-// FuzzTransactionFrame is a specialized TransactionFrame that includes
-// useful methods for fuzzing such as an attemptApplication method for resetting
-// ledger state and deterministically attempting application of transactions.
-class FuzzTransactionFrame : public TransactionFrame
-{
- private:
- MutableTxResultPtr mTxResult;
-
- public:
- FuzzTransactionFrame(Hash const& networkID,
- TransactionEnvelope const& envelope)
- : TransactionFrame(networkID, envelope)
- , mTxResult(MutableTransactionResult::createSuccess(*this, 0)) {};
-
- void
- attemptApplication(Application& app, AbstractLedgerTxn& ltx)
- {
- // No soroban ops allowed
- if (std::any_of(getOperations().begin(), getOperations().end(),
- [](auto const& x) { return x->isSoroban(); }))
- {
- mTxResult->setError(txFAILED);
- return;
- }
-
- // reset results of operations
- mTxResult = MutableTransactionResult::createSuccess(*this, 0);
-
- // attempt application of transaction without processing the fee or
- // committing the LedgerTxn
- SignatureChecker signatureChecker{
- ltx.loadHeader().current().ledgerVersion, getContentsHash(),
- mEnvelope.v1().signatures};
- // Do not track metrics related to background signature verification in
- // the fuzzer.
- signatureChecker.disableCacheMetricsTracking();
- LedgerSnapshot ltxStmt(ltx);
- // if any ill-formed Operations, do not attempt transaction application
- auto isInvalidOperation = [&](auto const& op, auto& opResult) {
- auto diagnostics =
- DiagnosticEventManager::createForValidation(app.getConfig());
- return !op->checkValid(
- app.getAppConnector(), signatureChecker,
- &app.getAppConnector().getLastClosedSorobanNetworkConfig(),
- ltxStmt, false, opResult, diagnostics);
- };
-
- auto const& ops = getOperations();
- for (size_t i = 0; i < ops.size(); ++i)
- {
- auto const& op = ops[i];
- auto& opResult = mTxResult->getOpResultAt(i);
- if (isInvalidOperation(op, opResult))
- {
- mTxResult->setError(txFAILED);
- return;
- }
- }
-
- // while the following method's result is not captured, regardless, for
- // protocols < 8, this triggered buggy caching, and potentially may do
- // so in the future
- loadSourceAccount(ltx, ltx.loadHeader());
- processSeqNum(ltx);
- TransactionMetaBuilder tm(true, *this,
- ltx.loadHeader().current().ledgerVersion,
- app.getAppConnector());
- std::optional sorobanNetworkConfig;
- Hash sorobanRngSeed;
- applyOperations(signatureChecker, app.getAppConnector(), ltx, tm,
- *mTxResult, sorobanNetworkConfig, sorobanRngSeed);
- if (mTxResult->getResultCode() == txINTERNAL_ERROR)
- {
- throw std::runtime_error("Internal error while fuzzing");
- }
- }
-
- TransactionResult const&
- getResult() const
- {
- return mTxResult->getXDR();
- }
-
- TransactionResultCode
- getResultCode() const
- {
- return mTxResult->getResultCode();
- }
-};
-
-namespace
-{
-std::shared_ptr
-createFuzzTransactionFrame(AbstractLedgerTxn& ltx,
- PublicKey const& sourceAccountID,
- std::vector::const_iterator begin,
- std::vector::const_iterator end,
- Hash const& networkID)
-{
- // construct a transaction envelope, which, for each transaction
- // application in the fuzzer, is the exact same, except for the inner
- // operations of course
- auto txEnv = TransactionEnvelope{};
- txEnv.type(ENVELOPE_TYPE_TX);
- auto& tx1 = txEnv.v1();
- tx1.tx.sourceAccount = toMuxedAccount(sourceAccountID);
- tx1.tx.fee = 0;
- tx1.tx.seqNum = FuzzUtils::getSequenceNumber(ltx, sourceAccountID) + 1;
- std::copy(begin, end, std::back_inserter(tx1.tx.operations));
-
- std::shared_ptr res =
- std::make_shared(networkID, txEnv);
- return res;
-}
-
-bool
-isBadOverlayFuzzerInput(StellarMessage const& m)
-{
- // HELLO, AUTH and ERROR_MSG messages cause the connection between
- // the peers to drop. Since peer connections are only established
- // preceding the persistent loop, a dropped peer is not only
- // inconvenient, it also confuses the fuzzer. Consider a msg A sent
- // before a peer is dropped and after a peer is dropped. The two,
- // even though the same message, will take drastically different
- // execution paths -- the fuzzer's main metric for determinism
- // (stability) and binary coverage.
- return m.type() == AUTH || m.type() == ERROR_MSG || m.type() == HELLO;
-}
-
-// Empties "ops" as operations are applied. Throws if any operations fail.
-// Handles breaking up the list of operations into multiple transactions, if the
-// caller provides more operations than fit in a single transaction.
-void
-applySetupOperations(LedgerTxn& ltx, PublicKey const& sourceAccount,
- xdr::xvector::const_iterator begin,
- xdr::xvector::const_iterator end,
- Application& app)
-{
- while (begin != end)
- {
- auto endOpsInThisTx = std::distance(begin, end) <= MAX_OPS_PER_TX
- ? end
- : begin + MAX_OPS_PER_TX;
- auto txFramePtr = createFuzzTransactionFrame(
- ltx, sourceAccount, begin, endOpsInThisTx, app.getNetworkID());
- txFramePtr->attemptApplication(app, ltx);
- begin = endOpsInThisTx;
-
- if (txFramePtr->getResultCode() != txSUCCESS)
- {
- auto const msg =
- fmt::format(FMT_STRING("Error {} while setting up fuzzing -- "
- "{}"),
- txFramePtr->getResultCode(),
- xdrToCerealString(txFramePtr->getResult(),
- "TransactionResult"));
- LOG_FATAL(DEFAULT_LOG, "{}", msg);
- throw std::runtime_error(msg);
- }
-
- auto const& ops = txFramePtr->getOperations();
- for (size_t i = 0; i < ops.size(); ++i)
- {
- auto const& opFrame = ops.at(i);
- auto& opResult = txFramePtr->getResult().result.results().at(i);
-
- auto const& op = opFrame->getOperation();
- auto const& tr = opResult.tr();
- auto const opType = op.body.type();
-
- if ((opType == MANAGE_BUY_OFFER &&
- tr.manageBuyOfferResult().success().offer.effect() !=
- MANAGE_OFFER_CREATED) ||
- (opType == MANAGE_SELL_OFFER &&
- tr.manageSellOfferResult().success().offer.effect() !=
- MANAGE_OFFER_CREATED) ||
- (opType == CREATE_PASSIVE_SELL_OFFER &&
- tr.createPassiveSellOfferResult().success().offer.effect() !=
- MANAGE_OFFER_CREATED))
- {
- auto const msg = fmt::format(
- FMT_STRING("Manage offer result {} while setting "
- "up fuzzing -- {}"),
- xdrToCerealString(tr, "Operation"),
- xdrToCerealString(op, "Operation"));
- LOG_FATAL(DEFAULT_LOG, "{}", msg);
- throw std::runtime_error(msg);
- }
- }
- }
-}
-
-// Requires a set of operations small enough to fit in a single transaction.
-// Tolerates the failure of transaction application.
-void
-applyFuzzOperations(LedgerTxn& ltx, PublicKey const& sourceAccount,
- xdr::xvector::const_iterator begin,
- xdr::xvector::const_iterator end,
- Application& app)
-{
- auto txFramePtr = createFuzzTransactionFrame(ltx, sourceAccount, begin, end,
- app.getNetworkID());
- txFramePtr->attemptApplication(app, ltx);
-}
-} // namespace
-
-// Unlike Asset, this can be a constexpr.
-struct AssetID
-{
- constexpr AssetID() : mIsNative(true), mIssuer(0), mSuffixDigit(0)
- {
- }
-
- constexpr AssetID(int id) : AssetID(id, id)
- {
- }
-
- constexpr AssetID(int issuer, int digit)
- : mIsNative(false), mIssuer(issuer), mSuffixDigit(digit)
- {
- assert(mSuffixDigit != 0); // id 0 is for native asset
- assert(mSuffixDigit < FuzzUtils::NUMBER_OF_ASSETS_TO_USE);
- }
-
- Asset
- toAsset() const
- {
- return mIsNative ? txtest::makeNativeAsset()
- : FuzzUtils::makeAsset(mIssuer, mSuffixDigit);
- }
-
- bool const mIsNative;
- int const mIssuer; // non-zero only if !isNative
- int const mSuffixDigit; // non-zero only if !isNative
-};
-
-struct SponsoredEntryParameters
-{
- constexpr SponsoredEntryParameters() : SponsoredEntryParameters(false, 0)
- {
- }
-
- constexpr SponsoredEntryParameters(int sponsorKey)
- : SponsoredEntryParameters(true, sponsorKey)
- {
- }
-
- bool const mSponsored;
- int const mSponsorKey; // meaningful only if mSponsored is true
-
- private:
- constexpr SponsoredEntryParameters(bool sponsored, int sponsorKey)
- : mSponsored(sponsored), mSponsorKey(sponsorKey)
- {
- }
-};
-
-struct AccountParameters : public SponsoredEntryParameters
-{
- constexpr AccountParameters(int shortKey,
- int64_t nativeAssetAvailableForTestActivity,
- uint32_t optionFlags)
- : SponsoredEntryParameters()
- , mShortKey(shortKey)
- , mNativeAssetAvailableForTestActivity(
- nativeAssetAvailableForTestActivity)
- , mOptionFlags(optionFlags)
- {
- }
-
- constexpr AccountParameters(int shortKey,
- int64_t nativeAssetAvailableForTestActivity,
- uint32_t optionFlags, int sponsorKey)
- : SponsoredEntryParameters(sponsorKey)
- , mShortKey(shortKey)
- , mNativeAssetAvailableForTestActivity(
- nativeAssetAvailableForTestActivity)
- , mOptionFlags(optionFlags)
- {
- }
-
- int const mShortKey;
- int64_t const mNativeAssetAvailableForTestActivity;
- uint32_t const mOptionFlags;
-};
-
-/*
-Scenarios we are testing with the account, trustline, claimable balance, and
-offer configurations below -
-1. All possible account flags, along with issued assets.
-2. Hitting limits due to buying liabilites for both native and non-native
- balances.
-3. Claimable balances with claimants in all possible auth states and missing
- trustline.
-4. Claimable balances with sponsor and issuer as the claimaint.
-5. Order books for native to non-native, and non-native to non-native.
-6. Offers created by the issuer.
-7. Entries with sponsorships.
-*/
-
-std::array<
- AccountParameters,
- FuzzUtils::NUMBER_OF_PREGENERATED_ACCOUNTS> constexpr accountParameters{
- {// This account will have all of it's entries sponsored, and buying
- // liabilities close to INT64_MAX
- {0, 0, 0},
- {1, 256, AUTH_REVOCABLE_FLAG | AUTH_CLAWBACK_ENABLED_FLAG},
- // sponsored by account 1 and AUTH_REVOCABLE so we can put a trustline
- // into the AUTHORIZED_TO_MAINTAIN_LIABILITIES state
- {2, 256, AUTH_REVOCABLE_FLAG, 1},
- {3, 256, AUTH_REQUIRED_FLAG},
- {4, 256, AUTH_IMMUTABLE_FLAG}}};
-
-struct TrustLineParameters : public SponsoredEntryParameters
-{
- constexpr TrustLineParameters(int trustor, AssetID const& assetID,
- int64_t assetAvailableForTestActivity,
- int64_t spareLimitAfterSetup)
- : TrustLineParameters(trustor, assetID, assetAvailableForTestActivity,
- spareLimitAfterSetup, false, 0)
- {
- assert(!mAssetID.mIsNative);
- }
-
- static TrustLineParameters constexpr withAllowTrust(
- int trustor, AssetID const& assetID,
- int64_t assetAvailableForTestActivity, int64_t spareLimitAfterSetup,
- uint32_t allowTrustFlags)
- {
- return TrustLineParameters(trustor, assetID,
- assetAvailableForTestActivity,
- spareLimitAfterSetup, true, allowTrustFlags);
- }
-
- static TrustLineParameters constexpr withSponsor(
- int trustor, AssetID const& assetID,
- int64_t assetAvailableForTestActivity, int64_t spareLimitAfterSetup,
- int sponsorKey)
- {
- return TrustLineParameters(trustor, assetID,
- assetAvailableForTestActivity,
- spareLimitAfterSetup, false, 0, sponsorKey);
- }
-
- static TrustLineParameters constexpr withAllowTrustAndSponsor(
- int trustor, AssetID const& assetID,
- int64_t assetAvailableForTestActivity, int64_t spareLimitAfterSetup,
- uint32_t allowTrustFlags, int sponsorKey)
- {
- return TrustLineParameters(
- trustor, assetID, assetAvailableForTestActivity,
- spareLimitAfterSetup, true, allowTrustFlags, sponsorKey);
- }
-
- int const mTrustor;
- AssetID const mAssetID;
- int64_t const mAssetAvailableForTestActivity;
- int64_t const mSpareLimitAfterSetup;
- bool const mCallAllowTrustOp;
- uint32_t const mAllowTrustFlags;
-
- private:
- constexpr TrustLineParameters(int const trustor, AssetID const& assetID,
- int64_t assetAvailableForTestActivity,
- int64_t spareLimitAfterSetup,
- bool callAllowTrustOp,
- uint32_t allowTrustFlags)
- : SponsoredEntryParameters()
- , mTrustor(trustor)
- , mAssetID(assetID)
- , mAssetAvailableForTestActivity(assetAvailableForTestActivity)
- , mSpareLimitAfterSetup(spareLimitAfterSetup)
- , mCallAllowTrustOp(callAllowTrustOp)
- , mAllowTrustFlags(allowTrustFlags)
- {
- assert(!mAssetID.mIsNative);
- }
-
- constexpr TrustLineParameters(int const trustor, AssetID const& assetID,
- int64_t assetAvailableForTestActivity,
- int64_t spareLimitAfterSetup,
- bool callAllowTrustOp,
- uint32_t allowTrustFlags, int sponsorKey)
- : SponsoredEntryParameters(sponsorKey)
- , mTrustor(trustor)
- , mAssetID(assetID)
- , mAssetAvailableForTestActivity(assetAvailableForTestActivity)
- , mSpareLimitAfterSetup(spareLimitAfterSetup)
- , mCallAllowTrustOp(callAllowTrustOp)
- , mAllowTrustFlags(allowTrustFlags)
- {
- assert(!mAssetID.mIsNative);
- }
-};
-
-std::array constexpr trustLineParameters{
- {// this trustline will be used to increase native buying liabilites
- TrustLineParameters::withSponsor(0, AssetID(4), INT64_MAX, 0, 2),
-
- // these trustlines are required for offers
- {2, AssetID(1), 256, 256},
- {3, AssetID(1), 256, 0}, // No available limit left
- {4, AssetID(1), 256, 256},
-
- {1, AssetID(2), 256, 256},
- {3, AssetID(2), 256, 256},
- {4, AssetID(2), 256, 0}, // No available limit left
-
- // these 5 trustlines are required for claimable balances
- {2, AssetID(4), 256, 256},
- {3, AssetID(4), INT64_MAX, 0},
- TrustLineParameters::withAllowTrust(4, AssetID(3), INT64_MAX, 0,
- AUTHORIZED_FLAG),
-
- // deauthorize trustline
- TrustLineParameters::withAllowTrustAndSponsor(0, AssetID(1), 0, 256, 0, 1),
-
- TrustLineParameters::withAllowTrustAndSponsor(
- 0, AssetID(2), 0, 256, AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG, 1)
-
- }};
-
-struct ClaimableBalanceParameters : public SponsoredEntryParameters
-{
- constexpr ClaimableBalanceParameters(int const sender, int const claimant,
- AssetID const& asset, int64_t amount)
- : SponsoredEntryParameters()
- , mSender(sender)
- , mClaimant(claimant)
- , mAsset(asset)
- , mAmount(amount)
- {
- }
-
- constexpr ClaimableBalanceParameters(int const sender, int const claimant,
- AssetID const& asset, int64_t amount,
- int sponsorKey)
- : SponsoredEntryParameters(sponsorKey)
- , mSender(sender)
- , mClaimant(claimant)
- , mAsset(asset)
- , mAmount(amount)
- {
- }
-
- int const mSender;
- int const mClaimant;
- AssetID const mAsset;
- int64_t const mAmount;
-};
-
-std::array constexpr claimableBalanceParameters{
- {{1, 2, AssetID(), 10}, // native asset
- {2, 3, AssetID(4), 5}, // non-native asset
- {4, 2, AssetID(4), 20, 2}, // sponsored by account 2
- {4, 3, AssetID(3), 30}, // issuer is claimant
- {1, 3, AssetID(1), 100}, // 3 has no available limit
- {1, 0, AssetID(2),
- 1}, // claimant trustline is AUTHORIZED_TO_MAINTAIN_LIABILITIES
- {2, 0, AssetID(), 100000}, // 0 does not have enough native limit
-
- // leave 0 with a small native balance so it can create a native buy
- // offer for INT64_MAX - balance
- {0, 1, AssetID(),
- FuzzUtils::INITIAL_ACCOUNT_BALANCE -
- (FuzzUtils::MIN_ACCOUNT_BALANCE + (2 * FuzzUtils::FUZZING_RESERVE) +
- 1),
- 2},
-
- {3, 0, AssetID(3), 30}, // 0 has no trustline to this asset
- {3, 0, AssetID(1), 30}, // claimant trustline is not authorized
- // enough limit to claim. trustline is clawback enabled
- {1, 2, AssetID(1), 100}}};
-
-struct OfferParameters : public SponsoredEntryParameters
-{
- constexpr OfferParameters(int publicKey, AssetID const& bid,
- AssetID const& sell, int64_t amount,
- int32_t priceNumerator, int32_t priceDenominator,
- bool passive)
- : SponsoredEntryParameters()
- , mPublicKey(publicKey)
- , mBid(bid)
- , mSell(sell)
- , mAmount(amount)
- , mNumerator(priceNumerator)
- , mDenominator(priceDenominator)
- , mPassive(passive)
- {
- }
-
- constexpr OfferParameters(int publicKey, AssetID const& bid,
- AssetID const& sell, int64_t amount,
- int32_t priceNumerator, int32_t priceDenominator,
- bool passive, int sponsorKey)
- : SponsoredEntryParameters(sponsorKey)
- , mPublicKey(publicKey)
- , mBid(bid)
- , mSell(sell)
- , mAmount(amount)
- , mNumerator(priceNumerator)
- , mDenominator(priceDenominator)
- , mPassive(passive)
- {
- }
-
- int const mPublicKey;
- AssetID const mBid;
- AssetID const mSell;
- int64_t const mAmount;
- int32_t const mNumerator;
- int32_t const mDenominator;
- bool const mPassive;
-};
-
-std::array constexpr orderBookParameters{{
-
- // The first two order books follow this structure
- // +------------+-----+------+--------+------------------------------+
- // | Account | Bid | Sell | Amount | Price (in terms of Sell/Bid) |
- // +------------+-----+------+--------+------------------------------+
- // | non-issuer | A | B | 10 | 3/2 |
- // | issuer | A | B | 50 | 3/2 |
- // | non-issuer | A | B | 100 | 1/1 (passive) |
- // | non-issuer | B | A | 100 | 1/1 (passive) |
- // | issuer | B | A | 10 | 10/9 |
- // | non-issuer | B | A | 50 | 10/9 |
- // | non-issuer | B | A | 100 | 22/7 |
- // +------------+-----+------+--------+------------------------------+
-
- // This is a simple order book between a native and non-native asset
- {2, AssetID(), AssetID(1), 10, 3, 2, false},
- {1, AssetID(), AssetID(1), 50, 3, 2, false, 3}, // sponsored by account 3
- {3, AssetID(), AssetID(1), 100, 1, 1, true},
- {3, AssetID(1), AssetID(), 100, 1, 1, true},
- {1, AssetID(1), AssetID(), 10, 10, 9, false},
- {2, AssetID(1), AssetID(), 50, 10, 9, false},
- {2, AssetID(1), AssetID(), 100, 22, 7, false},
-
- // This is a simple order book between two non-native assets
- {3, AssetID(1), AssetID(2), 10, 3, 2, false},
- {1, AssetID(1), AssetID(2), 50, 3, 2, false, 3}, // sponsored by account 3
- {3, AssetID(1), AssetID(2), 100, 1, 1, true},
- {3, AssetID(2), AssetID(1), 100, 1, 1, true},
- {1, AssetID(2), AssetID(1), 10, 10, 9, false},
- {3, AssetID(2), AssetID(1), 50, 10, 9, false},
- {3, AssetID(2), AssetID(1), 100, 22, 7, false},
-
- {4, AssetID(4), AssetID(3), INT64_MAX - 50, 1, 1, false},
-
- // offer to trade all of one asset to another up to the trustline limit
- {4, AssetID(2), AssetID(), 256, 1, 1, true},
-
- // Increase native buying liabilites for account 0
- {0, AssetID(), AssetID(4),
- INT64_MAX - (FuzzUtils::MIN_ACCOUNT_BALANCE +
- (2 * FuzzUtils::FUZZING_RESERVE) + 1),
- 1, 1, false, 2}}};
-
-struct PoolSetupParameters : public SponsoredEntryParameters
-{
- constexpr PoolSetupParameters(int trustor, AssetID const& assetA,
- AssetID const& assetB, int64_t maxAmountA,
- int64_t maxAmountB, int32_t minPriceNumerator,
- int32_t minPriceDenominator,
- int32_t maxPriceNumerator,
- int32_t maxPriceDenominator, int64_t limit)
- : SponsoredEntryParameters()
- , mTrustor(trustor)
- , mAssetA(assetA)
- , mAssetB(assetB)
- , mMaxAmountA(maxAmountA)
- , mMaxAmountB(maxAmountB)
- , mMinPriceNumerator(minPriceNumerator)
- , mMinPriceDenominator(minPriceDenominator)
- , mMaxPriceNumerator(maxPriceNumerator)
- , mMaxPriceDenominator(maxPriceDenominator)
- , mLimit(limit)
- {
- }
-
- constexpr PoolSetupParameters(int trustor, AssetID const& assetA,
- AssetID const& assetB, int64_t maxAmountA,
- int64_t maxAmountB, int32_t minPriceNumerator,
- int32_t minPriceDenominator,
- int32_t maxPriceNumerator,
- int32_t maxPriceDenominator, int64_t limit,
- int sponsorKey)
- : SponsoredEntryParameters(sponsorKey)
- , mTrustor(trustor)
- , mAssetA(assetA)
- , mAssetB(assetB)
- , mMaxAmountA(maxAmountA)
- , mMaxAmountB(maxAmountB)
- , mMinPriceNumerator(minPriceNumerator)
- , mMinPriceDenominator(minPriceDenominator)
- , mMaxPriceNumerator(maxPriceNumerator)
- , mMaxPriceDenominator(maxPriceDenominator)
- , mLimit(limit)
- {
- }
-
- int const mTrustor;
- AssetID const mAssetA;
- AssetID const mAssetB;
- int64_t const mMaxAmountA;
- int64_t const mMaxAmountB;
- int32_t const mMinPriceNumerator;
- int32_t const mMinPriceDenominator;
- int32_t const mMaxPriceNumerator;
- int32_t const mMaxPriceDenominator;
- int64_t const mLimit;
-};
-
-// NUM_STORED_POOL_IDS - 1 because we will push in a hash for a pool that
-// doesn't exist into mStoredPoolIDs later
-std::array constexpr poolSetupParameters{
- {// Native 1:1
- {1, AssetID(), AssetID(1), 1000, 1000, 1, 1, 1, 1, 1000},
- // Non-native 2:1
- {2, AssetID(1), AssetID(2), 1000, 500, 2, 1, 2, 1, 1000},
- // Non-native 1:2 sponsored by account 4
- {3, AssetID(1), AssetID(3), 500, 1000, 1, 2, 1, 2, 1000, 4},
- // Native no deposit
- {3, AssetID(), AssetID(4), 0, 0, 0, 0, 0, 0, 1000},
- // Non-native no deposit
- {3, AssetID(2), AssetID(4), 0, 0, 0, 0, 0, 0, 1000},
- // close to max reserves
- {3, AssetID(3), AssetID(4), INT64_MAX - 50, INT64_MAX - 50, 1, 1, 1, 1,
- INT64_MAX}}};
-
-void
-TransactionFuzzer::initialize()
-{
- reinitializeAllGlobalStateWithSeed(1);
- mApp = createTestApplication(mClock, getFuzzConfig(0));
- OrderBookIsNotCrossed::registerAndEnableInvariant(*mApp);
- auto root = mApp->getRoot();
- mSourceAccountID = root->getPublicKey();
-
- resetTxInternalState(*mApp);
- LedgerTxn ltxOuter(mApp->getLedgerTxnRoot());
-
- initializeAccounts(ltxOuter);
-
- initializeTrustLines(ltxOuter);
-
- initializeClaimableBalances(ltxOuter);
-
- initializeOffers(ltxOuter);
-
- initializeLiquidityPools(ltxOuter);
-
- reduceNativeBalancesAfterSetup(ltxOuter);
-
- adjustTrustLineBalancesAfterSetup(ltxOuter);
-
- reduceTrustLineLimitsAfterSetup(ltxOuter);
-
- storeSetupLedgerKeysAndPoolIDs(ltxOuter);
-
- // commit this to the ledger so that we have a starting, persistent
- // state to fuzz test against
- ltxOuter.commit();
-
-#ifdef BUILD_TESTS
- mApp->getInvariantManager().snapshotForFuzzer();
-#endif // BUILD_TESTS
-}
-
-void
-TransactionFuzzer::storeSetupPoolIDs(AbstractLedgerTxn& ltx,
- std::vector const& entries)
-{
- std::vector poolIDs;
- for (auto const& entry : entries)
- {
- if (entry.data.type() != LIQUIDITY_POOL)
- {
- continue;
- }
- poolIDs.emplace_back(entry.data.liquidityPool().liquidityPoolID);
- }
-
- assert(poolIDs.size() == FuzzUtils::NUM_STORED_POOL_IDS - 1);
- auto firstGeneratedPoolID =
- std::copy(poolIDs.cbegin(), poolIDs.cend(), mStoredPoolIDs.begin());
- std::generate(firstGeneratedPoolID, mStoredPoolIDs.end(),
- []() { return PoolID{}; });
-}
-
-void
-TransactionFuzzer::storeSetupLedgerKeysAndPoolIDs(AbstractLedgerTxn& ltx)
-{
- // Get the list of ledger entries created during setup to place into
- // mStoredLedgerKeys.
- std::vector init, live;
- std::vector dead;
- ltx.getAllEntries(init, live, dead);
-
- // getAllEntries() does not guarantee anything about the order in which
- // entries are returned, so to minimize non-determinism in fuzzing setup, we
- // sort them.
- std::sort(init.begin(), init.end());
-
- // Setup should only create entries; there should be no dead entries, and
- // at most one "live" (modified) one: the root account.
- assert(dead.empty());
- if (live.size() == 1)
- {
- assert(live[0].data.type() == ACCOUNT);
- assert(live[0].data.account().accountID ==
- txtest::getRoot(mApp->getNetworkID()).getPublicKey());
- }
- else
- {
- assert(live.empty());
- }
-
- // If we ever create more ledger entries during setup than we have room for
- // in mStoredLedgerEntries, then we will have to do something further.
- assert(init.size() <= FuzzUtils::NUM_VALIDATED_LEDGER_KEYS);
-
- // Store the ledger entries created during setup in mStoredLedgerKeys.
- auto firstGeneratedLedgerKey = std::transform(
- init.cbegin(), init.cend(), mStoredLedgerKeys.begin(), LedgerEntryKey);
-
- stellar::FuzzUtils::generateStoredLedgerKeys(firstGeneratedLedgerKey,
- mStoredLedgerKeys.end());
-
- storeSetupPoolIDs(ltx, init);
-}
-
-void
-TransactionFuzzer::initializeAccounts(AbstractLedgerTxn& ltxOuter)
-{
- LedgerTxn ltx(ltxOuter);
- xdr::xvector ops;
-
- for (auto const& param : accountParameters)
- {
- PublicKey publicKey;
- FuzzUtils::setShortKey(publicKey, param.mShortKey);
-
- FuzzUtils::emplaceConditionallySponsored(
- ops,
- txtest::createAccount(publicKey,
- FuzzUtils::INITIAL_ACCOUNT_BALANCE),
- param.mSponsored, param.mSponsorKey, publicKey);
-
- // Set options for any accounts whose parameters specify flags to
- // add.
- auto const optionFlags = param.mOptionFlags;
-
- if (optionFlags != 0)
- {
- auto optionsOp = txtest::setOptions(txtest::setFlags(optionFlags));
- optionsOp.sourceAccount.activate() = toMuxedAccount(publicKey);
- ops.emplace_back(optionsOp);
- }
- }
-
- applySetupOperations(ltx, mSourceAccountID, ops.begin(), ops.end(), *mApp);
-
- ltx.commit();
-}
-
-void
-TransactionFuzzer::initializeTrustLines(AbstractLedgerTxn& ltxOuter)
-{
- LedgerTxn ltx(ltxOuter);
-
- xdr::xvector ops;
-
- for (auto const& trustLine : trustLineParameters)
- {
- auto const trustor = trustLine.mTrustor;
- PublicKey account;
- FuzzUtils::setShortKey(account, trustor);
-
- auto const asset = trustLine.mAssetID.toAsset();
-
- // Trust the asset issuer.
- auto trustOp = txtest::changeTrust(
- asset, std::max(FuzzUtils::INITIAL_TRUST_LINE_LIMIT,
- trustLine.mAssetAvailableForTestActivity));
-
- trustOp.sourceAccount.activate() = toMuxedAccount(account);
- FuzzUtils::emplaceConditionallySponsored(
- ops, trustOp, trustLine.mSponsored, trustLine.mSponsorKey, account);
-
- PublicKey issuer;
- auto const issuerID = trustLine.mAssetID.mIssuer;
- FuzzUtils::setShortKey(issuer, issuerID);
-
- // Set trust line flags if specified.
- if (trustLine.mCallAllowTrustOp)
- {
- auto allowTrustOp =
- txtest::allowTrust(account, asset, trustLine.mAllowTrustFlags);
- allowTrustOp.sourceAccount.activate() = toMuxedAccount(issuer);
- ops.emplace_back(allowTrustOp);
- }
-
- if (!trustLine.mCallAllowTrustOp ||
- trustLine.mAllowTrustFlags & AUTHORIZED_FLAG)
- {
- // Distribute the starting amount of the asset (to be reduced after
- // orders have been placed).
- auto distributeOp = txtest::payment(
- account, asset,
- std::max(FuzzUtils::INITIAL_ASSET_DISTRIBUTION,
- trustLine.mAssetAvailableForTestActivity));
- distributeOp.sourceAccount.activate() = toMuxedAccount(issuer);
- ops.emplace_back(distributeOp);
- }
- }
-
- applySetupOperations(ltx, mSourceAccountID, ops.begin(), ops.end(), *mApp);
-
- ltx.commit();
-}
-
-void
-TransactionFuzzer::initializeClaimableBalances(AbstractLedgerTxn& ltxOuter)
-{
- LedgerTxn ltx(ltxOuter);
-
- xdr::xvector ops;
-
- for (auto const& param : claimableBalanceParameters)
- {
- Claimant claimant;
- claimant.v0().predicate.type(CLAIM_PREDICATE_UNCONDITIONAL);
- FuzzUtils::setShortKey(claimant.v0().destination, param.mClaimant);
-
- auto claimableBalanceOp = txtest::createClaimableBalance(
- param.mAsset.toAsset(), param.mAmount, {claimant});
-
- PublicKey senderKey;
- FuzzUtils::setShortKey(senderKey, param.mSender);
-
- claimableBalanceOp.sourceAccount.activate() = toMuxedAccount(senderKey);
- FuzzUtils::emplaceConditionallySponsored(ops, claimableBalanceOp,
- param.mSponsored,
- param.mSponsorKey, senderKey);
- }
-
- applySetupOperations(ltx, mSourceAccountID, ops.begin(), ops.end(), *mApp);
-
- ltx.commit();
-}
-
-void
-TransactionFuzzer::initializeOffers(AbstractLedgerTxn& ltxOuter)
-{
- LedgerTxn ltx(ltxOuter);
-
- xdr::xvector ops;
-
- for (auto const& param : orderBookParameters)
- {
- auto op = param.mPassive
- ? txtest::createPassiveOffer(
- param.mSell.toAsset(), param.mBid.toAsset(),
- Price{param.mNumerator, param.mDenominator},
- param.mAmount)
- : txtest::manageOffer(
- 0, param.mSell.toAsset(), param.mBid.toAsset(),
- Price{param.mNumerator, param.mDenominator},
- param.mAmount);
- PublicKey pkA;
- FuzzUtils::setShortKey(pkA, param.mPublicKey);
- op.sourceAccount.activate() = toMuxedAccount(pkA);
- FuzzUtils::emplaceConditionallySponsored(ops, op, param.mSponsored,
- param.mSponsorKey, pkA);
- }
-
- applySetupOperations(ltx, mSourceAccountID, ops.begin(), ops.end(), *mApp);
-
- ltx.commit();
-}
-
-void
-TransactionFuzzer::initializeLiquidityPools(AbstractLedgerTxn& ltxOuter)
-{
- LedgerTxn ltx(ltxOuter);
-
- xdr::xvector ops;
-
- for (auto const& param : poolSetupParameters)
- {
- auto const trustor = param.mTrustor;
- PublicKey account;
- FuzzUtils::setShortKey(account, trustor);
-
- // First create the pool
- auto const assetA = param.mAssetA.toAsset();
- auto const assetB = param.mAssetB.toAsset();
-
- ChangeTrustAsset poolAsset;
- poolAsset.type(ASSET_TYPE_POOL_SHARE);
- poolAsset.liquidityPool().constantProduct().assetA = assetA;
- poolAsset.liquidityPool().constantProduct().assetB = assetB;
- poolAsset.liquidityPool().constantProduct().fee =
- LIQUIDITY_POOL_FEE_V18;
-
- auto trustOp = txtest::changeTrust(
- poolAsset, std::max(FuzzUtils::INITIAL_TRUST_LINE_LIMIT,
- param.mLimit));
- trustOp.sourceAccount.activate() = toMuxedAccount(account);
- FuzzUtils::emplaceConditionallySponsored(ops, trustOp, param.mSponsored,
- param.mSponsorKey, account);
-
- // Then deposit
- if (param.mMaxAmountA > 0 && param.mMaxAmountB > 0)
- {
- auto depositOp = txtest::liquidityPoolDeposit(
- xdrSha256(poolAsset.liquidityPool()), param.mMaxAmountA,
- param.mMaxAmountB,
- Price{param.mMinPriceNumerator, param.mMinPriceDenominator},
- Price{param.mMaxPriceNumerator, param.mMaxPriceDenominator});
- depositOp.sourceAccount.activate() = toMuxedAccount(account);
- FuzzUtils::emplaceConditionallySponsored(
- ops, depositOp, param.mSponsored, param.mSponsorKey, account);
- }
- }
-
- applySetupOperations(ltx, mSourceAccountID, ops.begin(), ops.end(), *mApp);
-
- ltx.commit();
-}
-
-void
-TransactionFuzzer::reduceNativeBalancesAfterSetup(AbstractLedgerTxn& ltxOuter)
-{
- LedgerTxn ltx(ltxOuter);
-
- xdr::xvector ops;
-
- for (auto const& param : accountParameters)
- {
- PublicKey account;
- FuzzUtils::setShortKey(account, param.mShortKey);
-
- // Reduce "account"'s native balance by paying the *root, so that
- // fuzzing has a better chance of exercising edge cases.
- auto ae = stellar::loadAccount(ltx, account);
- auto const availableBalance = getAvailableBalance(ltx.loadHeader(), ae);
- auto const targetAvailableBalance =
- param.mNativeAssetAvailableForTestActivity +
- FuzzUtils::MIN_ACCOUNT_BALANCE;
-
- assert(availableBalance > targetAvailableBalance);
- auto reduceNativeBalanceOp = txtest::payment(
- mSourceAccountID, availableBalance - targetAvailableBalance);
- reduceNativeBalanceOp.sourceAccount.activate() =
- toMuxedAccount(account);
- ops.emplace_back(reduceNativeBalanceOp);
- }
-
- applySetupOperations(ltx, mSourceAccountID, ops.begin(), ops.end(), *mApp);
-
- ltx.commit();
-}
-
-void
-TransactionFuzzer::adjustTrustLineBalancesAfterSetup(
- AbstractLedgerTxn& ltxOuter)
-{
- LedgerTxn ltx(ltxOuter);
-
- xdr::xvector ops;
-
- // Reduce trustline balances so that fuzzing has a better chance of
- // exercising edge cases.
- for (auto const& trustLine : trustLineParameters)
- {
- auto const trustor = trustLine.mTrustor;
- PublicKey account;
- FuzzUtils::setShortKey(account, trustor);
-
- auto const asset = trustLine.mAssetID.toAsset();
-
- PublicKey issuer;
- FuzzUtils::setShortKey(issuer, trustLine.mAssetID.mIssuer);
-
- // Reduce "account"'s balance of this asset by paying the
- // issuer.
- auto tle = stellar::loadTrustLine(ltx, account, asset);
- if (!tle.isAuthorizedToMaintainLiabilities())
- {
- // Without authorization, this trustline could not have been funded
- // with how the setup currently works
- if (trustLine.mAssetAvailableForTestActivity != 0 ||
- tle.getBalance() != 0)
- {
- throw std::runtime_error("Invalid trustline setup");
- }
- continue;
- }
-
- auto const maxRecv = tle.getMaxAmountReceive(ltx.loadHeader());
- auto const availableTLBalance =
- tle.getAvailableBalance(ltx.loadHeader());
- auto const targetAvailableTLBalance =
- trustLine.mAssetAvailableForTestActivity;
- auto const paymentAmount =
- availableTLBalance - targetAvailableTLBalance;
-
- if (availableTLBalance > targetAvailableTLBalance)
- {
- auto reduceNonNativeBalanceOp =
- txtest::payment(issuer, asset, paymentAmount);
- reduceNonNativeBalanceOp.sourceAccount.activate() =
- toMuxedAccount(account);
- ops.emplace_back(reduceNonNativeBalanceOp);
- }
- else if (availableTLBalance < targetAvailableTLBalance && maxRecv > 0 &&
- (!trustLine.mCallAllowTrustOp ||
- trustLine.mAllowTrustFlags & AUTHORIZED_FLAG))
- {
- auto increaseNonNativeBalanceOp = txtest::payment(
- account, asset,
- std::min(targetAvailableTLBalance - availableTLBalance,
- maxRecv));
- increaseNonNativeBalanceOp.sourceAccount.activate() =
- toMuxedAccount(issuer);
- ops.emplace_back(increaseNonNativeBalanceOp);
- }
- }
-
- applySetupOperations(ltx, mSourceAccountID, ops.begin(), ops.end(), *mApp);
-
- ltx.commit();
-}
-
-void
-TransactionFuzzer::reduceTrustLineLimitsAfterSetup(AbstractLedgerTxn& ltxOuter)
-{
- LedgerTxn ltx(ltxOuter);
-
- xdr::xvector ops;
-
- // Reduce trustline limits so that fuzzing has a better chance of exercising
- // edge cases.
- for (auto const& trustLine : trustLineParameters)
- {
- auto const trustor = trustLine.mTrustor;
- PublicKey account;
- FuzzUtils::setShortKey(account, trustor);
-
- auto const asset = trustLine.mAssetID.toAsset();
-
- // Reduce this trustline's limit.
- auto tle = stellar::loadTrustLine(ltx, account, asset);
- auto const balancePlusBuyLiabilities =
- tle.getBalance() + tle.getBuyingLiabilities(ltx.loadHeader());
- auto const targetTrustLineLimit =
- INT64_MAX - trustLine.mSpareLimitAfterSetup <
- balancePlusBuyLiabilities
- ? INT64_MAX
- : balancePlusBuyLiabilities + trustLine.mSpareLimitAfterSetup;
-
- auto changeTrustLineLimitOp =
- txtest::changeTrust(asset, targetTrustLineLimit);
- changeTrustLineLimitOp.sourceAccount.activate() =
- toMuxedAccount(account);
- ops.emplace_back(changeTrustLineLimitOp);
- }
-
- applySetupOperations(ltx, mSourceAccountID, ops.begin(), ops.end(), *mApp);
-
- ltx.commit();
-}
-
-void
-TransactionFuzzer::shutdown()
-{
- exit(1);
-}
-
-void
-TransactionFuzzer::inject(std::string const& filename)
-{
- std::ifstream in;
- in.exceptions(std::ios::badbit);
- in.open(filename, std::ios::binary);
-
- xdr::xvector ops;
- std::vector bins(xdrSizeLimit());
- in.read(bins.data(), bins.size());
- auto actual = in.gcount();
- // stop if either
- // we could read the whole buffer (too much data was generated by the
- // fuzzer), or got a short read
- if (actual == xdrSizeLimit() || actual == 0)
- {
- return;
- }
- bins.resize(actual);
- try
- {
- xdr::xdr_from_fuzzer_opaque(mStoredLedgerKeys, mStoredPoolIDs, bins,
- ops);
- }
- catch (std::exception const& e)
- {
- // in case of fuzzer creating an ill-formed xdr, generate an
- // xdr that will trigger a non-execution path so that the fuzzer
- // realizes it has hit an uninteresting case
- LOG_TRACE(DEFAULT_LOG,
- "xdr::xdr_from_fuzzer_opaque() threw exception {}", e.what());
- return;
- }
- // limit operations per transaction to limit size of fuzzed input
- if (ops.size() < 1 || ops.size() > FuzzUtils::FUZZER_MAX_OPERATIONS)
- {
- LOG_TRACE(DEFAULT_LOG, "invalid ops.size() {}", ops.size());
- return;
- }
-
- resetTxInternalState(*mApp);
- LOG_TRACE(DEFAULT_LOG, "{}",
- xdrToCerealString(ops, fmt::format("Fuzz ops ({})", ops.size())));
-
- LedgerTxn ltx(mApp->getLedgerTxnRoot());
- applyFuzzOperations(ltx, mSourceAccountID, ops.begin(), ops.end(), *mApp);
-}
-
-int
-TransactionFuzzer::xdrSizeLimit()
-{
- // 50 bytes in compact mode seems to hold large operations
- return 50 * FuzzUtils::FUZZER_MAX_OPERATIONS;
-}
-
-#define FUZZER_INITIAL_CORPUS_OPERATION_GEN_UPPERBOUND 128
-void
-TransactionFuzzer::genFuzz(std::string const& filename)
-{
- reinitializeAllGlobalStateWithSeed(std::random_device()());
- std::ofstream out;
- out.exceptions(std::ios::failbit | std::ios::badbit);
- out.open(filename, std::ofstream::binary | std::ofstream::trunc);
- autocheck::generator gen;
- xdr::xvector ops;
- ops.reserve(FuzzUtils::FUZZER_MAX_OPERATIONS);
- auto const numops = rand_uniform(1, FuzzUtils::FUZZER_MAX_OPERATIONS);
- for (int i = 0; i < numops; ++i)
- {
- Operation op = gen(FUZZER_INITIAL_CORPUS_OPERATION_GEN_UPPERBOUND);
- if (op.body.type() == INVOKE_HOST_FUNCTION ||
- op.body.type() == EXTEND_FOOTPRINT_TTL ||
- op.body.type() == RESTORE_FOOTPRINT)
- {
- // Skip soroban txs for now because setting them up to be valid will
- // take some time.
- continue;
- }
-
- // Use account 0 for the base cases as it's more likely to be useful
- // right away.
- if (!op.sourceAccount)
- {
- PublicKey a0;
- FuzzUtils::setShortKey(a0, 0);
- op.sourceAccount.activate() = toMuxedAccount(a0);
- }
- ops.emplace_back(op);
- }
- auto bins = xdr::xdr_to_fuzzer_opaque(ops);
- out.write(reinterpret_cast(bins.data()), bins.size());
-}
-
-void
-OverlayFuzzer::shutdown()
-{
- mSimulation->stopAllNodes();
-}
-
-void
-OverlayFuzzer::initialize()
-{
- reinitializeAllGlobalStateWithSeed(1);
- stellar::FuzzUtils::generateStoredLedgerKeys(mStoredLedgerKeys.begin(),
- mStoredLedgerKeys.end());
- auto networkID = sha256(getTestConfig().NETWORK_PASSPHRASE);
- mSimulation = std::make_shared(Simulation::OVER_LOOPBACK,
- networkID, getFuzzConfig);
-
- SIMULATION_CREATE_NODE(10);
- SIMULATION_CREATE_NODE(11);
-
- SCPQuorumSet qSet0;
- qSet0.threshold = 2;
- qSet0.validators.push_back(v10NodeID);
- qSet0.validators.push_back(v11NodeID);
-
- mSimulation->addNode(v10SecretKey, qSet0);
- mSimulation->addNode(v11SecretKey, qSet0);
-
- mSimulation->addPendingConnection(v10SecretKey.getPublicKey(),
- v11SecretKey.getPublicKey());
-
- mSimulation->startAllNodes();
-
- // crank until nodes are connected
- mSimulation->crankUntil(
- [&]() {
- auto nodes = mSimulation->getNodes();
- auto numberOfSimulationConnections =
- nodes[ACCEPTOR_INDEX]
- ->getOverlayManager()
- .getAuthenticatedPeersCount() +
- nodes[INITIATOR_INDEX]
- ->getOverlayManager()
- .getAuthenticatedPeersCount();
- return numberOfSimulationConnections == 2;
- },
- std::chrono::milliseconds{500}, false);
-}
-
-void
-OverlayFuzzer::inject(std::string const& filename)
-{
- std::ifstream in;
- in.exceptions(std::ios::badbit);
- in.open(filename, std::ios::binary);
-
- StellarMessage msg;
- std::vector bins(xdrSizeLimit());
- in.read(bins.data(), bins.size());
- auto actual = in.gcount();
- // if we could read the whole buffer, or got a short read, stop
- if (in || actual == 0)
- {
- return;
- }
- bins.resize(actual);
- try
- {
- xdr::xdr_from_fuzzer_opaque(mStoredLedgerKeys, mStoredPoolIDs, bins,
- msg);
- }
- catch (...)
- {
- // in case of fuzzer creating an ill-formed xdr, generate an
- // xdr that will trigger a non-execution path so that the fuzzer
- // realizes it has hit an uninteresting case
- return;
- }
-
- if (isBadOverlayFuzzerInput(msg))
- {
- return;
- }
-
- auto nodeids = mSimulation->getNodeIDs();
- auto loopbackPeerConnection = mSimulation->getLoopbackConnection(
- nodeids[INITIATOR_INDEX], nodeids[ACCEPTOR_INDEX]);
-
- auto initiator = loopbackPeerConnection->getInitiator();
- auto acceptor = loopbackPeerConnection->getAcceptor();
-
- mSimulation->getNode(initiator->getPeerID())
- ->getClock()
- .postAction(
- [initiator, msg]() {
- initiator->Peer::sendMessage(
- std::make_shared(msg));
- },
- "main", Scheduler::ActionType::NORMAL_ACTION);
-
- mSimulation->crankForAtMost(std::chrono::milliseconds{500}, false);
-
- // clear all queues and cancel all events
- initiator->clearInAndOutQueues();
- acceptor->clearInAndOutQueues();
-
- while (mSimulation->getNode(initiator->getPeerID())
- ->getClock()
- .cancelAllEvents())
- ;
- while (mSimulation->getNode(acceptor->getPeerID())
- ->getClock()
- .cancelAllEvents())
- ;
-}
-
-int
-OverlayFuzzer::xdrSizeLimit()
-{
- return MAX_MESSAGE_SIZE;
-}
-
-#define FUZZER_INITIAL_CORPUS_MESSAGE_GEN_UPPERBOUND 16
-void
-OverlayFuzzer::genFuzz(std::string const& filename)
-{
- reinitializeAllGlobalStateWithSeed(std::random_device()());
- std::ofstream out;
- out.exceptions(std::ios::failbit | std::ios::badbit);
- out.open(filename, std::ofstream::binary | std::ofstream::trunc);
- autocheck::generator gen;
- StellarMessage m(gen(FUZZER_INITIAL_CORPUS_MESSAGE_GEN_UPPERBOUND));
- while (isBadOverlayFuzzerInput(m))
- {
- m = gen(FUZZER_INITIAL_CORPUS_MESSAGE_GEN_UPPERBOUND);
- }
- auto bins = xdr::xdr_to_fuzzer_opaque(m);
- out.write(reinterpret_cast(bins.data()), bins.size());
-}
-}
diff --git a/src/test/FuzzerImpl.h b/src/test/FuzzerImpl.h
deleted file mode 100644
index 5bd3a56c21..0000000000
--- a/src/test/FuzzerImpl.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2019 Stellar Development Foundation and contributors. Licensed
-// under the Apache License, Version 2.0. See the COPYING file at the root
-// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0
-
-#pragma once
-
-#include "ledger/LedgerTxn.h"
-#include "test/Fuzzer.h"
-#include "util/Timer.h"
-#include "xdr/Stellar-ledger-entries.h"
-#include "xdr/Stellar-types.h"
-
-namespace stellar
-{
-
-class XDRInputFileStream;
-class Simulation;
-class Application;
-struct StellarMessage;
-struct Operation;
-
-namespace FuzzUtils
-{
-size_t static constexpr NUM_STORED_LEDGER_KEYS = 0x100U;
-using StoredLedgerKeys = std::array;
-StoredLedgerKeys::difference_type static constexpr NUM_UNVALIDATED_LEDGER_KEYS =
- 0x40;
-size_t static constexpr NUM_VALIDATED_LEDGER_KEYS =
- NUM_STORED_LEDGER_KEYS - NUM_UNVALIDATED_LEDGER_KEYS;
-
-size_t static constexpr NUM_STORED_POOL_IDS = 0x7U;
-using StoredPoolIDs = std::array;
-}
-
-class TransactionFuzzer : public Fuzzer
-{
- public:
- TransactionFuzzer()
- {
- }
- void inject(std::string const& filename) override;
- void initialize() override;
- void shutdown() override;
- void genFuzz(std::string const& filename) override;
- int xdrSizeLimit() override;
-
- private:
- void storeSetupLedgerKeysAndPoolIDs(AbstractLedgerTxn& ltx);
- void storeSetupPoolIDs(AbstractLedgerTxn& ltx,
- std::vector const& entries);
- void initializeAccounts(AbstractLedgerTxn& ltxOuter);
- void initializeTrustLines(AbstractLedgerTxn& ltxOuter);
- void initializeClaimableBalances(AbstractLedgerTxn& ltxOuter);
- void initializeOffers(AbstractLedgerTxn& ltxOuter);
- void initializeLiquidityPools(AbstractLedgerTxn& ltxOuter);
- void reduceNativeBalancesAfterSetup(AbstractLedgerTxn& ltxOuter);
- void adjustTrustLineBalancesAfterSetup(AbstractLedgerTxn& ltxOuter);
- void reduceTrustLineLimitsAfterSetup(AbstractLedgerTxn& ltxOuter);
- VirtualClock mClock;
- std::shared_ptr mApp;
- PublicKey mSourceAccountID;
- FuzzUtils::StoredLedgerKeys mStoredLedgerKeys;
- FuzzUtils::StoredPoolIDs mStoredPoolIDs;
-};
-
-class OverlayFuzzer : public Fuzzer
-{
- int const ACCEPTOR_INDEX = 0;
- int const INITIATOR_INDEX = 1;
-
- public:
- OverlayFuzzer()
- {
- }
- void inject(std::string const& filename) override;
- void initialize() override;
- void shutdown() override;
- void genFuzz(std::string const& filename) override;
- int xdrSizeLimit() override;
-
- private:
- std::shared_ptr mSimulation;
- FuzzUtils::StoredLedgerKeys mStoredLedgerKeys;
- FuzzUtils::StoredPoolIDs mStoredPoolIDs;
-};
-}
diff --git a/src/test/fuzz.cpp b/src/test/fuzz.cpp
deleted file mode 100644
index a794b6ba56..0000000000
--- a/src/test/fuzz.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2015 Stellar Development Foundation and contributors. Licensed
-// under the Apache License, Version 2.0. See the COPYING file at the root
-// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0
-
-#include "test/fuzz.h"
-#include "test/FuzzerImpl.h"
-#include "test/test.h"
-#include "util/XDRStream.h"
-#include "util/types.h"
-
-#include
-/**
- * This is a very simple fuzzer _stub_. It's intended to be run under an
- * external fuzzer with some fuzzing brains, at this time, preferably AFL.
- *
- * It has two modes:
- *
- * - In genfuzz mode it spits out a small file containing a handful of
- * random FuzzTransactionInputs or StellarMessages. This is the mode you use
- * to generate seed data for the external fuzzer's corpus.
- *
- * - In fuzz mode it reads back a file and applies it to a stellar-core
- * instance, applying but not committing transactions one by one to simulate
- * certain transaction/overlay scenarios. It exits when it's applied the
- * input. This is the mode the external fuzzer will run its mutant inputs
- * through.
- *
- */
-
-namespace stellar
-{
-namespace FuzzUtils
-{
-std::unique_ptr
-createFuzzer(int processID, FuzzerMode fuzzerMode)
-{
- gBaseInstance = processID;
- switch (fuzzerMode)
- {
- case FuzzerMode::OVERLAY:
- return std::make_unique();
- case FuzzerMode::TRANSACTION:
- return std::make_unique();
- default:
- abort();
- }
-}
-}
-
-#define PERSIST_MAX 1000000
-void
-fuzz(std::string const& filename, std::vector const& metrics,
- int processID, FuzzerMode fuzzerMode)
-{
- auto fuzzer = FuzzUtils::createFuzzer(processID, fuzzerMode);
- fuzzer->initialize();
-
-// "To make this work, the library and this shim need to be compiled in LLVM
-// mode using afl-clang-fast (other compiler wrappers will *not* work)."
-// -- AFL docs
-#ifdef AFL_LLVM_MODE
- while (__AFL_LOOP(PERSIST_MAX))
-#endif // AFL_LLVM_MODE
- {
- fuzzer->inject(filename);
- }
- cleanupTmpDirs();
- fuzzer->shutdown();
-}
-}
diff --git a/src/test/fuzz.h b/src/test/fuzz.h
deleted file mode 100644
index 7bd8d1eb39..0000000000
--- a/src/test/fuzz.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2015 Stellar Development Foundation and contributors. Licensed
-// under the Apache License, Version 2.0. See the COPYING file at the root
-// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0
-
-#pragma once
-
-#include
-#include
-#include
-
-namespace stellar
-{
-
-class Fuzzer;
-
-enum class FuzzerMode
-{
- OVERLAY,
- TRANSACTION
-};
-
-namespace FuzzUtils
-{
-std::unique_ptr createFuzzer(int processID, FuzzerMode fuzzerMode);
-}
-
-void fuzz(std::string const& filename, std::vector const& metrics,
- int processID, FuzzerMode fuzzerMode);
-}
diff --git a/src/test/fuzz/FuzzMain.cpp b/src/test/fuzz/FuzzMain.cpp
new file mode 100644
index 0000000000..ca62bec96f
--- /dev/null
+++ b/src/test/fuzz/FuzzMain.cpp
@@ -0,0 +1,117 @@
+// Copyright 2026 Stellar Development Foundation and contributors. Licensed
+// under the Apache License, Version 2.0. See the COPYING file at the root
+// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0
+
+/**
+ * FuzzMain.cpp - libfuzzer compatible (though not necessarily always driven
+ * by libfuzzer) entry point for unified fuzzing infrastructure
+ *
+ * This file provides the LLVMFuzzerTestOneInput function required by libfuzzer,
+ * honggfuzz, and AFL++ (persistent mode). It can also be used with other
+ * coverage-guided fuzzers that support this API.
+ *
+ * The target to fuzz is selected at compile time via FUZZ_TARGET_NAME define.
+ * Build system generates separate binaries for each target defined in
+ * FUZZ_TARGETS in Makefile.am. See 'make fuzz-targets' for the full list.
+ *
+ * Usage:
+ *
+ * ./fuzz_tx corpus/tx/
+ *
+ * or just
+ *
+ * ./fuzz_tx
+ *
+ * The fuzzer driver is literally linked into this program, so just run it. The
+ * fuzzer driver provides its own main(). If you want to re-run a specific fuzz
+ * input outside of the fuzzer, say with logging enabled or such, use
+ * stellar-core's 'fuzz-one' command instead.
+ */
+
+#ifndef FUZZ_TARGET_NAME
+#error "FUZZ_TARGET_NAME must be defined at compile time (e.g., -DFUZZ_TARGET_NAME=\"tx\")"
+#endif
+
+#include "test/fuzz/FuzzTargetRegistry.h"
+#include "util/Logging.h"
+#include "util/Math.h"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace
+{
+
+// Global state for the fuzz target
+std::unique_ptr gFuzzTarget;
+bool gInitialized = false;
+
+void
+initializeFuzzer()
+{
+ if (gInitialized)
+ {
+ return;
+ }
+
+ // Suppress most logging during fuzzing for performance
+ stellar::Logging::setLogLevel(stellar::LogLevel::LVL_FATAL, nullptr);
+
+ // Initialize global state needed by stellar-core
+ stellar::reinitializeAllGlobalStateWithSeed(1);
+
+ // Create and initialize the target (selected at compile time)
+ constexpr const char* targetName = FUZZ_TARGET_NAME;
+ gFuzzTarget =
+ stellar::FuzzTargetRegistry::instance().createTarget(targetName);
+
+ // createTarget() throws on unknown target, so gFuzzTarget is never null
+ gFuzzTarget->initialize();
+ gInitialized = true;
+}
+
+} // namespace
+
+/**
+ * LLVMFuzzerTestOneInput - standard libfuzzer entry point
+ *
+ * This function is called by the fuzzer for each input. It should:
+ * - Process the input quickly (no I/O, minimal allocation)
+ * - Not call exit() or abort() for expected failures
+ * - Return 0 on success, -1 on rejected input
+ *
+ * Crashes and assertion failures will be reported by the fuzzer.
+ */
+extern "C" int
+LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
+{
+ // Initialize on first call
+ if (!gInitialized)
+ {
+ initializeFuzzer();
+ }
+
+ // Run the target once
+ switch (gFuzzTarget->run(data, size))
+ {
+ case stellar::FuzzResultCode::FUZZ_SUCCESS:
+ return 0;
+ case stellar::FuzzResultCode::FUZZ_REJECTED:
+ // Input was rejected (normal for fuzzing) - do not treat as error
+ return -1;
+ case stellar::FuzzResultCode::FUZZ_DISABLED:
+ // Target is not available in this build configuration
+ std::fprintf(stderr, "Fuzz target is disabled in this build\n");
+ return -1;
+ case stellar::FuzzResultCode::FUZZ_TARGET_UNKNOWN:
+ // Should not happen - we validated target at initialization
+ std::fprintf(stderr, "Fuzz target became unknown at runtime\n");
+ std::abort();
+ default:
+ std::fprintf(stderr, "Fuzz target returned unexpected result\n");
+ std::abort();
+ }
+}
\ No newline at end of file
diff --git a/src/test/fuzz/FuzzRegressionTests.cpp b/src/test/fuzz/FuzzRegressionTests.cpp
new file mode 100644
index 0000000000..16346d9fc1
--- /dev/null
+++ b/src/test/fuzz/FuzzRegressionTests.cpp
@@ -0,0 +1,180 @@
+// Copyright 2026 Stellar Development Foundation and contributors. Licensed
+// under the Apache License, Version 2.0. See the COPYING file at the root
+// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0
+
+/**
+ * FuzzRegressionTests.cpp - Shared helpers for fuzz target testing
+ *
+ * This file provides the implementation of shared test helpers used by
+ * the REGISTER_FUZZ_TARGET and REGISTER_SOROBAN_FUZZ_TARGET macros.
+ *
+ * Each macro generates a TEST_CASE that calls these helpers. This file
+ * does NOT need to be modified when adding new fuzz targets - just use
+ * the REGISTER_*_FUZZ_TARGET macro in your target's .cpp file.
+ *
+ * The helpers implement:
+ * - Smoke testing: Generate random input and run it through the target
+ * - Corpus regression: Run all files in corpus// through the target
+ *
+ * To run all fuzz tests:
+ * stellar-core test "[fuzz]"
+ *
+ * To run tests for a specific target:
+ * stellar-core test "[fuzz][tx]"
+ * stellar-core test "[fuzz][overlay]"
+ * stellar-core test "[fuzz][soroban_expr]"
+ */
+
+#include "test/fuzz/FuzzTargetRegistry.h"
+#include "util/Logging.h"
+
+#include
+#include
+#include
+
+namespace stellar
+{
+
+// Maximum number of iterations to attempt before declaring the smoke test
+// failed. This gives the fuzzer enough attempts to generate at least one
+// accepted input.
+static constexpr size_t SMOKE_TEST_MAX_ITERATIONS = 5000;
+
+// Number of successes we want to see in smoke testing. We should get this
+// many before we hit the max iterations.
+static constexpr size_t SMOKE_TEST_SUCCESS_TARGET = 10;
+
+void
+runFuzzTargetSmokeTest(std::string const& targetName)
+{
+ size_t successCount = 0;
+ auto target = FuzzTargetRegistry::instance().createTarget(targetName);
+ target->initialize();
+
+ for (size_t iteration = 0; iteration < SMOKE_TEST_MAX_ITERATIONS;
+ ++iteration)
+ {
+ // Generate a seed input
+ auto input = target->generateSeedInput();
+
+ // Run the input through the target
+ FuzzResultCode result = target->run(input.data(), input.size());
+
+ switch (result)
+ {
+ case FuzzResultCode::FUZZ_SUCCESS:
+ successCount++;
+ if (successCount >= SMOKE_TEST_SUCCESS_TARGET)
+ {
+ CLOG_INFO(Test,
+ "Smoke test for '{}' succeeded {} times after {} "
+ "iterations",
+ targetName, successCount, iteration + 1);
+ target->shutdown();
+ return;
+ }
+ break;
+
+ case FuzzResultCode::FUZZ_REJECTED:
+ CLOG_DEBUG(Test,
+ "Smoke test input for '{}' was rejected - continuing",
+ targetName);
+ break;
+
+ case FuzzResultCode::FUZZ_DISABLED:
+ // Target is not available in this build configuration
+ // (e.g., Soroban fuzz targets when Rust 'fuzz' feature is disabled)
+ CLOG_WARNING(Test,
+ "Smoke test for '{}' skipped: target is disabled in "
+ "this build",
+ targetName);
+ target->shutdown();
+ return;
+
+ case FuzzResultCode::FUZZ_TARGET_UNKNOWN:
+ throw std::runtime_error(fmt::format(
+ "Smoke test for '{}' failed with unknown target error",
+ targetName));
+ }
+ }
+ target->shutdown();
+
+ throw std::runtime_error("Smoke test for '" + targetName +
+ "' failed: no accepted inputs after " +
+ std::to_string(SMOKE_TEST_MAX_ITERATIONS) +
+ " iterations");
+}
+
+void
+runFuzzTargetCorpusRegression(std::string const& targetName)
+{
+ // Look for corpus in multiple locations
+ std::vector corpusDirs = {"corpus/" + targetName,
+ "src/corpus/" + targetName,
+ "../corpus/" + targetName};
+
+ std::string foundDir;
+ for (auto const& dir : corpusDirs)
+ {
+ if (std::filesystem::exists(dir))
+ {
+ foundDir = dir;
+ break;
+ }
+ }
+
+ if (foundDir.empty())
+ {
+ CLOG_WARNING(Tx,
+ "No corpus found for '{}'. Generate with: stellar-core "
+ "gen-fuzz --target={} --output-dir=corpus/{}",
+ targetName, targetName, targetName);
+ return;
+ }
+
+ auto target = FuzzTargetRegistry::instance().createTarget(targetName);
+ target->initialize();
+
+ int filesProcessed = 0;
+ for (auto const& entry : std::filesystem::directory_iterator(foundDir))
+ {
+ if (!entry.is_regular_file())
+ continue;
+
+ auto const& path = entry.path();
+ // Process .xdr and .bin files (common corpus formats)
+ auto ext = path.extension().string();
+ if (ext != ".xdr" && ext != ".bin")
+ continue;
+
+ CLOG_DEBUG(Tx, "Running fuzz input: {}", path.string());
+
+ try
+ {
+ std::ifstream in(path, std::ios::binary);
+ std::vector data(target->maxInputSize());
+ in.read(reinterpret_cast(data.data()), data.size());
+ auto actual = in.gcount();
+ if (actual > 0)
+ {
+ data.resize(actual);
+ target->run(data.data(), data.size());
+ }
+ filesProcessed++;
+ }
+ catch (std::exception const& e)
+ {
+ // Log but don't fail - fuzzer inputs may trigger expected
+ // errors
+ CLOG_DEBUG(Tx, "Fuzz input {} threw: {}", path.string(), e.what());
+ filesProcessed++;
+ }
+ }
+
+ target->shutdown();
+
+ CLOG_INFO(Tx, "Processed {} corpus files from {} for target '{}'",
+ filesProcessed, foundDir, targetName);
+}
+
+} // namespace stellar
diff --git a/src/test/fuzz/FuzzTargetRegistry.cpp b/src/test/fuzz/FuzzTargetRegistry.cpp
new file mode 100644
index 0000000000..b97a1d60a7
--- /dev/null
+++ b/src/test/fuzz/FuzzTargetRegistry.cpp
@@ -0,0 +1,112 @@
+// Copyright 2026 Stellar Development Foundation and contributors. Licensed
+// under the Apache License, Version 2.0. See the COPYING file at the root
+// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0
+
+#include "test/fuzz/FuzzTargetRegistry.h"
+#include "test/fuzz/FuzzUtils.h"
+#include "util/Math.h"
+#include
+#include
+#include
+#include
+#include
+
+namespace stellar
+{
+
+FuzzTargetRegistry&
+FuzzTargetRegistry::instance()
+{
+ static FuzzTargetRegistry registry;
+ return registry;
+}
+
+void
+FuzzTargetRegistry::registerTarget(std::string const& name,
+ std::string const& description,
+ TargetFactory factory)
+{
+ // Check for duplicate registration
+ for (auto const& target : mTargets)
+ {
+ if (target.name == name)
+ {
+ throw std::runtime_error("Duplicate fuzz target registration: " +
+ name);
+ }
+ }
+ mTargets.push_back({name, description, std::move(factory)});
+}
+
+std::vector const&
+FuzzTargetRegistry::targets() const
+{
+ return mTargets;
+}
+
+std::optional
+FuzzTargetRegistry::findTarget(std::string const& name) const
+{
+ auto it = std::find_if(mTargets.begin(), mTargets.end(),
+ [&](auto const& t) { return t.name == name; });
+ if (it != mTargets.end())
+ {
+ return *it;
+ }
+ return std::nullopt;
+}
+
+std::unique_ptr
+FuzzTargetRegistry::createTarget(std::string const& name) const
+{
+ auto info = findTarget(name);
+ if (!info)
+ {
+ throw std::runtime_error("Unknown fuzz target: " + name);
+ }
+ return info->factory();
+}
+
+std::vector
+FuzzTargetRegistry::listTargetNames() const
+{
+ std::vector names;
+ names.reserve(mTargets.size());
+ for (auto const& target : mTargets)
+ {
+ names.push_back(target.name);
+ }
+ return names;
+}
+
+void
+FuzzTarget::generateSeedCorpus(std::string const& outputDir, size_t count)
+{
+ std::filesystem::create_directories(outputDir);
+
+ // Initialize global state once before generating corpus
+ reinitializeAllGlobalStateForFuzzing(std::random_device()());
+
+ for (size_t i = 0; i < count; ++i)
+ {
+ auto data = generateSeedInput();
+
+ auto filename = std::filesystem::path(outputDir) /
+ (name() + "_seed_" + std::to_string(i) + ".bin");
+
+ std::ofstream out;
+ out.exceptions(std::ios::failbit | std::ios::badbit);
+ out.open(filename, std::ofstream::binary | std::ofstream::trunc);
+ out.write(reinterpret_cast(data.data()), data.size());
+ }
+}
+
+FuzzTargetRegistrar::FuzzTargetRegistrar(
+ std::string const& name, std::string const& description,
+ FuzzTargetRegistry::TargetFactory factory)
+{
+ FuzzTargetRegistry::instance().registerTarget(name, description,
+ std::move(factory));
+}
+
+} // namespace stellar
diff --git a/src/test/fuzz/FuzzTargetRegistry.h b/src/test/fuzz/FuzzTargetRegistry.h
new file mode 100644
index 0000000000..bf5e8dbea8
--- /dev/null
+++ b/src/test/fuzz/FuzzTargetRegistry.h
@@ -0,0 +1,153 @@
+// Copyright 2026 Stellar Development Foundation and contributors. Licensed
+// under the Apache License, Version 2.0. See the COPYING file at the root
+// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0
+
+#pragma once
+
+#include "rust/RustBridge.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace stellar
+{
+
+/**
+ * FuzzTarget is the interface all fuzz targets must implement.
+ *
+ * This design follows the libfuzzer model where each target is a function
+ * that accepts a byte buffer and returns 0 on success. The infrastructure
+ * handles setup/teardown, corpus management, and fuzzer integration.
+ */
+class FuzzTarget
+{
+ public:
+ virtual ~FuzzTarget() = default;
+
+ // Name of this fuzz target (e.g., "tx", "overlay")
+ virtual std::string name() const = 0;
+
+ // Human-readable description
+ virtual std::string description() const = 0;
+
+ // Initialize the target (called once before fuzzing starts)
+ virtual void initialize() = 0;
+
+ // Run one fuzzing iteration with the given input
+ // Returns FUZZ_SUCCESS on success (input was processed)
+ // Throws on catastrophic error
+ virtual FuzzResultCode run(uint8_t const* data, size_t size) = 0;
+
+ // Shutdown the target (called once when fuzzing is done)
+ virtual void shutdown() = 0;
+
+ // Maximum input size this target can handle
+ virtual size_t maxInputSize() const = 0;
+
+ // Generate a single seed input as a byte buffer.
+ // This is the core generation logic that each target must implement.
+ virtual std::vector generateSeedInput() = 0;
+
+ // Generate seed corpus entries and write them to the given directory.
+ // Default implementation calls generateSeedInput() for each entry.
+ virtual void generateSeedCorpus(std::string const& outputDir, size_t count);
+};
+
+/**
+ * FuzzTargetRegistry manages all registered fuzz targets.
+ *
+ * Targets self-register using the REGISTER_FUZZ_TARGET macro.
+ */
+class FuzzTargetRegistry
+{
+ public:
+ using TargetFactory = std::function()>;
+
+ struct TargetInfo
+ {
+ std::string name;
+ std::string description;
+ TargetFactory factory;
+ };
+
+ // Get the global registry instance
+ static FuzzTargetRegistry& instance();
+
+ // Register a fuzz target factory
+ void registerTarget(std::string const& name, std::string const& description,
+ TargetFactory factory);
+
+ // Get list of all registered targets
+ std::vector const& targets() const;
+
+ // Find a target by name
+ std::optional