diff --git a/Cargo.lock b/Cargo.lock index 822954b..5dd2b47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -428,7 +428,7 @@ dependencies = [ "derive_more", "foldhash 0.2.0", "hashbrown 0.16.1", - "indexmap 2.13.0", + "indexmap 2.13.1", "itoa", "k256", "keccak-asm", @@ -485,9 +485,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93e50f64a77ad9c5470bf2ad0ca02f228da70c792a8f06634801e202579f35e" +checksum = "dc90b1e703d3c03f4ff7f48e82dd0bc1c8211ab7d079cd836a06fcfeb06651cb" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -496,9 +496,9 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8849c74c9ca0f5a03da1c865e3eb6f768df816e67dd3721a398a8a7e398011" +checksum = "f36834a5c0a2fa56e171bf256c34d70fca07d0c0031583edea1c4946b7889c9e" dependencies = [ "proc-macro2", "quote", @@ -650,7 +650,7 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.13.0", + "indexmap 2.13.1", "proc-macro-error2", "proc-macro2", "quote", @@ -901,7 +901,8 @@ dependencies = [ [[package]] name = "ant-node" -version = "0.9.0" +version = "0.10.0" +source = "git+https://github.com/WithAutonomi/ant-node?rev=44358c1b4e35cc9aff5841a746bb2b1fcb6dcbee#44358c1b4e35cc9aff5841a746bb2b1fcb6dcbee" dependencies = [ "aes-gcm-siv", "blake3", @@ -920,6 +921,7 @@ dependencies = [ "lru", "objc2", "objc2-foundation", + "page_size", "parking_lot", "postcard", "rand 0.8.5", @@ -2415,9 +2417,9 @@ dependencies = [ [[package]] name = "evmlib" -version = "0.5.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d608fcd0976beee509fef7fa391735571cb2fffd715ddca174322180300b6615" +checksum = "1338c23c9ce1b4e54ff5cc65e53ce859095f121bfc742e474e1e1f2e03748000" dependencies = [ "alloy", "ant-merkle", @@ -2826,7 +2828,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.13.0", + "indexmap 2.13.1", "slab", "tokio", "tokio-util", @@ -3144,12 +3146,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -3157,9 +3160,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -3170,9 +3173,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -3184,15 +3187,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -3204,15 +3207,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -3295,9 +3298,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" dependencies = [ "equivalent", "hashbrown 0.16.1", @@ -3425,9 +3428,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.93" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "797146bb2677299a1eb6b7b50a890f4c361b29ef967addf5b2fa45dae1bb6d7d" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" dependencies = [ "cfg-if", "futures-util", @@ -3492,9 +3495,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.183" +version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" [[package]] name = "libm" @@ -3522,9 +3525,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "lmdb-master-sys" @@ -4201,9 +4204,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -4259,7 +4262,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.8+spec-1.1.0", + "toml_edit 0.25.10+spec-1.1.0", ] [[package]] @@ -4983,6 +4986,8 @@ dependencies = [ [[package]] name = "saorsa-core" version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19de0950a0c9c9a3f97681c539e513c52c909866caddef5d825fe2dcbd1b0173" dependencies = [ "anyhow", "async-trait", @@ -5095,6 +5100,8 @@ dependencies = [ [[package]] name = "saorsa-transport" version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbe38ad2be12e4be34e203e79a33eab1664c23a4b65a1ff4b08973ae9af01f01" dependencies = [ "anyhow", "async-trait", @@ -5108,7 +5115,7 @@ dependencies = [ "dirs 5.0.1", "futures-util", "hex", - "indexmap 2.13.0", + "indexmap 2.13.1", "keyring", "libc", "lru-slab", @@ -5412,7 +5419,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.13.0", + "indexmap 2.13.1", "schemars 0.9.0", "schemars 1.2.1", "serde_core", @@ -5439,7 +5446,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.13.1", "itoa", "ryu", "serde", @@ -5897,9 +5904,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -5922,9 +5929,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.50.0" +version = "1.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" dependencies = [ "bytes", "libc", @@ -5939,9 +5946,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -6028,9 +6035,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] @@ -6041,7 +6048,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.13.1", "serde", "serde_spanned", "toml_datetime 0.6.11", @@ -6051,21 +6058,21 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.8+spec-1.1.0" +version = "0.25.10+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16bff38f1d86c47f9ff0647e6838d7bb362522bdf44006c7068c2b1e606f1f3c" +checksum = "a82418ca169e235e6c399a84e395ab6debeb3bc90edc959bf0f48647c6a32d1b" dependencies = [ - "indexmap 2.13.0", - "toml_datetime 1.1.0+spec-1.1.0", + "indexmap 2.13.1", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "winnow 1.0.1", ] [[package]] name = "toml_parser" -version = "1.1.0+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ "winnow 1.0.1", ] @@ -6333,7 +6340,7 @@ version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.13.1", "serde", "serde_json", "utoipa-gen", @@ -6435,9 +6442,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.116" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dc0882f7b5bb01ae8c5215a1230832694481c1a4be062fd410e12ea3da5b631" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" dependencies = [ "cfg-if", "once_cell", @@ -6448,9 +6455,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.66" +version = "0.4.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19280959e2844181895ef62f065c63e0ca07ece4771b53d89bfdb967d97cbf05" +checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" dependencies = [ "js-sys", "wasm-bindgen", @@ -6458,9 +6465,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.116" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75973d3066e01d035dbedaad2864c398df42f8dd7b1ea057c35b8407c015b537" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6468,9 +6475,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.116" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91af5e4be765819e0bcfee7322c14374dc821e35e72fa663a830bbc7dc199eac" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" dependencies = [ "bumpalo", "proc-macro2", @@ -6481,9 +6488,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.116" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9bf0406a78f02f336bf1e451799cca198e8acde4ffa278f0fb20487b150a633" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" dependencies = [ "unicode-ident", ] @@ -6505,7 +6512,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.13.0", + "indexmap 2.13.1", "wasm-encoder", "wasmparser", ] @@ -6531,7 +6538,7 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags", "hashbrown 0.15.5", - "indexmap 2.13.0", + "indexmap 2.13.1", "semver 1.0.27", ] @@ -6551,9 +6558,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.93" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749466a37ee189057f54748b200186b59a03417a117267baf3fd89cecc9fb837" +checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a" dependencies = [ "js-sys", "wasm-bindgen", @@ -7087,7 +7094,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap 2.13.0", + "indexmap 2.13.1", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -7118,7 +7125,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags", - "indexmap 2.13.0", + "indexmap 2.13.1", "log", "serde", "serde_derive", @@ -7137,7 +7144,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.13.0", + "indexmap 2.13.1", "log", "semver 1.0.27", "serde", @@ -7149,9 +7156,9 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "wyz" @@ -7236,9 +7243,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -7247,9 +7254,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", @@ -7279,18 +7286,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", @@ -7320,9 +7327,9 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -7331,9 +7338,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -7342,9 +7349,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", @@ -7368,7 +7375,7 @@ dependencies = [ "flate2", "getrandom 0.3.4", "hmac", - "indexmap 2.13.0", + "indexmap 2.13.1", "lzma-rs", "memchr", "pbkdf2", diff --git a/ant-cli/src/main.rs b/ant-cli/src/main.rs index 1a39279..245344a 100644 --- a/ant-cli/src/main.rs +++ b/ant-cli/src/main.rs @@ -218,24 +218,14 @@ fn resolve_evm_network( .payment_token_address .parse() .map_err(|e| anyhow::anyhow!("Invalid token address: {e}"))?; - let payments_addr: EvmAddress = evm - .data_payments_address + let vault_addr: EvmAddress = evm + .payment_vault_address .parse() - .map_err(|e| anyhow::anyhow!("Invalid payments address: {e}"))?; - let merkle_addr: Option = evm - .merkle_payments_address - .as_ref() - .map(|s| { - s.parse().map_err(|e| { - anyhow::anyhow!("Invalid merkle payments address: {e}") - }) - }) - .transpose()?; + .map_err(|e| anyhow::anyhow!("Invalid payment vault address: {e}"))?; return Ok(EvmNetwork::Custom(CustomNetwork { rpc_url_http: rpc_url, payment_token_address: token_addr, - data_payments_address: payments_addr, - merkle_payments_address: merkle_addr, + payment_vault_address: vault_addr, })); } } diff --git a/ant-core/Cargo.toml b/ant-core/Cargo.toml index ae56603..686531e 100644 --- a/ant-core/Cargo.toml +++ b/ant-core/Cargo.toml @@ -27,7 +27,7 @@ zip = "2" tower-http = { version = "0.6.8", features = ["cors"] } # Data operations -evmlib = "0.5.0" +evmlib = "0.8" xor_name = "5" self_encryption = "0.35.0" futures = "0.3" @@ -38,7 +38,7 @@ tracing = "0.1" bytes = "1" lru = "0.16" rand = "0.8" -ant-node = "0.9.0" +ant-node = { git = "https://github.com/WithAutonomi/ant-node", rev = "44358c1b4e35cc9aff5841a746bb2b1fcb6dcbee" } saorsa-pqc = "0.5" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/ant-core/src/data/client/batch.rs b/ant-core/src/data/client/batch.rs index 7c53361..2afdbbd 100644 --- a/ant-core/src/data/client/batch.rs +++ b/ant-core/src/data/client/batch.rs @@ -13,7 +13,6 @@ use ant_node::core::{MultiAddr, PeerId}; use ant_node::payment::{serialize_single_node_proof, PaymentProof, SingleNodePayment}; use bytes::Bytes; use evmlib::common::{Amount, QuoteHash, TxHash}; -use evmlib::contract::payment_vault::get_market_price; use evmlib::wallet::PayForQuotesError; use evmlib::{EncodedPeerId, PaymentQuote, ProofOfPayment, RewardsAddress}; use futures::stream::{self, StreamExt}; @@ -144,7 +143,7 @@ pub fn finalize_batch_payment( impl Client { /// Prepare a single chunk for batch payment. /// - /// Collects quotes and fetches contract prices without making any + /// Collects quotes and uses node-reported prices without making any /// on-chain transaction. Returns `Ok(None)` if the chunk is already /// stored on the network. /// @@ -168,44 +167,21 @@ impl Client { Err(e) => return Err(e), }; - let evm_network = self.require_evm_network()?; - // Capture all quoted peers for close-group replication. let quoted_peers: Vec<(PeerId, Vec)> = quotes_with_peers .iter() .map(|(peer_id, addrs, _, _)| (*peer_id, addrs.clone())) .collect(); - // Fetch authoritative prices from the on-chain contract. - let metrics_batch: Vec<_> = quotes_with_peers - .iter() - .map(|(_, _, quote, _)| quote.quoting_metrics.clone()) - .collect(); - - let contract_prices = get_market_price(evm_network, metrics_batch) - .await - .map_err(|e| { - Error::Payment(format!("Failed to get market prices from contract: {e}")) - })?; - - if contract_prices.len() != quotes_with_peers.len() { - return Err(Error::Payment(format!( - "Contract returned {} prices for {} quotes", - contract_prices.len(), - quotes_with_peers.len() - ))); - } - // Build peer_quotes for ProofOfPayment + quotes for SingleNodePayment. + // Use node-reported prices directly — no contract price fetch needed. let mut peer_quotes = Vec::with_capacity(quotes_with_peers.len()); let mut quotes_for_payment = Vec::with_capacity(quotes_with_peers.len()); - for ((peer_id, _addrs, quote, _local_price), contract_price) in - quotes_with_peers.into_iter().zip(contract_prices) - { + for (peer_id, _addrs, quote, price) in quotes_with_peers { let encoded = peer_id_to_encoded(&peer_id)?; peer_quotes.push((encoded, quote.clone())); - quotes_for_payment.push((quote, contract_price)); + quotes_for_payment.push((quote, price)); } let payment = SingleNodePayment::from_quotes(quotes_for_payment) @@ -437,31 +413,23 @@ mod tests { use super::*; use ant_node::payment::single_node::QuotePaymentInfo; use ant_node::CLOSE_GROUP_SIZE; - use evmlib::quoting_metrics::QuotingMetrics; - - fn test_metrics() -> QuotingMetrics { - QuotingMetrics { - data_size: 0, - data_type: 0, - close_records_stored: 0, - records_per_type: vec![], - max_records: 0, - received_payment_count: 0, - live_time: 0, - network_density: None, - network_size: None, - } - } - /// Helper: build a PreparedChunk with specified payment amounts. - fn make_prepared_chunk(amounts: [u64; CLOSE_GROUP_SIZE]) -> PreparedChunk { - let quotes: [QuotePaymentInfo; CLOSE_GROUP_SIZE] = - std::array::from_fn(|i| QuotePaymentInfo { + /// Median index in the quotes array. + const MEDIAN_INDEX: usize = CLOSE_GROUP_SIZE / 2; + + /// Helper: build a `PreparedChunk` with `median_amount` at the median + /// quote index and zero for all other quotes. Adapts automatically to + /// `CLOSE_GROUP_SIZE` changes. + fn make_prepared_chunk(median_amount: u64) -> PreparedChunk { + let quotes: [QuotePaymentInfo; CLOSE_GROUP_SIZE] = std::array::from_fn(|i| { + let amount = if i == MEDIAN_INDEX { median_amount } else { 0 }; + QuotePaymentInfo { quote_hash: QuoteHash::from([i as u8 + 1; 32]), rewards_address: RewardsAddress::new([i as u8 + 10; 20]), - amount: Amount::from(amounts[i]), - quoting_metrics: test_metrics(), - }); + amount: Amount::from(amount), + price: Amount::from(amount), + } + }); PreparedChunk { content: Bytes::from(vec![0xAA; 32]), @@ -474,23 +442,22 @@ mod tests { #[test] fn payment_intent_from_single_chunk() { - // Median (index 2) gets 3x price, others get 0 — matches SingleNodePayment layout - let chunk = make_prepared_chunk([0, 0, 300, 0, 0]); + let chunk = make_prepared_chunk(300); let intent = PaymentIntent::from_prepared_chunks(&[chunk]); assert_eq!(intent.payments.len(), 1, "only non-zero amounts"); assert_eq!(intent.total_amount, Amount::from(300)); let (hash, addr, amt) = &intent.payments[0]; - assert_eq!(*hash, QuoteHash::from([3u8; 32])); // index 2 → byte 3 - assert_eq!(*addr, RewardsAddress::new([12u8; 20])); // index 2 → byte 12 + assert_eq!(*hash, QuoteHash::from([MEDIAN_INDEX as u8 + 1; 32])); + assert_eq!(*addr, RewardsAddress::new([MEDIAN_INDEX as u8 + 10; 20])); assert_eq!(*amt, Amount::from(300)); } #[test] fn payment_intent_from_multiple_chunks() { - let c1 = make_prepared_chunk([0, 0, 100, 0, 0]); - let c2 = make_prepared_chunk([0, 0, 250, 0, 0]); + let c1 = make_prepared_chunk(100); + let c2 = make_prepared_chunk(250); let intent = PaymentIntent::from_prepared_chunks(&[c1, c2]); assert_eq!(intent.payments.len(), 2); @@ -499,7 +466,7 @@ mod tests { #[test] fn payment_intent_skips_all_zero_chunks() { - let chunk = make_prepared_chunk([0, 0, 0, 0, 0]); + let chunk = make_prepared_chunk(0); let intent = PaymentIntent::from_prepared_chunks(&[chunk]); assert!(intent.payments.is_empty()); @@ -515,8 +482,8 @@ mod tests { #[test] fn finalize_batch_payment_builds_proofs() { - let chunk = make_prepared_chunk([0, 0, 500, 0, 0]); - let quote_hash = chunk.payment.quotes[2].quote_hash; + let chunk = make_prepared_chunk(500); + let quote_hash = chunk.payment.quotes[MEDIAN_INDEX].quote_hash; let mut tx_map = HashMap::new(); tx_map.insert(quote_hash, TxHash::from([0xBB; 32])); @@ -538,7 +505,7 @@ mod tests { fn finalize_batch_payment_missing_tx_hash_errors() { // Missing tx hash for a non-zero-amount quote should error, // since the chunk would be rejected by the network without a valid proof. - let chunk = make_prepared_chunk([0, 0, 500, 0, 0]); + let chunk = make_prepared_chunk(500); let result = finalize_batch_payment(vec![chunk], &HashMap::new()); assert!(result.is_err()); @@ -548,9 +515,9 @@ mod tests { #[test] fn finalize_batch_payment_multiple_chunks() { - let c1 = make_prepared_chunk([0, 0, 100, 0, 0]); - let c2 = make_prepared_chunk([0, 0, 200, 0, 0]); - let q1 = c1.payment.quotes[2].quote_hash; + let c1 = make_prepared_chunk(100); + let c2 = make_prepared_chunk(200); + let q1 = c1.payment.quotes[MEDIAN_INDEX].quote_hash; let mut tx_map = HashMap::new(); // Both chunks have the same quote_hash (same index/byte pattern) // so one tx_hash covers both diff --git a/ant-core/src/data/client/merkle.rs b/ant-core/src/data/client/merkle.rs index 0b28841..95c912a 100644 --- a/ant-core/src/data/client/merkle.rs +++ b/ant-core/src/data/client/merkle.rs @@ -376,12 +376,8 @@ impl Client { candidate_futures.push(fut); } - self.collect_validated_candidates( - &mut candidate_futures, - merkle_payment_timestamp, - data_type, - ) - .await + self.collect_validated_candidates(&mut candidate_futures, merkle_payment_timestamp) + .await } /// Collect and validate merkle candidate responses until we have enough. @@ -396,7 +392,6 @@ impl Client { >, >, merkle_payment_timestamp: u64, - expected_data_type: u32, ) -> Result<[MerklePaymentCandidateNode; CANDIDATES_PER_POOL]> { let mut candidates = Vec::with_capacity(CANDIDATES_PER_POOL); let mut failures: Vec = Vec::new(); @@ -414,14 +409,6 @@ impl Client { failures.push(format!("{peer_id}: timestamp mismatch")); continue; } - if candidate.quoting_metrics.data_type != expected_data_type { - warn!( - "Data type mismatch from {peer_id}: expected {expected_data_type}, got {}", - candidate.quoting_metrics.data_type - ); - failures.push(format!("{peer_id}: wrong data_type")); - continue; - } candidates.push(candidate); if candidates.len() >= CANDIDATES_PER_POOL { break; @@ -633,8 +620,8 @@ mod tests { #[test] fn test_merkle_proof_serialize_deserialize_roundtrip() { use ant_node::payment::{deserialize_merkle_proof, serialize_merkle_proof}; + use evmlib::common::Amount; use evmlib::merkle_payments::MerklePaymentCandidateNode; - use evmlib::quoting_metrics::QuotingMetrics; use evmlib::RewardsAddress; let addrs = make_test_addresses(4); @@ -654,17 +641,7 @@ mod tests { let candidate_nodes: [MerklePaymentCandidateNode; CANDIDATES_PER_POOL] = std::array::from_fn(|i| MerklePaymentCandidateNode { pub_key: vec![i as u8; 32], - quoting_metrics: QuotingMetrics { - data_size: 1024, - data_type: 0, - close_records_stored: 0, - records_per_type: vec![], - max_records: 100, - received_payment_count: 0, - live_time: 0, - network_density: None, - network_size: None, - }, + price: Amount::from(1024u64), reward_address: RewardsAddress::new([i as u8; 20]), merkle_payment_timestamp: timestamp, signature: vec![i as u8; 64], @@ -702,17 +679,7 @@ mod tests { // Simulates what collect_validated_candidates checks let candidate = MerklePaymentCandidateNode { pub_key: vec![0u8; 32], - quoting_metrics: evmlib::quoting_metrics::QuotingMetrics { - data_size: 0, - data_type: 0, - close_records_stored: 0, - records_per_type: vec![], - max_records: 0, - received_payment_count: 0, - live_time: 0, - network_density: None, - network_size: None, - }, + price: evmlib::common::Amount::ZERO, reward_address: evmlib::RewardsAddress::new([0u8; 20]), merkle_payment_timestamp: 1000, signature: vec![0u8; 64], @@ -722,30 +689,6 @@ mod tests { assert_ne!(candidate.merkle_payment_timestamp, 2000); } - #[test] - fn test_candidate_wrong_data_type_rejected() { - let candidate = MerklePaymentCandidateNode { - pub_key: vec![0u8; 32], - quoting_metrics: evmlib::quoting_metrics::QuotingMetrics { - data_size: 0, - data_type: 1, // scratchpad - close_records_stored: 0, - records_per_type: vec![], - max_records: 0, - received_payment_count: 0, - live_time: 0, - network_density: None, - network_size: None, - }, - reward_address: evmlib::RewardsAddress::new([0u8; 20]), - merkle_payment_timestamp: 1000, - signature: vec![0u8; 64], - }; - - // data_type check: 1 (scratchpad) != 0 (chunk) - assert_ne!(candidate.quoting_metrics.data_type, 0); - } - // ========================================================================= // Batch splitting edge cases // ========================================================================= diff --git a/ant-core/src/data/client/mod.rs b/ant-core/src/data/client/mod.rs index eaa9fb1..bbf9e52 100644 --- a/ant-core/src/data/client/mod.rs +++ b/ant-core/src/data/client/mod.rs @@ -118,7 +118,7 @@ impl Client { /// Set the wallet for payment operations. /// /// Also populates the EVM network from the wallet so that - /// price queries work without a separate `with_evm_network` call. + /// token approvals work without a separate `with_evm_network` call. #[must_use] pub fn with_wallet(mut self, wallet: Wallet) -> Self { self.evm_network = Some(wallet.network().clone()); @@ -126,9 +126,9 @@ impl Client { self } - /// Set the EVM network for price queries without requiring a wallet. + /// Set the EVM network without requiring a wallet. /// - /// This enables operations like quote collection and cost estimation + /// This enables token approval and contract interactions /// for external-signer flows where the private key lives outside Rust. #[must_use] pub fn with_evm_network(mut self, network: evmlib::Network) -> Self { diff --git a/ant-core/src/data/client/payment.rs b/ant-core/src/data/client/payment.rs index 39ba048..651bdb6 100644 --- a/ant-core/src/data/client/payment.rs +++ b/ant-core/src/data/client/payment.rs @@ -24,7 +24,7 @@ impl Client { /// /// This orchestrates the full payment flow: /// 1. Collect `CLOSE_GROUP_SIZE` quotes from closest peers - /// 2. Build `SingleNodePayment` (median 3x, others 0) + /// 2. Build `SingleNodePayment` using node-reported prices (median 3x, others 0) /// 3. Pay on-chain via the wallet /// 4. Serialize `PaymentProof` with transaction hashes /// @@ -41,7 +41,7 @@ impl Client { data_size: u64, data_type: u32, ) -> Result<(Vec, Vec<(PeerId, Vec)>)> { - // Wallet is required for the on-chain payment step (step 5 below). + // Wallet is required for the on-chain payment step (step 4 below). // Check early so we don't waste time collecting quotes for a misconfigured client. let wallet = self.require_wallet()?; @@ -56,50 +56,24 @@ impl Client { .map(|(peer_id, addrs, _, _)| (*peer_id, addrs.clone())) .collect(); - // 2. Fetch prices from the on-chain contract rather than using the - // locally-computed estimates. The contract's getQuote() is the authoritative - // price source — the verifyPayment() call recomputes prices from QuotingMetrics - // using the same formula, so we must use matching prices. - let metrics_batch: Vec<_> = quotes_with_peers - .iter() - .map(|(_, _, quote, _)| quote.quoting_metrics.clone()) - .collect(); - - let evm_network = self.require_evm_network()?; - let contract_prices = - evmlib::contract::payment_vault::get_market_price(evm_network, metrics_batch) - .await - .map_err(|e| { - Error::Payment(format!("Failed to get market prices from contract: {e}")) - })?; - - if contract_prices.len() != quotes_with_peers.len() { - return Err(Error::Payment(format!( - "Contract returned {} prices for {} quotes", - contract_prices.len(), - quotes_with_peers.len() - ))); - } - - // 3. Build peer_quotes for ProofOfPayment + quotes for SingleNodePayment + // 2. Build peer_quotes for ProofOfPayment + quotes for SingleNodePayment. + // Use node-reported prices directly — no contract price fetch needed. let mut peer_quotes = Vec::with_capacity(quotes_with_peers.len()); let mut quotes_for_payment = Vec::with_capacity(quotes_with_peers.len()); - for ((peer_id, _addrs, quote, _local_price), contract_price) in - quotes_with_peers.into_iter().zip(contract_prices) - { + for (peer_id, _addrs, quote, price) in quotes_with_peers { let encoded = peer_id_to_encoded(&peer_id)?; peer_quotes.push((encoded, quote.clone())); - quotes_for_payment.push((quote, contract_price)); + quotes_for_payment.push((quote, price)); } - // 4. Create SingleNodePayment (sorts by price, selects median) + // 3. Create SingleNodePayment (sorts by price, selects median) let payment = SingleNodePayment::from_quotes(quotes_for_payment) .map_err(|e| Error::Payment(format!("Failed to create payment: {e}")))?; info!("Payment total: {} atto", payment.total_amount()); - // 5. Pay on-chain + // 4. Pay on-chain let tx_hashes = payment .pay(wallet) .await @@ -110,7 +84,7 @@ impl Client { tx_hashes.len() ); - // 6. Build and serialize proof with version tag + // 5. Build and serialize proof with version tag let proof = PaymentProof { proof_of_payment: ProofOfPayment { peer_quotes }, tx_hashes, @@ -134,22 +108,12 @@ impl Client { let wallet = self.require_wallet()?; let evm_network = self.require_evm_network()?; - // Approve data payments contract - let data_payments_address = evm_network.data_payments_address(); + let vault_address = evm_network.payment_vault_address(); wallet - .approve_to_spend_tokens(*data_payments_address, evmlib::common::U256::MAX) + .approve_to_spend_tokens(*vault_address, evmlib::common::U256::MAX) .await .map_err(|e| Error::Payment(format!("Token approval failed: {e}")))?; - info!("Token spend approved for data payments contract"); - - // Approve merkle payments contract (if deployed) - if let Some(merkle_address) = evm_network.merkle_payments_address() { - wallet - .approve_to_spend_tokens(*merkle_address, evmlib::common::U256::MAX) - .await - .map_err(|e| Error::Payment(format!("Merkle token approval failed: {e}")))?; - info!("Token spend approved for merkle payments contract"); - } + info!("Token spend approved for payment vault contract"); Ok(()) } diff --git a/ant-core/src/data/client/quote.rs b/ant-core/src/data/client/quote.rs index cd9a552..92c8f02 100644 --- a/ant-core/src/data/client/quote.rs +++ b/ant-core/src/data/client/quote.rs @@ -10,7 +10,6 @@ use ant_node::ant_protocol::{ }; use ant_node::client::send_and_await_chunk_response; use ant_node::core::{MultiAddr, PeerId}; -use ant_node::payment::calculate_price; use ant_node::{CLOSE_GROUP_MAJORITY, CLOSE_GROUP_SIZE}; use evmlib::common::Amount; use evmlib::PaymentQuote; @@ -107,7 +106,7 @@ impl Client { } match rmp_serde::from_slice::("e) { Ok(payment_quote) => { - let price = calculate_price(&payment_quote.quoting_metrics); + let price = payment_quote.price; debug!("Received quote from {peer_id_clone}: price = {price}"); Some(Ok((payment_quote, price))) } diff --git a/ant-core/src/node/devnet.rs b/ant-core/src/node/devnet.rs index 04755a0..9d1f012 100644 --- a/ant-core/src/node/devnet.rs +++ b/ant-core/src/node/devnet.rs @@ -48,8 +48,7 @@ impl LocalDevnet { .default_wallet_private_key() .map_err(|e| Error::Config(format!("failed to get wallet key: {e}")))?; - let (rpc_url, token_addr, payments_addr, merkle_addr) = - extract_custom_network_info(&network)?; + let (rpc_url, token_addr, vault_addr) = extract_custom_network_info(&network)?; config.evm_network = Some(network.clone()); @@ -70,8 +69,7 @@ impl LocalDevnet { rpc_url, wallet_private_key: wallet_key.clone(), payment_token_address: token_addr, - data_payments_address: payments_addr, - merkle_payments_address: merkle_addr, + payment_vault_address: vault_addr, }; let manifest = DevnetManifest { @@ -192,21 +190,16 @@ impl LocalDevnet { } } -/// Extract RPC URL, token address, payments address, and merkle address from a Custom network. -fn extract_custom_network_info( - network: &EvmNetwork, -) -> Result<(String, String, String, Option)> { +/// Extract RPC URL, token address, and payment vault address from a Custom network. +fn extract_custom_network_info(network: &EvmNetwork) -> Result<(String, String, String)> { match network { EvmNetwork::Custom(custom) => { let token = custom.payment_token_address; - let payments = custom.data_payments_address; + let vault = custom.payment_vault_address; Ok(( custom.rpc_url_http.to_string(), format!("{token:?}"), - format!("{payments:?}"), - custom - .merkle_payments_address - .map(|addr| format!("{addr:?}")), + format!("{vault:?}"), )) } _ => Err(Error::Config( diff --git a/ant-core/tests/e2e_chunk.rs b/ant-core/tests/e2e_chunk.rs index 48bc072..edbce4d 100644 --- a/ant-core/tests/e2e_chunk.rs +++ b/ant-core/tests/e2e_chunk.rs @@ -8,10 +8,10 @@ use ant_core::data::{compute_address, Client, ClientConfig}; use bytes::Bytes; use serial_test::serial; use std::sync::Arc; -use support::MiniTestnet; +use support::{MiniTestnet, DEFAULT_NODE_COUNT}; async fn setup() -> (Client, MiniTestnet) { - let testnet = MiniTestnet::start(6).await; + let testnet = MiniTestnet::start(DEFAULT_NODE_COUNT).await; let node = testnet.node(3).expect("Node 3 should exist"); let client = Client::from_node(Arc::clone(&node), ClientConfig::default()) @@ -241,7 +241,7 @@ async fn test_chunk_put_with_invalid_proof_rejected() { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_chunk_put_no_wallet_fails() { - let testnet = MiniTestnet::start(6).await; + let testnet = MiniTestnet::start(DEFAULT_NODE_COUNT).await; let node = testnet.node(3).expect("Node 3 should exist"); // Client WITHOUT wallet diff --git a/ant-core/tests/e2e_data.rs b/ant-core/tests/e2e_data.rs index ca74c67..a592678 100644 --- a/ant-core/tests/e2e_data.rs +++ b/ant-core/tests/e2e_data.rs @@ -9,10 +9,10 @@ use bytes::Bytes; use self_encryption::encrypt; use serial_test::serial; use std::sync::Arc; -use support::MiniTestnet; +use support::{MiniTestnet, DEFAULT_NODE_COUNT}; async fn setup() -> (Client, MiniTestnet) { - let testnet = MiniTestnet::start(8).await; + let testnet = MiniTestnet::start(DEFAULT_NODE_COUNT).await; let node = testnet.node(4).expect("Node 4 should exist"); let client = Client::from_node(Arc::clone(&node), ClientConfig::default()) diff --git a/ant-core/tests/e2e_file.rs b/ant-core/tests/e2e_file.rs index 295e252..546a47b 100644 --- a/ant-core/tests/e2e_file.rs +++ b/ant-core/tests/e2e_file.rs @@ -9,11 +9,11 @@ use serial_test::serial; use std::io::Write; use std::path::PathBuf; use std::sync::Arc; -use support::MiniTestnet; +use support::{MiniTestnet, DEFAULT_NODE_COUNT}; use tempfile::{NamedTempFile, TempDir}; async fn setup() -> (Client, MiniTestnet) { - let testnet = MiniTestnet::start(6).await; + let testnet = MiniTestnet::start(DEFAULT_NODE_COUNT).await; let node = testnet.node(3).expect("Node 3 should exist"); let client = Client::from_node(Arc::clone(&node), ClientConfig::default()) diff --git a/ant-core/tests/e2e_huge_file.rs b/ant-core/tests/e2e_huge_file.rs index e708ceb..6503707 100644 --- a/ant-core/tests/e2e_huge_file.rs +++ b/ant-core/tests/e2e_huge_file.rs @@ -11,7 +11,7 @@ use ant_core::data::{Client, ClientConfig}; use serial_test::serial; use std::io::Write; use std::sync::Arc; -use support::MiniTestnet; +use support::{MiniTestnet, DEFAULT_NODE_COUNT}; use tempfile::TempDir; /// Size of the 1 GB test file. @@ -26,7 +26,7 @@ const FILE_SIZE_1GB: u64 = 1024 * 1024 * 1024; const MAX_RSS_INCREASE_BYTES: u64 = 512 * 1024 * 1024; async fn setup_large() -> (Client, MiniTestnet) { - let testnet = MiniTestnet::start(6).await; + let testnet = MiniTestnet::start(DEFAULT_NODE_COUNT).await; let node = testnet.node(3).expect("Node 3 should exist"); let client = Client::from_node(Arc::clone(&node), ClientConfig::default()) diff --git a/ant-core/tests/e2e_merkle.rs b/ant-core/tests/e2e_merkle.rs index dc848f1..dd4f22b 100644 --- a/ant-core/tests/e2e_merkle.rs +++ b/ant-core/tests/e2e_merkle.rs @@ -13,7 +13,7 @@ mod support; use ant_core::data::client::merkle::PaymentMode; -use ant_core::data::{Client, ClientConfig}; +use ant_core::data::{compute_address, Client, ClientConfig}; use serial_test::serial; use std::io::Write; use std::sync::Arc; @@ -22,6 +22,9 @@ use tempfile::{NamedTempFile, TempDir}; const CLIENT_TIMEOUT_SECS: u64 = 120; +/// Chunk size for merkle security tests (small, fast to hash). +const TEST_CHUNK_SIZE: usize = 1024; + /// Create a 35-node testnet suitable for merkle payments. async fn setup_merkle_testnet() -> (Client, MiniTestnet) { eprintln!("Starting 35-node testnet..."); @@ -143,7 +146,140 @@ async fn test_merkle_data_upload_download() { testnet.teardown().await; } -// Single-node coexistence is tested in e2e_file.rs (6-node testnet). +// ─── Merkle Payment Security Tests ───────────────────────────────────────── +// +// Verify that nodes reject tampered merkle proofs. Unlike single-node payments +// where the client controls the amount, merkle payment amounts are determined +// by the smart contract. The cheating vectors for merkle are: +// - Using a proof from one chunk to store a different chunk (address mismatch) +// - Using a proof from a payment that didn't happen on-chain + +/// Use a valid merkle proof from chunk A to try storing chunk B. +/// +/// The merkle proof contains an address-binding commitment: the proof's +/// `address` field and sibling hashes bind it to a specific leaf in the tree. +/// Nodes must verify this binding rejects mismatched chunks. +#[tokio::test(flavor = "multi_thread")] +#[serial] +async fn test_attack_merkle_proof_for_wrong_chunk() { + let (client, testnet) = setup_merkle_testnet().await; + + // Create 4 small chunks for a minimal merkle tree + let chunks: Vec = (0..4u8) + .map(|i| bytes::Bytes::from(vec![i; TEST_CHUNK_SIZE])) + .collect(); + let addresses: Vec<[u8; 32]> = chunks.iter().map(|c| compute_address(c)).collect(); + + eprintln!("Paying for 4 chunks via merkle batch..."); + + // Pay for these chunks via merkle batch payment + let batch_result = client + .pay_for_merkle_batch(&addresses, 0, TEST_CHUNK_SIZE as u64) + .await + .expect("merkle batch payment should succeed"); + + assert_eq!( + batch_result.proofs.len(), + 4, + "should have proofs for all 4 chunks" + ); + + // Get the proof for chunk 0 + let proof_for_chunk_0 = batch_result + .proofs + .get(&addresses[0]) + .expect("should have proof for chunk 0") + .clone(); + + // Create a completely different chunk NOT in the merkle tree + let evil_content = bytes::Bytes::from("this content was NOT in the merkle tree"); + let evil_address = compute_address(&evil_content); + assert_ne!( + evil_address, addresses[0], + "evil chunk must have a different address" + ); + + // Find a peer close to the evil chunk's address to PUT to + let peers = client + .network() + .find_closest_peers(&evil_address, 1) + .await + .expect("should find peers"); + let (target_peer, target_addrs) = &peers[0]; + + eprintln!("Attempting PUT of wrong chunk with merkle proof for chunk 0..."); + + // Try to store the evil chunk using chunk 0's merkle proof + let result = client + .chunk_put_with_proof(evil_content, proof_for_chunk_0, target_peer, target_addrs) + .await; + + assert!( + result.is_err(), + "PUT with merkle proof for a different chunk should be rejected (address mismatch)" + ); + + drop(client); + testnet.teardown().await; +} + +/// Use a proof from chunk A to try storing chunk B where both are in the tree. +/// +/// Even when both chunks have valid merkle proofs from the same batch, the +/// proofs are NOT interchangeable — each proof binds to its specific leaf +/// via the address and sibling hash path. +#[tokio::test(flavor = "multi_thread")] +#[serial] +async fn test_attack_merkle_proof_swap_within_batch() { + let (client, testnet) = setup_merkle_testnet().await; + + let chunks: Vec = (0..4u8) + .map(|i| bytes::Bytes::from(vec![i; TEST_CHUNK_SIZE])) + .collect(); + let addresses: Vec<[u8; 32]> = chunks.iter().map(|c| compute_address(c)).collect(); + + eprintln!("Paying for 4 chunks via merkle batch..."); + + let batch_result = client + .pay_for_merkle_batch(&addresses, 0, TEST_CHUNK_SIZE as u64) + .await + .expect("merkle batch payment should succeed"); + + // Take chunk 0's proof and try to store chunk 1 with it + let proof_for_chunk_0 = batch_result + .proofs + .get(&addresses[0]) + .expect("should have proof for chunk 0") + .clone(); + + let peers = client + .network() + .find_closest_peers(&addresses[1], 1) + .await + .expect("should find peers"); + let (target_peer, target_addrs) = &peers[0]; + + eprintln!("Attempting to store chunk 1 using chunk 0's merkle proof..."); + + let result = client + .chunk_put_with_proof( + chunks[1].clone(), + proof_for_chunk_0, + target_peer, + target_addrs, + ) + .await; + + assert!( + result.is_err(), + "Swapping merkle proofs between chunks in the same batch should be rejected" + ); + + drop(client); + testnet.teardown().await; +} + +// Single-node coexistence is tested in e2e_file.rs (DEFAULT_NODE_COUNT testnet). // The 35-node testnet's DHT can have sparse XOR regions where single-node // quotes can't find 5 peers for a random chunk address, making that test // unreliable here. Merkle tests are the focus of this file. diff --git a/ant-core/tests/e2e_payment.rs b/ant-core/tests/e2e_payment.rs index 5709d84..e999aa6 100644 --- a/ant-core/tests/e2e_payment.rs +++ b/ant-core/tests/e2e_payment.rs @@ -11,10 +11,10 @@ use ant_core::data::{compute_address, Client, ClientConfig}; use bytes::Bytes; use serial_test::serial; use std::sync::Arc; -use support::MiniTestnet; +use support::{MiniTestnet, DEFAULT_NODE_COUNT}; async fn setup() -> (Client, MiniTestnet) { - let testnet = MiniTestnet::start(6).await; + let testnet = MiniTestnet::start(DEFAULT_NODE_COUNT).await; let node = testnet.node(3).expect("Node 3 should exist"); let client = Client::from_node(Arc::clone(&node), ClientConfig::default()) .with_wallet(testnet.wallet().clone()); @@ -110,7 +110,7 @@ async fn test_concurrent_paid_uploads() { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_payment_required_enforcement() { - let testnet = MiniTestnet::start(6).await; + let testnet = MiniTestnet::start(DEFAULT_NODE_COUNT).await; let node = testnet.node(3).expect("Node 3 should exist"); // Client with wallet for the paid test @@ -286,7 +286,7 @@ async fn test_quote_collection() { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_chunk_put_fails_with_insufficient_funds() { - let testnet = MiniTestnet::start(6).await; + let testnet = MiniTestnet::start(DEFAULT_NODE_COUNT).await; let node = testnet.node(3).expect("Node 3 should exist"); // Create a wallet with zero balance by using a random private key @@ -318,11 +318,11 @@ async fn test_chunk_put_fails_with_insufficient_funds() { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_payment_flow_with_node_failure() { - let mut testnet = MiniTestnet::start(6).await; + let mut testnet = MiniTestnet::start(DEFAULT_NODE_COUNT).await; - // With a 6-node testnet and CLOSE_GROUP_SIZE = 5, we need exactly 5 - // remote peers. The client's own node is excluded from quote collection, - // so killing even one node leaves only 4 remote peers — not enough. + // We need CLOSE_GROUP_SIZE remote peers for quote collection. The + // client's own node is excluded, so with DEFAULT_NODE_COUNT nodes + // killing one leaves only CLOSE_GROUP_SIZE - 1 remote peers — not enough. // // Instead, verify the payment flow survives a brief disruption: shut // down a node, wait, then bring it back by verifying the remaining @@ -341,7 +341,11 @@ async fn test_payment_flow_with_node_failure() { // Shut down node 5 testnet.shutdown_node(5); let remaining = testnet.running_node_count(); - assert_eq!(remaining, 5, "Should have 5 nodes after shutting down 1"); + assert_eq!( + remaining, + DEFAULT_NODE_COUNT - 1, + "Should have one fewer node after shutting down 1" + ); tokio::time::sleep(std::time::Duration::from_secs(5)).await; diff --git a/ant-core/tests/e2e_security.rs b/ant-core/tests/e2e_security.rs index cbb0ca3..072bff7 100644 --- a/ant-core/tests/e2e_security.rs +++ b/ant-core/tests/e2e_security.rs @@ -11,14 +11,14 @@ use ant_core::data::{compute_address, Client, ClientConfig}; use ant_node::core::PeerId; use ant_node::payment::{serialize_single_node_proof, PaymentProof, SingleNodePayment}; use bytes::Bytes; -use evmlib::common::TxHash; -use evmlib::{EncodedPeerId, ProofOfPayment}; +use evmlib::common::{Amount, TxHash}; +use evmlib::{EncodedPeerId, ProofOfPayment, RewardsAddress}; use serial_test::serial; use std::sync::Arc; -use support::MiniTestnet; +use support::{MiniTestnet, DEFAULT_NODE_COUNT, MEDIAN_QUOTE_INDEX}; async fn setup() -> (Client, MiniTestnet) { - let testnet = MiniTestnet::start(6).await; + let testnet = MiniTestnet::start(DEFAULT_NODE_COUNT).await; let node = testnet.node(3).expect("Node 3 should exist"); let client = Client::from_node(Arc::clone(&node), ClientConfig::default()) .with_wallet(testnet.wallet().clone()); @@ -339,7 +339,7 @@ async fn test_attack_corrupted_public_key() { #[tokio::test(flavor = "multi_thread")] #[serial] async fn test_attack_client_without_wallet() { - let testnet = MiniTestnet::start(6).await; + let testnet = MiniTestnet::start(DEFAULT_NODE_COUNT).await; let node = testnet.node(3).expect("Node 3 should exist"); // Create client WITHOUT wallet @@ -359,3 +359,172 @@ async fn test_attack_client_without_wallet() { drop(client); testnet.teardown().await; } + +// ─── Test 9: Underpayment — Single Node ───────────────────────────────────── +// +// Collects real quotes, builds a valid SingleNodePayment, then tampers with +// the median quote's amount (reducing it to 1 atto). Pays on-chain with the +// reduced amount. The node's on-chain verifyPayment check should detect that +// the paid amount is far below the expected 3× median price and reject the PUT. + +#[tokio::test(flavor = "multi_thread")] +#[serial] +async fn test_attack_underpayment_single_node() { + let (client, testnet) = setup().await; + + let content = Bytes::from("underpayment attack: client pays too little"); + let address = compute_address(&content); + let data_size = content.len() as u64; + + // 1. Collect real quotes from close group + let quotes = client + .get_store_quotes(&address, data_size, 0) + .await + .expect("quote collection should succeed"); + + // Save (PeerId, RewardsAddress) mapping before consuming quotes — needed + // to target the median peer after from_quotes() sorts internally. + let peer_by_rewards: Vec<(PeerId, RewardsAddress)> = quotes + .iter() + .map(|(pid, _, q, _)| (*pid, q.rewards_address)) + .collect(); + + // 2. Build SingleNodePayment normally (sorts by price, median gets 3×) + let mut peer_quotes = Vec::with_capacity(quotes.len()); + let mut quotes_for_payment = Vec::with_capacity(quotes.len()); + for (peer_id, _addrs, quote, price) in quotes { + let encoded = EncodedPeerId::new(*peer_id.as_bytes()); + peer_quotes.push((encoded, quote.clone())); + quotes_for_payment.push((quote, price)); + } + + let mut payment = SingleNodePayment::from_quotes(quotes_for_payment) + .expect("payment creation should succeed"); + + // Target the median peer specifically — it's the only one that receives + // payment, so only it will detect the amount mismatch in verifyPayment(). + let original_amount = payment.quotes[MEDIAN_QUOTE_INDEX].amount; + let median_rewards = payment.quotes[MEDIAN_QUOTE_INDEX].rewards_address; + let target_peer = peer_by_rewards + .iter() + .find(|(_, addr)| *addr == median_rewards) + .expect("median rewards address must match a quoted peer") + .0; + assert!( + !original_amount.is_zero(), + "Median quote should have non-zero payment amount" + ); + + // 3. Tamper: reduce median payment to 1 atto (should be 3× median price) + payment.quotes[MEDIAN_QUOTE_INDEX].amount = Amount::from(1u64); + + // 4. Pay on-chain with the reduced amount — the contract records whatever + // amount is sent, it only validates amounts in verifyPayment() + let wallet = client.wallet().expect("wallet should be set"); + let tx_hashes = payment + .pay(wallet) + .await + .expect("on-chain payment should succeed (contract accepts any amount in payForQuotes)"); + + assert!( + !tx_hashes.is_empty(), + "Should have at least one tx hash for the 1-atto payment" + ); + + // 5. Build proof with real ML-DSA-65 signed quotes but underpaid tx + let proof = PaymentProof { + proof_of_payment: ProofOfPayment { peer_quotes }, + tx_hashes, + }; + let proof_bytes = serialize_single_node_proof(&proof).expect("serialize proof"); + + // 6. PUT should be REJECTED — node's verifyPayment detects 1 atto < 3× expected + let result = client + .chunk_put_with_proof(content, proof_bytes, &target_peer, &[]) + .await; + + assert!( + result.is_err(), + "PUT with underpayment (1 atto instead of {original_amount}) should be rejected" + ); + + drop(client); + testnet.teardown().await; +} + +// ─── Test 10: Underpayment — Pay Half the Required Amount ─────────────────── +// +// Verifies the boundary more closely: pays roughly half the expected 3× median +// price. The contract should still reject this because the amount is below the +// 3× threshold, even though it's not trivially small like 1 atto. + +#[tokio::test(flavor = "multi_thread")] +#[serial] +async fn test_attack_underpayment_half_price() { + let (client, testnet) = setup().await; + + let content = Bytes::from("half-price underpayment attack test data"); + let address = compute_address(&content); + let data_size = content.len() as u64; + + let quotes = client + .get_store_quotes(&address, data_size, 0) + .await + .expect("quote collection should succeed"); + + let peer_by_rewards: Vec<(PeerId, RewardsAddress)> = quotes + .iter() + .map(|(pid, _, q, _)| (*pid, q.rewards_address)) + .collect(); + + let mut peer_quotes = Vec::with_capacity(quotes.len()); + let mut quotes_for_payment = Vec::with_capacity(quotes.len()); + for (peer_id, _addrs, quote, price) in quotes { + let encoded = EncodedPeerId::new(*peer_id.as_bytes()); + peer_quotes.push((encoded, quote.clone())); + quotes_for_payment.push((quote, price)); + } + + let mut payment = SingleNodePayment::from_quotes(quotes_for_payment) + .expect("payment creation should succeed"); + + // Halve the median payment (3× → ~1.5×). + // Target the median peer — only it verifies the amount it received. + let original_amount = payment.quotes[MEDIAN_QUOTE_INDEX].amount; + let median_rewards = payment.quotes[MEDIAN_QUOTE_INDEX].rewards_address; + let target_peer = peer_by_rewards + .iter() + .find(|(_, addr)| *addr == median_rewards) + .expect("median rewards address must match a quoted peer") + .0; + let half_amount = original_amount / Amount::from(2u64); + assert!( + !half_amount.is_zero(), + "Half of original amount should still be non-zero" + ); + payment.quotes[MEDIAN_QUOTE_INDEX].amount = half_amount; + + let wallet = client.wallet().expect("wallet should be set"); + let tx_hashes = payment + .pay(wallet) + .await + .expect("on-chain payment should succeed"); + + let proof = PaymentProof { + proof_of_payment: ProofOfPayment { peer_quotes }, + tx_hashes, + }; + let proof_bytes = serialize_single_node_proof(&proof).expect("serialize proof"); + + let result = client + .chunk_put_with_proof(content, proof_bytes, &target_peer, &[]) + .await; + + assert!( + result.is_err(), + "PUT with half-price payment ({half_amount} instead of {original_amount}) should be rejected" + ); + + drop(client); + testnet.teardown().await; +} diff --git a/ant-core/tests/support/mod.rs b/ant-core/tests/support/mod.rs index 5748a61..166a130 100644 --- a/ant-core/tests/support/mod.rs +++ b/ant-core/tests/support/mod.rs @@ -24,6 +24,7 @@ use ant_node::payment::{ QuotingMetricsTracker, }; use ant_node::storage::{AntProtocol, LmdbStorage, LmdbStorageConfig}; +use ant_node::CLOSE_GROUP_SIZE; use evmlib::testnet::Testnet; use evmlib::wallet::Wallet; use evmlib::Network as EvmNetwork; @@ -40,12 +41,17 @@ const BOOTSTRAP_COUNT: usize = 2; const SPAWN_DELAY_MS: u64 = 200; const STABILIZATION_TIMEOUT_SECS: u64 = 180; +/// Default node count for standard E2E tests. Must exceed `CLOSE_GROUP_SIZE` +/// so that quote collection can find enough peers. +pub const DEFAULT_NODE_COUNT: usize = CLOSE_GROUP_SIZE + 1; + +/// Index of the median quote in a `SingleNodePayment` quotes array. +pub const MEDIAN_QUOTE_INDEX: usize = CLOSE_GROUP_SIZE / 2; + /// Test rewards address (20 bytes, all 0x01). const TEST_REWARDS_ADDRESS: [u8; 20] = [0x01; 20]; /// Max records for quoting metrics. const TEST_MAX_RECORDS: usize = 1280; -/// Initial records for quoting metrics. -const TEST_INITIAL_RECORDS: usize = 1000; pub struct TestNode { pub p2p_node: Option>, @@ -65,7 +71,7 @@ pub struct MiniTestnet { impl MiniTestnet { /// Start a testnet with the given number of nodes. /// - /// Use 6 for standard tests, 35+ for merkle tests (need 16 peers per pool). + /// Use `DEFAULT_NODE_COUNT` for standard tests, 35+ for merkle tests (need 16 peers per pool). pub async fn start(node_count: usize) -> Self { // Start Anvil EVM testnet FIRST let testnet = Testnet::new().await.expect("start Anvil testnet"); @@ -153,20 +159,12 @@ impl MiniTestnet { sleep(Duration::from_millis(500)).await; } - // Approve token spend for data payments contract - let data_payments_address = evm_network.data_payments_address(); + // Approve token spend for the unified payment vault contract + let vault_address = evm_network.payment_vault_address(); wallet - .approve_to_spend_tokens(*data_payments_address, evmlib::common::U256::MAX) + .approve_to_spend_tokens(*vault_address, evmlib::common::U256::MAX) .await - .expect("approve data payment token spend"); - - // Approve token spend for merkle payments contract (if deployed) - if let Some(merkle_address) = evm_network.merkle_payments_address() { - wallet - .approve_to_spend_tokens(*merkle_address, evmlib::common::U256::MAX) - .await - .expect("approve merkle payment token spend"); - } + .expect("approve payment vault token spend"); Self { nodes, @@ -225,8 +223,8 @@ impl MiniTestnet { let storage_config = LmdbStorageConfig { root_dir: data_dir.to_path_buf(), verify_on_read: true, - max_chunks: 0, max_map_size: 0, + disk_reserve: 0, }; let storage = Arc::new( LmdbStorage::new(storage_config) @@ -250,7 +248,7 @@ impl MiniTestnet { local_rewards_address: rewards_address, }; let payment_verifier = Arc::new(PaymentVerifier::new(payment_config)); - let metrics_tracker = QuotingMetricsTracker::new(TEST_MAX_RECORDS, TEST_INITIAL_RECORDS); + let metrics_tracker = QuotingMetricsTracker::new(TEST_MAX_RECORDS); let mut quote_generator = QuoteGenerator::new(rewards_address, metrics_tracker); // Wire ML-DSA-65 signing so quotes are properly signed and verifiable