diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 989bfb6d7..c3ddaf3a1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -7,14 +7,20 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - job_args: [api, config, controller, impls, libwallet, .] + job_args: [api, config, controller, impls, libwallet, integration, .] steps: - uses: actions/checkout@v6 with: submodules: recursive - name: Test ${{ matrix.job_args }} working-directory: ${{ matrix.job_args }} - run: cargo test --release + # Live node+wallet tests need serial execution (fixed ports and data directories). + run: | + if [ "${{ matrix.job_args }}" = "integration" ]; then + cargo test --release -- --test-threads=1 + else + cargo test --release + fi macos-tests: name: macOS Tests diff --git a/Cargo.lock b/Cargo.lock index 7587e35e6..6eef07a01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,7 +58,7 @@ dependencies = [ "nom", "pin-project", "rand 0.7.3", - "rand 0.8.6", + "rand 0.8.7", "rust-embed", "scrypt", "sha2 0.10.9", @@ -80,7 +80,7 @@ dependencies = [ "hkdf", "io_tee", "nom", - "rand 0.8.6", + "rand 0.8.7", "secrecy", "sha2 0.10.9", ] @@ -115,8 +115,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a6309e6b8d89b36b9f959b7a8fa093583b94922a0f6438a24fb08936de4d428" dependencies = [ "amplify_syn", - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", "syn 1.0.109", ] @@ -135,8 +135,8 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7736fb8d473c0d83098b5bac44df6a561e20470375cd8bcae30516dc889fd62a" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", "syn 1.0.109", ] @@ -160,9 +160,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "arc-swap" @@ -213,7 +213,7 @@ dependencies = [ "derive_more", "educe", "fs-mistrust", - "futures 0.3.32", + "futures 0.3.33", "hostname-validator", "humantime 2.4.0", "humantime-serde", @@ -224,7 +224,7 @@ dependencies = [ "safelog", "serde", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.19", "time", "tor-async-utils", "tor-basic-utils", @@ -273,7 +273,7 @@ dependencies = [ "nom", "num-traits 0.2.19", "rusticata-macros", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -282,9 +282,9 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", "synstructure", ] @@ -294,9 +294,9 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -307,9 +307,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" +checksum = "3976abdc8fe7d1133d43d304afd42abdf5bc3e1319d263d223bde07b5efc4be8" dependencies = [ "compression-codecs", "compression-core", @@ -325,19 +325,41 @@ checksum = "37dd6b179962fe4048a6f81d4c0d7ed419a21fdf49204b4c6b04971693358e79" dependencies = [ "futures-util", "native-tls", - "thiserror 2.0.18", + "thiserror 2.0.19", "url", ] +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", +] + [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 3.0.3", ] [[package]] @@ -361,7 +383,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233" dependencies = [ - "bytes 1.12.0", + "bytes 1.12.1", "futures-sink", "futures-util", "memchr", @@ -530,9 +552,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" dependencies = [ "serde_core", ] @@ -576,9 +598,9 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0b121a9fe0df916e362fb3271088d071159cdf11db0e4182d02152850756eff" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -637,9 +659,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.3" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79" +checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530" dependencies = [ "memchr", "regex-automata", @@ -675,9 +697,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" [[package]] name = "byteorder" @@ -693,9 +715,9 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "bytes" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "caret" @@ -705,9 +727,9 @@ checksum = "2bb840a063107846780a14acc022b55bf6c174997a393ffde8b58ff6630c1b56" [[package]] name = "cc" -version = "1.2.66" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" +checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" dependencies = [ "find-msvc-tools", "jobserver", @@ -863,15 +885,6 @@ version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -905,7 +918,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" dependencies = [ - "futures 0.3.32", + "futures 0.3.33", ] [[package]] @@ -982,9 +995,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -992,27 +1005,27 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crypto-bigint" @@ -1135,9 +1148,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -1178,8 +1191,8 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", "strsim 0.10.0", "syn 1.0.109", ] @@ -1192,9 +1205,9 @@ checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -1204,10 +1217,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ "ident_case", - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", "strsim 0.11.1", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1217,7 +1230,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core 0.14.4", - "quote 1.0.46", + "quote 1.0.47", "syn 1.0.109", ] @@ -1228,8 +1241,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", - "quote 1.0.46", - "syn 2.0.118", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -1239,8 +1252,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core 0.23.0", - "quote 1.0.46", - "syn 2.0.118", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -1298,9 +1311,9 @@ dependencies = [ [[package]] name = "derive-deftly" -version = "1.11.3" +version = "1.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bc153c91ebf221a2e7feb166aee259acf8a00ecf35c83df8b79fe8f5c3861d7" +checksum = "98e02cd118058400797c74550dca132b57cf2e6af0e9ea0672a83eb186fd655e" dependencies = [ "derive-deftly-macros", "heck 0.5.0", @@ -1308,19 +1321,19 @@ dependencies = [ [[package]] name = "derive-deftly-macros" -version = "1.11.3" +version = "1.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1747ed5fb4ab3a9f8253da3401efe7ca8f8e5ec373b2e700ff55c3304d84e31f" +checksum = "ba91f619216e76a5eb2515783f7ec2e271938b51aadac592f4930517f4626863" dependencies = [ "heck 0.5.0", "indexmap 2.14.0", "itertools 0.15.0", "proc-macro-crate", - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", "sha3 0.12.0", "strum 0.28.0", - "syn 2.0.118", + "syn 3.0.3", "unicode-ident", "void", ] @@ -1332,8 +1345,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24c1b715c79be6328caa9a5e1a387a196ea503740f0722ec3dd8f67a9e72314d" dependencies = [ "darling 0.14.4", - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", "syn 1.0.109", ] @@ -1372,10 +1385,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case", - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", "rustc_version", - "syn 2.0.118", + "syn 2.0.119", "unicode-xid 0.2.6", ] @@ -1519,13 +1532,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 3.0.3", ] [[package]] @@ -1621,16 +1634,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", "syn 1.0.109", ] [[package]] name = "either" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" [[package]] name = "elliptic-curve" @@ -1680,9 +1693,9 @@ checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" dependencies = [ "num-bigint 0.4.8", "num-traits 0.2.19", - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -1692,9 +1705,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" dependencies = [ "once_cell", - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -1708,9 +1721,9 @@ dependencies = [ [[package]] name = "enumset" -version = "1.1.13" +version = "1.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "839c4174b41e75c8f7306110b2c51996a293b8d1d850edd529011841d9fede7d" +checksum = "ccc5801fd11762e24d1e420d01d2ac518f2a2ca4329d4fbb6639f2412b6204e0" dependencies = [ "enumset_derive", ] @@ -1722,9 +1735,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bd536557b58c682b217b8fb199afdff47cd3eff260623f19e77074eb073d63a" dependencies = [ "darling 0.21.3", - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -1768,11 +1781,10 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.4.1" +version = "5.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" dependencies = [ - "concurrent-queue", "parking", "pin-project-lite", ] @@ -1791,9 +1803,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "fd-lock" @@ -1979,11 +1991,21 @@ dependencies = [ "libc", "pwd-grp", "serde", - "thiserror 2.0.18", + "thiserror 2.0.19", "void", "walkdir", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "fslock-guard" version = "0.8.0" @@ -1991,7 +2013,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b63307a37c3d90d55a90eefa5441ec12cbd291ff456b6bf1126656c5bc43b058" dependencies = [ "libc", - "thiserror 2.0.18", + "thiserror 2.0.19", "windows-sys 0.61.2", ] @@ -2015,9 +2037,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" dependencies = [ "futures-channel", "futures-core", @@ -2030,9 +2052,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" dependencies = [ "futures-core", "futures-sink", @@ -2044,7 +2066,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcad91f8ba72542347d5cc8ee43891abe4698429fa7b3de56f17f4bb6fa514c4" dependencies = [ - "futures 0.3.32", + "futures 0.3.33", "libc", "pin-project", "windows-sys 0.61.2", @@ -2052,15 +2074,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" dependencies = [ "futures-core", "futures-task", @@ -2069,38 +2091,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" dependencies = [ "futures-channel", "futures-core", @@ -2191,9 +2213,9 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6cf442baaabe4213ce7d1239afc26c039180b6456da2cededa316ae2c8a77a77" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -2208,7 +2230,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddddbf932745a6be37109b6112d3ee09696106f848449069d3a57bba937ab82e" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "libc", "libgit2-sys", "log", @@ -2224,9 +2246,9 @@ checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d" name = "grin_api" version = "5.5.1-alpha.0" dependencies = [ - "bytes 1.12.0", + "bytes 1.12.1", "easy-jsonrpc-mw", - "futures 0.3.32", + "futures 0.3.33", "grin_chain", "grin_core", "grin_p2p", @@ -2378,6 +2400,37 @@ dependencies = [ "zeroize", ] +[[package]] +name = "grin_servers" +version = "5.5.1-alpha.0" +dependencies = [ + "async-stream", + "chrono", + "fs2", + "futures 0.3.33", + "grin_api", + "grin_chain", + "grin_core", + "grin_keychain", + "grin_p2p", + "grin_pool", + "grin_store", + "grin_util", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "log", + "millisecond", + "rand 0.6.5", + "serde", + "serde_derive", + "serde_json", + "tokio", + "tokio-util", + "walkdir", +] + [[package]] name = "grin_store" version = "5.5.1-alpha.0" @@ -2527,12 +2580,12 @@ dependencies = [ "arti-client", "base64 0.12.3", "blake2-rfc", - "bytes 1.12.0", + "bytes 1.12.1", "chrono", "ed25519-dalek", "fs-mistrust", "fslock-guard", - "futures 0.3.32", + "futures 0.3.33", "grin_api", "grin_chain", "grin_core", @@ -2567,6 +2620,26 @@ dependencies = [ "url", ] +[[package]] +name = "grin_wallet_integration" +version = "5.4.1" +dependencies = [ + "grin_api", + "grin_core", + "grin_keychain", + "grin_p2p", + "grin_servers", + "grin_util", + "grin_wallet_api", + "grin_wallet_config", + "grin_wallet_controller", + "grin_wallet_impls", + "grin_wallet_libwallet", + "log", + "serde_json", + "tokio", +] + [[package]] name = "grin_wallet_libwallet" version = "5.4.1" @@ -2647,7 +2720,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" dependencies = [ "atomic-waker", - "bytes 1.12.0", + "bytes 1.12.1", "fnv", "futures-core", "futures-sink", @@ -2725,7 +2798,7 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad82d6598ccf1dac15c8b758a1bd282b755b6776be600429176757190a1b0202" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "byteorder", "heed-traits", "heed-types", @@ -2814,31 +2887,31 @@ checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" [[package]] name = "http" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ - "bytes 1.12.0", + "bytes 1.12.1", "itoa", ] [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ - "bytes 1.12.0", + "bytes 1.12.1", "http", ] [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" dependencies = [ - "bytes 1.12.0", + "bytes 1.12.1", "futures-core", "http", "http-body", @@ -2884,21 +2957,21 @@ dependencies = [ [[package]] name = "hybrid-array" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" dependencies = [ "typenum", ] [[package]] name = "hyper" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" dependencies = [ "atomic-waker", - "bytes 1.12.0", + "bytes 1.12.1", "futures-channel", "futures-core", "h2", @@ -2949,7 +3022,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ - "bytes 1.12.0", + "bytes 1.12.1", "http-body-util", "hyper", "hyper-util", @@ -2966,7 +3039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64 0.22.1", - "bytes 1.12.0", + "bytes 1.12.1", "futures-channel", "futures-util", "http", @@ -3033,10 +3106,10 @@ dependencies = [ "i18n-embed", "lazy_static", "proc-macro-error", - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", "strsim 0.10.0", - "syn 2.0.118", + "syn 2.0.119", "unic-langid", ] @@ -3048,9 +3121,9 @@ checksum = "0f2cc0e0523d1fe6fc2c6f66e5038624ea8091b3e7748b5e8e0c84b1698db6c2" dependencies = [ "find-crate", "i18n-config", - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -3221,20 +3294,20 @@ dependencies = [ [[package]] name = "inotify" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533e68a5842e734946fe159fb03fc9bbbb254f590dd0d8ad321ae5ff7beca2c1" +checksum = "153be1941a183ec9ccd095ddbe17a8b8d435ef6c76e9e02451b933c3999af2c8" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "inotify-sys", "libc", ] [[package]] name = "inotify-sys" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea94e891b3606826e9c998be69ddca42247dad8ad50b1649a5cb7e1c9ae06fd" +checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d" dependencies = [ "libc", ] @@ -3284,9 +3357,9 @@ checksum = "4b3f7cef34251886990511df1c61443aa928499d598a9473929ab5a90a527304" [[package]] name = "ipnet" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" [[package]] name = "is-terminal" @@ -3325,11 +3398,11 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] @@ -3402,7 +3475,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "libc", ] @@ -3417,15 +3490,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libgit2-sys" -version = "0.18.5+1.9.4" +version = "0.18.7+1.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005d6ae6eac1912906073e069f7db60b1fa98e052a68227824afe3e3a1c59ca2" +checksum = "23c7391e4b9f4ffab1a624223cc1d7385ff9a678f490768add717de7ea2f4d89" dependencies = [ "cc", "libc", @@ -3579,12 +3652,12 @@ dependencies = [ "log-mdc", "mock_instant", "parking_lot", - "rand 0.9.4", + "rand 0.9.5", "serde", "serde-value", "serde_json", "serde_yaml", - "thiserror 2.0.18", + "thiserror 2.0.19", "thread-id", "typemap-ors", "unicode-segmentation", @@ -3611,9 +3684,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memmap" @@ -3655,6 +3728,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "millisecond" +version = "0.15.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b64459a911d6996e76aa2e708f88858891d98aaf1a1ab9ffb81415a33a768d79" + [[package]] name = "mime" version = "0.3.17" @@ -3679,9 +3758,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" dependencies = [ "libc", "log", @@ -3701,7 +3780,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c624fa1b7aab6bd2aff6e9b18565cc0363b6d45cbcd7465c9ed5e3740ebf097" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "libc", "nix 0.26.4", "smallstr", @@ -3789,7 +3868,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "inotify", "kqueue", "libc", @@ -3806,7 +3885,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -3873,7 +3952,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits 0.2.19", - "rand 0.8.6", + "rand 0.8.7", "smallvec", "zeroize", ] @@ -3905,11 +3984,10 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" dependencies = [ - "autocfg 1.5.1", "num-integer", "num-traits 0.2.19", ] @@ -3962,9 +4040,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -3973,7 +4051,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -4007,7 +4085,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60d6f8f565703c8a15f98b7782e39526490f8c291910d80c9be54a753a822f6f" dependencies = [ - "futures 0.3.32", + "futures 0.3.33", ] [[package]] @@ -4028,7 +4106,7 @@ version = "0.10.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cfg-if 1.0.4", "foreign-types", "libc", @@ -4042,9 +4120,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -4268,7 +4346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared 0.11.3", - "rand 0.8.6", + "rand 0.8.7", ] [[package]] @@ -4289,9 +4367,9 @@ checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ "phf_generator 0.11.3", "phf_shared 0.11.3", - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -4302,9 +4380,9 @@ checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" dependencies = [ "phf_generator 0.13.1", "phf_shared 0.13.1", - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -4340,9 +4418,9 @@ version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -4397,7 +4475,7 @@ checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" dependencies = [ "atomic 0.5.3", "crossbeam-queue", - "futures 0.3.32", + "futures 0.3.33", "parking_lot", "pin-project", "static_assertions", @@ -4468,7 +4546,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.12+spec-1.1.0", + "toml_edit 0.25.13+spec-1.1.0", ] [[package]] @@ -4478,8 +4556,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", "syn 1.0.109", "version_check", ] @@ -4490,8 +4568,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", "version_check", ] @@ -4506,9 +4584,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -4522,7 +4600,7 @@ dependencies = [ "derive-deftly", "libc", "paste", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -4548,11 +4626,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ - "proc-macro2 1.0.106", + "proc-macro2 1.0.107", ] [[package]] @@ -4630,9 +4708,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -4641,9 +4719,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -4896,7 +4974,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -4929,34 +5007,34 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] name = "ref-cast" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 3.0.3", ] [[package]] name = "regex" -version = "1.12.4" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -4966,9 +5044,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" dependencies = [ "aho-corasick", "memchr", @@ -4988,7 +5066,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" dependencies = [ "base64 0.22.1", - "bytes 1.12.0", + "bytes 1.12.1", "encoding_rs", "futures-core", "h2", @@ -5111,7 +5189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c" dependencies = [ "hashbrown 0.16.1", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -5130,7 +5208,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2b0146dd9661bf67bb107c0bb2a55064d556eeb3fc314151b957f313bcd4e" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -5169,10 +5247,10 @@ version = "6.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49b94b81e5b2c284684141a2fb9e2a31be90638caf040bf9afbc5a0416afe1ac" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", "rust-embed-utils", - "syn 2.0.118", + "syn 2.0.119", "walkdir", ] @@ -5188,9 +5266,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" [[package]] name = "rustc-hash" @@ -5228,7 +5306,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -5241,7 +5319,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys 0.12.1", @@ -5250,9 +5328,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.41" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "log", "once_cell", @@ -5286,9 +5364,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] @@ -5306,9 +5384,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "rustyline" @@ -5350,7 +5428,7 @@ dependencies = [ "educe", "either", "fluid-let", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -5415,9 +5493,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" dependencies = [ "dyn-clone", "ref-cast", @@ -5472,7 +5550,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -5495,14 +5573,14 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" dependencies = [ - "self_cell 1.2.2", + "self_cell 1.3.0", ] [[package]] name = "self_cell" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" +checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" [[package]] name = "semver" @@ -5512,9 +5590,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -5542,22 +5620,22 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 3.0.3", ] [[package]] @@ -5572,9 +5650,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -5614,7 +5692,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.14.0", "schemars 0.9.0", - "schemars 1.2.1", + "schemars 1.2.2", "serde_core", "serde_json", "serde_with_macros", @@ -5628,9 +5706,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ "darling 0.23.0", - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -5648,9 +5726,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if 1.0.4", "cpufeatures 0.2.17", @@ -5763,9 +5841,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] name = "siphasher" @@ -5804,7 +5882,7 @@ dependencies = [ "paste", "serde", "slotmap", - "thiserror 2.0.18", + "thiserror 2.0.19", "void", ] @@ -5825,9 +5903,9 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "socket2" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", "windows-sys 0.61.2", @@ -5835,9 +5913,9 @@ dependencies = [ [[package]] name = "spin" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" [[package]] name = "spki" @@ -5967,8 +6045,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", "syn 1.0.109", ] @@ -5979,9 +6057,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -6007,19 +6085,30 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2 1.0.107", + "quote 1.0.47", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.118" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", + "proc-macro2 1.0.107", + "quote 1.0.47", "unicode-ident", ] @@ -6047,9 +6136,9 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -6087,7 +6176,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -6184,11 +6273,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.19", ] [[package]] @@ -6197,20 +6286,20 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 3.0.3", ] [[package]] @@ -6225,18 +6314,18 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if 1.0.4", ] [[package]] name = "time" -version = "0.3.53" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", "js-sys", @@ -6255,9 +6344,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", @@ -6285,9 +6374,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -6300,11 +6389,11 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ - "bytes 1.12.0", + "bytes 1.12.1", "libc", "mio", "parking_lot", @@ -6317,13 +6406,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 3.0.3", ] [[package]] @@ -6348,14 +6437,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ - "bytes 1.12.0", + "bytes 1.12.1", "futures-core", "futures-io", "futures-sink", + "libc", "pin-project-lite", "tokio", ] @@ -6383,9 +6473,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ "indexmap 2.14.0", "serde_core", @@ -6393,7 +6483,7 @@ dependencies = [ "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -6430,23 +6520,23 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.12+spec-1.1.0" +version = "0.25.13+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -6457,9 +6547,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.1.1+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "tor-async-utils" @@ -6470,12 +6560,12 @@ dependencies = [ "cfg-if 1.0.4", "derive-deftly", "educe", - "futures 0.3.32", + "futures 0.3.33", "oneshot-fused-workaround", "pin-project", "postage", "sync_wrapper", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "tokio-util", "tor-basic-utils", @@ -6503,7 +6593,7 @@ dependencies = [ "serde", "slab", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.19", "tracing", "weak-table", "web-time-compat", @@ -6515,13 +6605,13 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b4833cc8ab3fca79eabc06f3ca97db102a3b7db861d77848d39932a6f0ed598" dependencies = [ - "bytes 1.12.0", + "bytes 1.12.1", "derive-deftly", "digest 0.10.7", "educe", "getrandom 0.4.3", "safelog", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-error", "tor-llcrypto", "zeroize", @@ -6534,8 +6624,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73a01cb2ac6697c12b647729d3d7db404e306d1e08a7a3aacdbee08e35170ebd" dependencies = [ "amplify", - "bitflags 2.13.0", - "bytes 1.12.0", + "bitflags 2.13.1", + "bytes 1.12.1", "caret", "derive-deftly", "derive_more", @@ -6544,7 +6634,7 @@ dependencies = [ "paste", "rand 0.10.2", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-basic-utils", "tor-bytes", "tor-cert", @@ -6569,7 +6659,7 @@ dependencies = [ "derive_more", "digest 0.10.7", "saturating-time", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-bytes", "tor-checkable", "tor-error", @@ -6590,7 +6680,7 @@ dependencies = [ "derive-deftly", "derive_more", "educe", - "futures 0.3.32", + "futures 0.3.33", "httparse", "oneshot-fused-workaround", "percent-encoding", @@ -6599,7 +6689,7 @@ dependencies = [ "safelog", "serde", "serde_with", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-async-utils", "tor-basic-utils", "tor-cell", @@ -6628,7 +6718,7 @@ checksum = "ef81835cb0b2db460c694678eae8c4cbad5f31847e473aac42605f9b945fb31a" dependencies = [ "humantime 2.4.0", "signature", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-llcrypto", "web-time-compat", ] @@ -6648,7 +6738,7 @@ dependencies = [ "downcast-rs", "dyn-clone", "educe", - "futures 0.3.32", + "futures 0.3.33", "humantime-serde", "itertools 0.14.0", "once_cell", @@ -6658,7 +6748,7 @@ dependencies = [ "retry-error", "safelog", "serde", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-async-utils", "tor-basic-utils", "tor-cell", @@ -6697,7 +6787,7 @@ dependencies = [ "either", "figment", "fs-mistrust", - "futures 0.3.32", + "futures 0.3.33", "humantime-serde", "itertools 0.14.0", "notify", @@ -6708,8 +6798,8 @@ dependencies = [ "serde-value", "serde_ignored", "strum 0.28.0", - "thiserror 2.0.18", - "toml 1.1.2+spec-1.1.0", + "thiserror 2.0.19", + "toml 1.1.4+spec-1.1.0", "tor-basic-utils", "tor-error", "tor-rtcompat", @@ -6726,7 +6816,7 @@ dependencies = [ "directories", "serde", "shellexpand", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-error", "tor-general-addr", ] @@ -6742,7 +6832,7 @@ dependencies = [ "hex", "imara-diff", "static_assertions", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-error", "tor-llcrypto", "tor-netdoc", @@ -6757,14 +6847,14 @@ dependencies = [ "async-compression", "base64ct", "derive_more", - "futures 0.3.32", + "futures 0.3.33", "hex", "http", "httparse", "httpdate", "itertools 0.14.0", "memchr", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-circmgr", "tor-error", "tor-hscrypto", @@ -6813,7 +6903,7 @@ dependencies = [ "event-listener", "fs-mistrust", "fslock-guard", - "futures 0.3.32", + "futures 0.3.33", "hex", "humantime 2.4.0", "humantime-serde", @@ -6831,7 +6921,7 @@ dependencies = [ "signature", "static_assertions", "strum 0.28.0", - "thiserror 2.0.18", + "thiserror 2.0.19", "time", "tor-async-utils", "tor-basic-utils", @@ -6862,12 +6952,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6a7ed4aee3e0613a75d420032116dac82d7702594e28566befdfa6707a8fd0e" dependencies = [ "derive_more", - "futures 0.3.32", + "futures 0.3.33", "paste", "retry-error", "static_assertions", "strum 0.28.0", - "thiserror 2.0.18", + "thiserror 2.0.19", "tracing", "void", "web-time-compat", @@ -6880,7 +6970,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfee544a33dd3e67e0311cc46ef8f667cbf9c02c7c00ef4f25098a25b2b6e1cf" dependencies = [ "derive_more", - "thiserror 2.0.18", + "thiserror 2.0.19", "void", ] @@ -6897,7 +6987,7 @@ dependencies = [ "derive_more", "dyn-clone", "educe", - "futures 0.3.32", + "futures 0.3.33", "humantime 2.4.0", "humantime-serde", "itertools 0.14.0", @@ -6909,7 +6999,7 @@ dependencies = [ "safelog", "serde", "strum 0.28.0", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-async-utils", "tor-basic-utils", "tor-config", @@ -6940,7 +7030,7 @@ dependencies = [ "derive_more", "educe", "either", - "futures 0.3.32", + "futures 0.3.33", "itertools 0.14.0", "oneshot-fused-workaround", "postage", @@ -6949,7 +7039,7 @@ dependencies = [ "safelog", "slotmap-careful", "strum 0.28.0", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-async-utils", "tor-basic-utils", "tor-bytes", @@ -6994,7 +7084,7 @@ dependencies = [ "serde", "signature", "subtle", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-basic-utils", "tor-bytes", "tor-error", @@ -7014,7 +7104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4231a55048af49ae2a8fcb2d41ea0df986b68c26bf6128f97f7f1324d47498" dependencies = [ "derive-deftly", - "futures 0.3.32", + "futures 0.3.33", "futures-copy", "itertools 0.14.0", "oneshot-fused-workaround", @@ -7023,7 +7113,7 @@ dependencies = [ "serde", "serde_with", "strum 0.28.0", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-async-utils", "tor-cell", "tor-config", @@ -7052,7 +7142,7 @@ dependencies = [ "digest 0.10.7", "educe", "fs-mistrust", - "futures 0.3.32", + "futures 0.3.33", "growable-bloom-filter", "hex", "humantime 2.4.0", @@ -7068,7 +7158,7 @@ dependencies = [ "serde", "serde_with", "strum 0.28.0", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-async-utils", "tor-basic-utils", "tor-bytes", @@ -7109,7 +7199,7 @@ dependencies = [ "rsa", "signature", "ssh-key-fork-arti", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-bytes", "tor-cert", "tor-checkable", @@ -7141,7 +7231,7 @@ dependencies = [ "serde", "signature", "ssh-key-fork-arti", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-basic-utils", "tor-bytes", "tor-config", @@ -7176,7 +7266,7 @@ dependencies = [ "serde", "serde_with", "strum 0.28.0", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-basic-utils", "tor-bytes", "tor-config", @@ -7220,7 +7310,7 @@ dependencies = [ "sha3 0.10.9", "signature", "subtle", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-error", "tor-memquota-cost", "visibility", @@ -7234,9 +7324,9 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37d0f759a875b6f9b6f3657a2626a70e2474e698a4404501033babe63ad888a6" dependencies = [ - "futures 0.3.32", + "futures 0.3.33", "humantime 2.4.0", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-error", "tor-rtcompat", "tracing", @@ -7255,7 +7345,7 @@ dependencies = [ "derive_more", "dyn-clone", "educe", - "futures 0.3.32", + "futures 0.3.33", "itertools 0.14.0", "paste", "pin-project", @@ -7263,7 +7353,7 @@ dependencies = [ "slotmap-careful", "static_assertions", "sysinfo 0.38.4", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-async-utils", "tor-basic-utils", "tor-config", @@ -7294,10 +7384,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8160580273eec3576158c3e833ff198870665dacebb373585892dde93cf9803" dependencies = [ "async-trait", - "bitflags 2.13.0", + "bitflags 2.13.1", "derive_more", "digest 0.10.7", - "futures 0.3.32", + "futures 0.3.33", "hex", "humantime 2.4.0", "itertools 0.14.0", @@ -7305,7 +7395,7 @@ dependencies = [ "rand 0.10.2", "serde", "strum 0.28.0", - "thiserror 2.0.18", + "thiserror 2.0.19", "time", "tor-basic-utils", "tor-error", @@ -7351,7 +7441,7 @@ dependencies = [ "smallvec", "strum 0.28.0", "subtle", - "thiserror 2.0.18", + "thiserror 2.0.19", "time", "tinystr", "tor-basic-utils", @@ -7382,14 +7472,14 @@ dependencies = [ "filetime", "fs-mistrust", "fslock-guard", - "futures 0.3.32", + "futures 0.3.33", "itertools 0.14.0", "oneshot-fused-workaround", "paste", "sanitize-filename", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.19", "time", "tor-async-utils", "tor-basic-utils", @@ -7409,7 +7499,7 @@ dependencies = [ "async-trait", "asynchronous-codec", "bitvec", - "bytes 1.12.0", + "bytes 1.12.1", "caret", "cfg-if 1.0.4", "cipher 0.4.4", @@ -7420,7 +7510,7 @@ dependencies = [ "digest 0.10.7", "educe", "enum_dispatch", - "futures 0.3.32", + "futures 0.3.33", "futures-util", "hkdf", "hmac 0.12.1", @@ -7437,7 +7527,7 @@ dependencies = [ "static_assertions", "strum 0.28.0", "subtle", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "tokio-util", "tor-async-utils", @@ -7474,7 +7564,7 @@ dependencies = [ "caret", "paste", "serde_with", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-basic-utils", "tor-bytes", ] @@ -7490,11 +7580,11 @@ dependencies = [ "derive-deftly", "derive_builder_fork_arti", "fs-mistrust", - "futures 0.3.32", + "futures 0.3.33", "itertools 0.14.0", "oneshot-fused-workaround", "serde", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-async-utils", "tor-basic-utils", "tor-chanmgr", @@ -7540,14 +7630,14 @@ dependencies = [ "derive_more", "dyn-clone", "educe", - "futures 0.3.32", + "futures 0.3.33", "hex", "libc", "native-tls", "paste", "pin-project", "socket2", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "tokio-util", "tor-error", @@ -7570,7 +7660,7 @@ dependencies = [ "derive-deftly", "derive_more", "educe", - "futures 0.3.32", + "futures 0.3.33", "humantime 2.4.0", "itertools 0.14.0", "oneshot-fused-workaround", @@ -7578,7 +7668,7 @@ dependencies = [ "priority-queue", "slotmap-careful", "strum 0.28.0", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-error", "tor-general-addr", "tor-rtcompat", @@ -7600,7 +7690,7 @@ dependencies = [ "educe", "safelog", "subtle", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-bytes", "tor-error", ] @@ -7614,7 +7704,7 @@ dependencies = [ "derive-deftly", "derive_more", "serde", - "thiserror 2.0.18", + "thiserror 2.0.19", "tor-memquota", ] @@ -7639,8 +7729,8 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "bitflags 2.13.0", - "bytes 1.12.0", + "bitflags 2.13.1", + "bytes 1.12.1", "futures-util", "http", "http-body", @@ -7680,9 +7770,9 @@ version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -7741,8 +7831,8 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad06847b7afb65c7866a36664b75c40b895e318cea4f71299f013fb22965329d" dependencies = [ - "quote 1.0.46", - "syn 2.0.118", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -7915,9 +8005,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.4" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -7955,9 +8045,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -8044,7 +8134,7 @@ version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ - "quote 1.0.46", + "quote 1.0.47", "wasm-bindgen-macro-support", ] @@ -8055,9 +8145,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", "wasm-bindgen-shared", ] @@ -8187,9 +8277,9 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -8198,9 +8288,9 @@ version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -8499,9 +8589,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] @@ -8552,9 +8642,9 @@ dependencies = [ [[package]] name = "xxhash-rust" -version = "0.8.16" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d93c89cdc2d3a63c3ec48ffe926931bdc069eafa8e4402fe6d8f790c9d1e576" +checksum = "aee1b19627c7c60102ab80d3a9cbe18de90bfe03bfa6c3715447681f0e8c8af6" [[package]] name = "yaml-rust" @@ -8579,30 +8669,30 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.52" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.52" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -8620,9 +8710,9 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", "synstructure", ] @@ -8641,9 +8731,9 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -8675,9 +8765,9 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ - "proc-macro2 1.0.106", - "quote 1.0.46", - "syn 2.0.118", + "proc-macro2 1.0.107", + "quote 1.0.47", + "syn 2.0.119", ] [[package]] @@ -8693,9 +8783,9 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zstd" diff --git a/Cargo.toml b/Cargo.toml index 18dfbf045..4f03e50c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,7 @@ name = "grin-wallet" path = "src/bin/grin-wallet.rs" [workspace] -members = ["api", "config", "controller", "impls", "libwallet", "util"] -exclude = ["integration"] +members = ["api", "config", "controller", "impls", "libwallet", "util", "integration"] [dependencies] clap = { version = "2.33", features = ["yaml"] } diff --git a/integration/Cargo.toml b/integration/Cargo.toml index 1230be2a3..6a15aa348 100644 --- a/integration/Cargo.toml +++ b/integration/Cargo.toml @@ -1,39 +1,30 @@ [package] -name = "grin_integration" -version = "1.1.0" +name = "grin_wallet_integration" +version = "5.4.1" authors = ["Grin Developers "] -description = "Simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format." +description = "Node + wallet multi-process integration tests for grin-wallet" license = "Apache-2.0" -repository = "https://github.com/mimblewimble/grin" -keywords = [ "crypto", "grin", "mimblewimble" ] +repository = "https://github.com/mimblewimble/grin-wallet" +keywords = ["crypto", "grin", "mimblewimble"] workspace = ".." edition = "2021" +publish = false [dependencies] -hyper = "0.12" -futures = "0.1" -http = "0.1" -itertools = "0.7" -rand = "0.5" -serde = "1" log = "0.4" -serde_derive = "1" serde_json = "1" -chrono = "0.4.4" -tokio = "0.1.11" -blake2-rfc = "0.2" -bufstream = "0.1" +tokio = { version = "1", features = ["sync"] } -#grin_apiwallet = { path = "../apiwallet", version = "1.1.0" } -#grin_libwallet = { path = "../libwallet", version = "1.1.0" } -#grin_refwallet = { path = "../refwallet", version = "1.1.0" } -#grin_wallet_config = { path = "../config", version = "1.1.0" } +grin_wallet_api = { path = "../api", version = "5.4.1" } +grin_wallet_config = { path = "../config", version = "5.4.1" } +grin_wallet_controller = { path = "../controller", version = "5.4.1" } +grin_wallet_impls = { path = "../impls", version = "5.4.1" } +grin_wallet_libwallet = { path = "../libwallet", version = "5.4.1" } -#grin_core = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } -#grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } -#grin_chain = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } -#grin_util = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } -#grin_api = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } -#grin_store = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } -#grin_p2p = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } -#grin_servers = { git = "https://github.com/mimblewimble/grin", branch = "milestone/1.1.0" } +# Same path dependencies as the rest of the workspace / staging. +grin_core = { path = "../grin/core" } +grin_keychain = { path = "../grin/keychain" } +grin_util = { path = "../grin/util" } +grin_api = { path = "../grin/api" } +grin_p2p = { path = "../grin/p2p" } +grin_servers = { path = "../grin/servers" } diff --git a/integration/README.md b/integration/README.md new file mode 100644 index 000000000..72e35573a --- /dev/null +++ b/integration/README.md @@ -0,0 +1,30 @@ +# Wallet + node integration tests + +Live **node + wallet** tests that run a real Grin node (`grin_servers` from the +pinned `grin` submodule) together with `grin-wallet` **foreign HTTP** listeners +in the same process (threaded). Coverage focuses on: + +- mining coinbase into a wallet foreign listener and reading summary via the Owner API +- two-wallet slate send/receive over the foreign HTTP path (`receive_tx` JSON-RPC) + +Owner HTTP and true multi-process (separate OS processes) coverage is not in +this crate yet; Owner calls here use the in-process Owner API while the +**foreign receive path is exercised over HTTP**. + +**Node-only** multi-server suites (seeding, propagation, body sync, stratum) are +being revived separately in open PR +[mimblewimble/grin#3910](https://github.com/mimblewimble/grin/pull/3910) +([#2957](https://github.com/mimblewimble/grin/issues/2957)). They are not yet +merged into `grin`. + +In-process wallet unit/integration coverage (mock node via `WalletProxy`) +remains under `controller/tests/`. + +## Run + +```bash +cd integration +cargo test --release -- --test-threads=1 +``` + +Serial execution avoids races on fixed ports and data directories. diff --git a/integration/src/lib.rs b/integration/src/lib.rs index 608320163..f15473aef 100644 --- a/integration/src/lib.rs +++ b/integration/src/lib.rs @@ -12,10 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Grin integration test crate - -#![deny(non_upper_case_globals)] -#![deny(non_camel_case_types)] -#![deny(non_snake_case)] -#![deny(unused_mut)] -#![warn(missing_docs)] +//! Node + wallet multi-process integration tests. +//! +//! Node-only multi-server coverage lives in the [grin](https://github.com/mimblewimble/grin) +//! `integration` crate ([#2957](https://github.com/mimblewimble/grin/issues/2957)). +//! This crate exercises wallet listeners against a real `grin_servers` node. diff --git a/integration/tests/api.rs b/integration/tests/api.rs deleted file mode 100644 index 215880541..000000000 --- a/integration/tests/api.rs +++ /dev/null @@ -1,485 +0,0 @@ -// Copyright 2021 The Grin Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#[macro_use] -extern crate log; - -mod framework; - -use self::core::global::{self, ChainTypes}; -use self::util::init_test_logger; -use self::util::Mutex; -use crate::framework::{LocalServerContainer, LocalServerContainerConfig}; -use grin_api as api; -use grin_core as core; -use grin_p2p as p2p; -use grin_util as util; -use std::sync::Arc; -use std::{thread, time}; - -#[test] -fn simple_server_wallet() { - init_test_logger(); - info!("starting simple_server_wallet"); - let _test_name_dir = "test_servers"; - core::global::set_local_chain_type(core::global::ChainTypes::AutomatedTesting); - - // Run a separate coinbase wallet for coinbase transactions - let coinbase_dir = "coinbase_wallet_api"; - framework::clean_all_output(coinbase_dir); - let mut coinbase_config = LocalServerContainerConfig::default(); - coinbase_config.name = String::from(coinbase_dir); - coinbase_config.wallet_validating_node_url = String::from("http://127.0.0.1:40001"); - coinbase_config.wallet_port = 50002; - let coinbase_wallet = Arc::new(Mutex::new( - LocalServerContainer::new(coinbase_config).unwrap(), - )); - - let _ = thread::spawn(move || { - let mut w = coinbase_wallet.lock(); - w.run_wallet(0); - }); - - // Wait for the wallet to start - thread::sleep(time::Duration::from_millis(1000)); - - let api_server_one_dir = "api_server_one"; - framework::clean_all_output(api_server_one_dir); - let mut server_config = LocalServerContainerConfig::default(); - server_config.name = String::from(api_server_one_dir); - server_config.p2p_server_port = 40000; - server_config.api_server_port = 40001; - server_config.start_miner = true; - server_config.start_wallet = false; - server_config.coinbase_wallet_address = - String::from(format!("http://{}:{}", server_config.base_addr, 50002)); - let mut server_one = LocalServerContainer::new(server_config.clone()).unwrap(); - - // Spawn server and let it run for a bit - let _ = thread::spawn(move || server_one.run_server(120)); - - //Wait for chain to build - thread::sleep(time::Duration::from_millis(5000)); - - // Starting tests - let base_addr = server_config.base_addr; - let api_server_port = server_config.api_server_port; - - warn!("Testing chain handler"); - let tip = get_tip(&base_addr, api_server_port); - assert!(tip.is_ok()); - - warn!("Testing status handler"); - let status = get_status(&base_addr, api_server_port); - assert!(status.is_ok()); - - // Be sure that at least a block is mined by Travis - let mut current_tip = get_tip(&base_addr, api_server_port).unwrap(); - while current_tip.height == 0 { - thread::sleep(time::Duration::from_millis(1000)); - current_tip = get_tip(&base_addr, api_server_port).unwrap(); - } - - warn!("Testing block handler"); - let last_block_by_height = get_block_by_height(&base_addr, api_server_port, current_tip.height); - assert!(last_block_by_height.is_ok()); - let last_block_by_height_compact = - get_block_by_height_compact(&base_addr, api_server_port, current_tip.height); - assert!(last_block_by_height_compact.is_ok()); - - let block_hash = current_tip.last_block_pushed; - let last_block_by_hash = get_block_by_hash(&base_addr, api_server_port, &block_hash); - assert!(last_block_by_hash.is_ok()); - let last_block_by_hash_compact = - get_block_by_hash_compact(&base_addr, api_server_port, &block_hash); - assert!(last_block_by_hash_compact.is_ok()); - - warn!("Testing chain output handler"); - let start_height = 0; - let end_height = current_tip.height; - let outputs_by_height = - get_outputs_by_height(&base_addr, api_server_port, start_height, end_height); - assert!(outputs_by_height.is_ok()); - let ids = get_ids_from_block_outputs(outputs_by_height.unwrap()); - let outputs_by_ids1 = get_outputs_by_ids1(&base_addr, api_server_port, ids.clone()); - assert!(outputs_by_ids1.is_ok()); - let outputs_by_ids2 = get_outputs_by_ids2(&base_addr, api_server_port, ids.clone()); - assert!(outputs_by_ids2.is_ok()); - - warn!("Testing txhashset handler"); - let roots = get_txhashset_roots(&base_addr, api_server_port); - assert!(roots.is_ok()); - let last_10_outputs = get_txhashset_lastoutputs(&base_addr, api_server_port, 0); - assert!(last_10_outputs.is_ok()); - let last_5_outputs = get_txhashset_lastoutputs(&base_addr, api_server_port, 5); - assert!(last_5_outputs.is_ok()); - let last_10_rangeproofs = get_txhashset_lastrangeproofs(&base_addr, api_server_port, 0); - assert!(last_10_rangeproofs.is_ok()); - let last_5_rangeproofs = get_txhashset_lastrangeproofs(&base_addr, api_server_port, 5); - assert!(last_5_rangeproofs.is_ok()); - let last_10_kernels = get_txhashset_lastkernels(&base_addr, api_server_port, 0); - assert!(last_10_kernels.is_ok()); - let last_5_kernels = get_txhashset_lastkernels(&base_addr, api_server_port, 5); - assert!(last_5_kernels.is_ok()); - - //let some more mining happen, make sure nothing pukes - thread::sleep(time::Duration::from_millis(5000)); -} - -/// Creates 2 servers and test P2P API -#[test] -fn test_p2p() { - init_test_logger(); - info!("starting test_p2p"); - global::set_local_chain_type(ChainTypes::AutomatedTesting); - - let _test_name_dir = "test_servers"; - - // Spawn server and let it run for a bit - let server_one_dir = "p2p_server_one"; - framework::clean_all_output(server_one_dir); - let mut server_config_one = LocalServerContainerConfig::default(); - server_config_one.name = String::from(server_one_dir); - server_config_one.p2p_server_port = 40002; - server_config_one.api_server_port = 40003; - server_config_one.start_miner = false; - server_config_one.start_wallet = false; - server_config_one.is_seeding = true; - let mut server_one = LocalServerContainer::new(server_config_one.clone()).unwrap(); - let _ = thread::spawn(move || server_one.run_server(120)); - - thread::sleep(time::Duration::from_millis(1000)); - - // Spawn server and let it run for a bit - let server_two_dir = "p2p_server_two"; - framework::clean_all_output(server_two_dir); - let mut server_config_two = LocalServerContainerConfig::default(); - server_config_two.name = String::from(server_two_dir); - server_config_two.p2p_server_port = 40004; - server_config_two.api_server_port = 40005; - server_config_two.start_miner = false; - server_config_two.start_wallet = false; - server_config_two.is_seeding = false; - let mut server_two = LocalServerContainer::new(server_config_two.clone()).unwrap(); - server_two.add_peer(format!( - "{}:{}", - server_config_one.base_addr, server_config_one.p2p_server_port - )); - let _ = thread::spawn(move || server_two.run_server(120)); - - // Let them do the handshake - thread::sleep(time::Duration::from_millis(2000)); - - // Starting tests - warn!("Starting P2P Tests"); - let base_addr = server_config_one.base_addr; - let api_server_port = server_config_one.api_server_port; - - // Check that peer all is also working - let mut peers_all = get_all_peers(&base_addr, api_server_port); - assert!(peers_all.is_ok()); - let pall = peers_all.unwrap(); - assert_eq!(pall.len(), 2); - - // Check that when we get peer connected the peer is here - let peers_connected = get_connected_peers(&base_addr, api_server_port); - assert!(peers_connected.is_ok()); - let pc = peers_connected.unwrap(); - assert_eq!(pc.len(), 1); - - // Check that the peer status is Healthy - let addr = format!( - "{}:{}", - server_config_two.base_addr, server_config_two.p2p_server_port - ); - let peer = get_peer(&base_addr, api_server_port, &addr); - assert!(peer.is_ok()); - assert_eq!(peer.unwrap().flags, p2p::State::Healthy); - - // Ban the peer - let ban_result = ban_peer(&base_addr, api_server_port, &addr); - assert!(ban_result.is_ok()); - thread::sleep(time::Duration::from_millis(2000)); - - // Check its status is banned with get peer - let peer = get_peer(&base_addr, api_server_port, &addr); - assert!(peer.is_ok()); - assert_eq!(peer.unwrap().flags, p2p::State::Banned); - - // Check from peer all - peers_all = get_all_peers(&base_addr, api_server_port); - assert!(peers_all.is_ok()); - assert_eq!(peers_all.unwrap().len(), 2); - - // Unban - let unban_result = unban_peer(&base_addr, api_server_port, &addr); - assert!(unban_result.is_ok()); - - // Check from peer connected - let peers_connected = get_connected_peers(&base_addr, api_server_port); - assert!(peers_connected.is_ok()); - assert_eq!(peers_connected.unwrap().len(), 0); - - // Check its status is healthy with get peer - let peer = get_peer(&base_addr, api_server_port, &addr); - assert!(peer.is_ok()); - assert_eq!(peer.unwrap().flags, p2p::State::Healthy); -} - -// Tip handler function -fn get_tip(base_addr: &String, api_server_port: u16) -> Result { - let url = format!("http://{}:{}/v1/chain", base_addr, api_server_port); - api::client::get::(url.as_str(), None).map_err(|e| Error::API(e)) -} - -// Status handler function -fn get_status(base_addr: &String, api_server_port: u16) -> Result { - let url = format!("http://{}:{}/v1/status", base_addr, api_server_port); - api::client::get::(url.as_str(), None).map_err(|e| Error::API(e)) -} - -// Block handler functions -fn get_block_by_height( - base_addr: &String, - api_server_port: u16, - height: u64, -) -> Result { - let url = format!( - "http://{}:{}/v1/blocks/{}", - base_addr, api_server_port, height - ); - api::client::get::(url.as_str(), None).map_err(|e| Error::API(e)) -} - -fn get_block_by_height_compact( - base_addr: &String, - api_server_port: u16, - height: u64, -) -> Result { - let url = format!( - "http://{}:{}/v1/blocks/{}?compact", - base_addr, api_server_port, height - ); - api::client::get::(url.as_str(), None).map_err(|e| Error::API(e)) -} - -fn get_block_by_hash( - base_addr: &String, - api_server_port: u16, - block_hash: &String, -) -> Result { - let url = format!( - "http://{}:{}/v1/blocks/{}", - base_addr, api_server_port, block_hash - ); - api::client::get::(url.as_str(), None).map_err(|e| Error::API(e)) -} - -fn get_block_by_hash_compact( - base_addr: &String, - api_server_port: u16, - block_hash: &String, -) -> Result { - let url = format!( - "http://{}:{}/v1/blocks/{}?compact", - base_addr, api_server_port, block_hash - ); - api::client::get::(url.as_str(), None).map_err(|e| Error::API(e)) -} - -// Chain output handler functions -fn get_outputs_by_ids1( - base_addr: &String, - api_server_port: u16, - ids: Vec, -) -> Result, Error> { - let url = format!( - "http://{}:{}/v1/chain/outputs/byids?id={}", - base_addr, - api_server_port, - ids.join(",") - ); - api::client::get::>(url.as_str(), None).map_err(|e| Error::API(e)) -} - -fn get_outputs_by_ids2( - base_addr: &String, - api_server_port: u16, - ids: Vec, -) -> Result, Error> { - let mut ids_string: String = String::from(""); - for id in ids { - ids_string = ids_string + "?id=" + &id; - } - let ids_string = String::from(&ids_string[1..ids_string.len()]); - let url = format!( - "http://{}:{}/v1/chain/outputs/byids?{}", - base_addr, api_server_port, ids_string - ); - api::client::get::>(url.as_str(), None).map_err(|e| Error::API(e)) -} - -fn get_outputs_by_height( - base_addr: &String, - api_server_port: u16, - start_height: u64, - end_height: u64, -) -> Result, Error> { - let url = format!( - "http://{}:{}/v1/chain/outputs/byheight?start_height={}&end_height={}", - base_addr, api_server_port, start_height, end_height - ); - api::client::get::>(url.as_str(), None).map_err(|e| Error::API(e)) -} - -// TxHashSet handler functions -fn get_txhashset_roots(base_addr: &String, api_server_port: u16) -> Result { - let url = format!( - "http://{}:{}/v1/txhashset/roots", - base_addr, api_server_port - ); - api::client::get::(url.as_str(), None).map_err(|e| Error::API(e)) -} - -fn get_txhashset_lastoutputs( - base_addr: &String, - api_server_port: u16, - n: u64, -) -> Result, Error> { - let url: String; - if n == 0 { - url = format!( - "http://{}:{}/v1/txhashset/lastoutputs", - base_addr, api_server_port - ); - } else { - url = format!( - "http://{}:{}/v1/txhashset/lastoutputs?n={}", - base_addr, api_server_port, n - ); - } - api::client::get::>(url.as_str(), None).map_err(|e| Error::API(e)) -} - -fn get_txhashset_lastrangeproofs( - base_addr: &String, - api_server_port: u16, - n: u64, -) -> Result, Error> { - let url: String; - if n == 0 { - url = format!( - "http://{}:{}/v1/txhashset/lastrangeproofs", - base_addr, api_server_port - ); - } else { - url = format!( - "http://{}:{}/v1/txhashset/lastrangeproofs?n={}", - base_addr, api_server_port, n - ); - } - api::client::get::>(url.as_str(), None).map_err(|e| Error::API(e)) -} - -fn get_txhashset_lastkernels( - base_addr: &String, - api_server_port: u16, - n: u64, -) -> Result, Error> { - let url: String; - if n == 0 { - url = format!( - "http://{}:{}/v1/txhashset/lastkernels", - base_addr, api_server_port - ); - } else { - url = format!( - "http://{}:{}/v1/txhashset/lastkernels?n={}", - base_addr, api_server_port, n - ); - } - api::client::get::>(url.as_str(), None).map_err(|e| Error::API(e)) -} - -// Helper function to get a vec of commitment output ids from a vec of block -// outputs -fn get_ids_from_block_outputs(block_outputs: Vec) -> Vec { - let mut ids: Vec = Vec::new(); - for block_output in block_outputs { - let outputs = &block_output.outputs; - for output in outputs { - ids.push(util::to_hex(output.clone().commit.0.to_vec())); - } - } - ids.into_iter().take(100).collect() -} - -pub fn ban_peer(base_addr: &String, api_server_port: u16, peer_addr: &String) -> Result<(), Error> { - let url = format!( - "http://{}:{}/v1/peers/{}/ban", - base_addr, api_server_port, peer_addr - ); - api::client::post_no_ret(url.as_str(), None, &"").map_err(|e| Error::API(e)) -} - -pub fn unban_peer( - base_addr: &String, - api_server_port: u16, - peer_addr: &String, -) -> Result<(), Error> { - let url = format!( - "http://{}:{}/v1/peers/{}/unban", - base_addr, api_server_port, peer_addr - ); - api::client::post_no_ret(url.as_str(), None, &"").map_err(|e| Error::API(e)) -} - -pub fn get_peer( - base_addr: &String, - api_server_port: u16, - peer_addr: &String, -) -> Result { - let url = format!( - "http://{}:{}/v1/peers/{}", - base_addr, api_server_port, peer_addr - ); - api::client::get::(url.as_str(), None).map_err(|e| Error::API(e)) -} - -pub fn get_connected_peers( - base_addr: &String, - api_server_port: u16, -) -> Result, Error> { - let url = format!( - "http://{}:{}/v1/peers/connected", - base_addr, api_server_port - ); - api::client::get::>(url.as_str(), None) - .map_err(|e| Error::API(e)) -} - -pub fn get_all_peers( - base_addr: &String, - api_server_port: u16, -) -> Result, Error> { - let url = format!("http://{}:{}/v1/peers/all", base_addr, api_server_port); - api::client::get::>(url.as_str(), None).map_err(|e| Error::API(e)) -} - -/// Error type wrapping underlying module errors. -#[derive(Debug)] -pub enum Error { - /// Error originating from HTTP API calls. - API(api::Error), -} diff --git a/integration/tests/common/mod.rs b/integration/tests/common/mod.rs new file mode 100644 index 000000000..aadb9ce12 --- /dev/null +++ b/integration/tests/common/mod.rs @@ -0,0 +1,318 @@ +// Copyright 2026 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Helpers for live node + wallet foreign-HTTP integration tests. + +use grin_api as api; +use grin_core::global::{self, ChainTypes}; +use grin_keychain::ExtKeychain; +use grin_p2p as p2p; +use grin_servers as servers; +use grin_util::{Mutex, StopState, ZeroingString}; +use grin_wallet_api::ConfigPath; +use grin_wallet_config::config::{reload_global_config, WALLET_CONFIG_FILE_NAME}; +use grin_wallet_config::{GlobalWalletConfig, TorConfig}; +use grin_wallet_controller::controller::ForeignAPIHandlerV2; +use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient}; +use grin_wallet_libwallet::slate_versions::{SlateVersion, VersionedSlate}; +use grin_wallet_libwallet::{Slate, WalletInst}; +use serde_json::{json, Value}; +use std::net::{SocketAddr, TcpStream}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use std::time::{Duration, Instant}; +use std::{fs, thread}; +use tokio::sync::mpsc; + +/// Configure AutomatedTesting for the test thread and server worker threads. +pub fn init_chain() { + global::set_local_chain_type(ChainTypes::AutomatedTesting); + global::set_global_chain_type(ChainTypes::AutomatedTesting); +} + +/// Remove leftover data from a previous run. +pub fn clean_all_output(test_name_dir: &str) { + let target_dir = format!("target/tmp/{}", test_name_dir); + if let Err(e) = fs::remove_dir_all(&target_dir) { + if Path::new(&target_dir).exists() { + println!( + "can't remove output from previous test {}: {}, may be ok", + target_dir, e + ); + } + } +} + +pub fn settle() { + thread::sleep(Duration::from_millis(500)); +} + +/// Build a node `ServerConfig` with unique ports derived from `n`. +pub fn node_config(n: u16, test_name_dir: &str) -> servers::ServerConfig { + servers::ServerConfig { + api_http_addr: format!("127.0.0.1:{}", 20000 + n), + api_secret_path: None, + foreign_api_secret_path: None, + db_root: format!("target/tmp/{}/grin-node-{}", test_name_dir, n), + p2p_config: p2p::P2PConfig { + host: std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)), + port: 10000 + n, + seeding_type: p2p::Seeding::None, + seeds: None, + ..p2p::P2PConfig::default() + }, + chain_type: ChainTypes::AutomatedTesting, + archive_mode: Some(true), + skip_sync_wait: Some(true), + run_tui: Some(false), + run_test_miner: Some(false), + stratum_mining_config: None, + ..Default::default() + } +} + +/// Start a node. Shutdown is via `Server::stop()`. +pub fn start_node(cfg: servers::ServerConfig) -> servers::Server { + let (tx, rx) = mpsc::channel::<()>(1); + servers::Server::new(cfg, None, None, (tx, rx)).expect("node starts") +} + +pub type WalletHandle = Arc< + Mutex< + Box< + dyn WalletInst< + 'static, + DefaultLCProvider, + HTTPNodeClient, + ExtKeychain, + >, + >, + >, +>; + +/// Create wallet data dir, write `grin-wallet.toml`, open wallet, register global config. +pub fn create_wallet( + test_dir: &str, + name: &str, + node_url: &str, + foreign_port: u16, +) -> ( + WalletHandle, + Option, + PathBuf, +) { + let dir = format!("target/tmp/{}/{}", test_dir, name); + fs::create_dir_all(&dir).unwrap(); + + let config_path = PathBuf::from(&dir).join(WALLET_CONFIG_FILE_NAME); + let mut global = GlobalWalletConfig::for_chain(&ChainTypes::AutomatedTesting, &config_path); + global.members.wallet.data_file_dir = dir.clone(); + global.members.wallet.check_node_api_http_addr = node_url.to_string(); + global.members.wallet.api_listen_port = foreign_port; + global.members.wallet.api_secret_path = None; + global.members.wallet.node_api_secret_path = None; + // No Tor in these tests. + global.members.tor = Some(TorConfig { + use_tor_listener: false, + skip_send_attempt: Some(true), + send_config_dir: dir.clone(), + ..TorConfig::default() + }); + global + .write_to_file(config_path.to_str().unwrap(), false, None, None) + .expect("write wallet config"); + reload_global_config(&config_path).expect("load global config"); + + let client = + HTTPNodeClient::new(node_url, None, Duration::from_secs(30)).expect("HTTPNodeClient"); + let mut wallet = Box::new(DefaultWalletImpl::::new(client).unwrap()) + as Box< + dyn WalletInst< + 'static, + DefaultLCProvider, + HTTPNodeClient, + ExtKeychain, + >, + >; + let mask = { + let lc = wallet.lc_provider().unwrap(); + lc.set_top_level_directory(&dir).unwrap(); + lc.create_wallet(None, None, 32, ZeroingString::from(""), false) + .unwrap(); + lc.open_wallet(None, ZeroingString::from(""), false, false) + .unwrap() + }; + (Arc::new(Mutex::new(wallet)), mask, config_path) +} + +/// Running foreign HTTP listener with readiness wait and clean stop. +pub struct ForeignListener { + stop_tx: mpsc::Sender<()>, + join: Option>>, + addr: String, +} + +impl ForeignListener { + pub fn addr(&self) -> &str { + &self.addr + } + + pub fn stop(mut self) { + let _ = self.stop_tx.try_send(()); + if let Some(j) = self.join.take() { + let _ = j.join(); + } + } +} + +impl Drop for ForeignListener { + fn drop(&mut self) { + let _ = self.stop_tx.try_send(()); + if let Some(j) = self.join.take() { + let _ = j.join(); + } + } +} + +/// Start a foreign HTTP listener (v2 receive_tx) without Tor. +/// +/// Reports readiness by waiting until the port accepts TCP connections, and +/// surfaces startup failures from the listener thread. Call `stop()` (or drop) +/// to shut the API server down cleanly. +pub fn start_foreign_listener( + wallet: WalletHandle, + mask: Option, + config_path: PathBuf, + addr: &str, +) -> ForeignListener { + let keychain_mask = Arc::new(Mutex::new(mask)); + let listen = addr.to_string(); + let (stop_tx, stop_rx) = mpsc::channel::<()>(1); + let stop_for_thread = stop_tx.clone(); + let (ready_tx, ready_rx) = std::sync::mpsc::channel::>(); + + let join = thread::Builder::new() + .name(format!("foreign-{}", addr)) + .spawn(move || { + let api_handler = ForeignAPIHandlerV2::new( + wallet, + ConfigPath::from(config_path), + keychain_mask, + true, // test_mode + ); + let mut router = api::Router::new(); + if let Err(e) = router.add_route("/v2/foreign", Arc::new(api_handler)) { + let msg = format!("router: {}", e); + let _ = ready_tx.send(Err(msg.clone())); + return Err(msg); + } + + let api_chan: (mpsc::Sender<()>, mpsc::Receiver<()>) = (stop_for_thread, stop_rx); + let mut apis = api::ApiServer::new(); + let socket_addr: SocketAddr = listen + .parse() + .map_err(|e| format!("bad listen addr: {}", e))?; + let api_thread = match apis.start(socket_addr, router, None, api_chan) { + Ok(t) => t, + Err(e) => { + let msg = format!("API start failed: {:?}", e); + let _ = ready_tx.send(Err(msg.clone())); + return Err(msg); + } + }; + // Server is bound; signal readiness. + let _ = ready_tx.send(Ok(())); + api_thread + .join() + .map_err(|e| format!("API thread panicked: {:?}", e)) + }) + .expect("spawn foreign listener"); + + // Wait for bind success or failure (do not assume after a fixed sleep). + match ready_rx.recv_timeout(Duration::from_secs(10)) { + Ok(Ok(())) => {} + Ok(Err(e)) => panic!("foreign listener failed to start: {}", e), + Err(_) => panic!("foreign listener readiness timed out for {}", addr), + } + + // Extra TCP check so callers know the port is accepting connections. + wait_for_port(addr, Duration::from_secs(5)).expect("listener port"); + + ForeignListener { + stop_tx, + join: Some(join), + addr: addr.to_string(), + } +} + +fn wait_for_port(addr: &str, timeout: Duration) -> Result<(), String> { + let deadline = Instant::now() + timeout; + while Instant::now() < deadline { + if TcpStream::connect(addr).is_ok() { + return Ok(()); + } + thread::sleep(Duration::from_millis(50)); + } + Err(format!("timed out waiting for {}", addr)) +} + +/// POST a slate to a foreign HTTP listener's `receive_tx` (real network path). +pub fn receive_tx_via_http(base_url: &str, slate: &Slate) -> Result { + let trailing = if base_url.ends_with('/') { "" } else { "/" }; + let url = format!("{}{}v2/foreign", base_url, trailing); + + // Version negotiation (same as TorSlateSender::check_other_version). + let ver_req = json!({ + "jsonrpc": "2.0", + "method": "check_version", + "id": 1, + "params": [] + }); + let ver_res: Value = api::client::post(&url, None, &ver_req, api::client::TimeOut::default()) + .map_err(|e| format!("check_version: {}", e))?; + if ver_res["error"] != json!(null) { + return Err(format!("check_version error: {}", ver_res["error"])); + } + let supported: Vec = + serde_json::from_value(ver_res["result"]["Ok"]["supported_slate_versions"].clone()) + .map_err(|e| format!("parse versions: {}", e))?; + if !supported.iter().any(|v| v == "V4") { + return Err("remote does not support slate V4".into()); + } + + let slate_send = VersionedSlate::into_version(slate.clone(), SlateVersion::V4) + .map_err(|e| format!("version slate: {}", e))?; + let req = json!({ + "jsonrpc": "2.0", + "method": "receive_tx", + "id": 1, + "params": [slate_send, null, null] + }); + let res: Value = api::client::post(&url, None, &req, api::client::TimeOut::default()) + .map_err(|e| format!("receive_tx http: {}", e))?; + if res["error"] != json!(null) { + return Err(format!("receive_tx error: {}", res["error"])); + } + let slate_value = res["result"]["Ok"].clone(); + let slate_str = + serde_json::to_string(&slate_value).map_err(|e| format!("serialize slate: {}", e))?; + Slate::deserialize_upgrade(&slate_str).map_err(|e| format!("deserialize slate: {}", e)) +} + +/// Mine with the internal test miner, sending coinbase to `wallet_url` if set. +pub fn start_test_miner(server: &servers::Server, wallet_url: Option) -> Arc { + let stop = Arc::new(StopState::new()); + server.start_test_miner(wallet_url, stop.clone()); + stop +} diff --git a/integration/tests/dandelion.rs b/integration/tests/dandelion.rs deleted file mode 100644 index c80e3b648..000000000 --- a/integration/tests/dandelion.rs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2021 The Grin Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#[macro_use] -extern crate log; - -mod framework; - -use self::util::Mutex; -use crate::framework::{LocalServerContainer, LocalServerContainerConfig}; -use grin_core as core; -use grin_util as util; -use std::sync::Arc; -use std::{thread, time}; - -/// Start 1 node mining, 1 non mining node and two wallets. -/// Then send a transaction from one wallet to another and propagate it a stem -/// transaction but without stem relay and check if the transaction is still -/// broadcasted. -#[test] -#[ignore] -fn test_dandelion_timeout() { - let test_name_dir = "test_dandelion_timeout"; - core::global::set_local_chain_type(core::global::ChainTypes::AutomatedTesting); - framework::clean_all_output(test_name_dir); - let mut log_config = util::LoggingConfig::default(); - //log_config.stdout_log_level = util::LogLevel::Trace; - log_config.stdout_log_level = util::LogLevel::Info; - //init_logger(Some(log_config)); - util::init_test_logger(); - - // Run a separate coinbase wallet for coinbase transactions - let mut coinbase_config = LocalServerContainerConfig::default(); - coinbase_config.name = String::from("coinbase_wallet"); - coinbase_config.wallet_validating_node_url = String::from("http://127.0.0.1:30001"); - coinbase_config.wallet_port = 10002; - let coinbase_wallet = Arc::new(Mutex::new( - LocalServerContainer::new(coinbase_config).unwrap(), - )); - let coinbase_wallet_config = { coinbase_wallet.lock().wallet_config.clone() }; - - let coinbase_seed = LocalServerContainer::get_wallet_seed(&coinbase_wallet_config); - - let _ = thread::spawn(move || { - let mut w = coinbase_wallet.lock(); - w.run_wallet(0); - }); - - let mut recp_config = LocalServerContainerConfig::default(); - recp_config.name = String::from("target_wallet"); - recp_config.wallet_validating_node_url = String::from("http://127.0.0.1:30001"); - recp_config.wallet_port = 20002; - let target_wallet = Arc::new(Mutex::new(LocalServerContainer::new(recp_config).unwrap())); - let target_wallet_cloned = target_wallet.clone(); - let recp_wallet_config = { target_wallet.lock().wallet_config.clone() }; - - let recp_seed = LocalServerContainer::get_wallet_seed(&recp_wallet_config); - //Start up a second wallet, to receive - let _ = thread::spawn(move || { - let mut w = target_wallet_cloned.lock(); - w.run_wallet(0); - }); - - // Spawn server and let it run for a bit - let mut server_one_config = LocalServerContainerConfig::default(); - server_one_config.name = String::from("server_one"); - server_one_config.p2p_server_port = 30000; - server_one_config.api_server_port = 30001; - server_one_config.start_miner = true; - server_one_config.start_wallet = false; - server_one_config.is_seeding = false; - server_one_config.coinbase_wallet_address = - String::from(format!("http://{}:{}", server_one_config.base_addr, 10002)); - let mut server_one = LocalServerContainer::new(server_one_config).unwrap(); - - let mut server_two_config = LocalServerContainerConfig::default(); - server_two_config.name = String::from("server_two"); - server_two_config.p2p_server_port = 40000; - server_two_config.api_server_port = 40001; - server_two_config.start_miner = false; - server_two_config.start_wallet = false; - server_two_config.is_seeding = true; - let mut server_two = LocalServerContainer::new(server_two_config.clone()).unwrap(); - - server_one.add_peer(format!( - "{}:{}", - server_two_config.base_addr, server_two_config.p2p_server_port - )); - - // Spawn servers and let them run for a bit - let _ = thread::spawn(move || { - server_two.run_server(120); - }); - - // Wait for the first server to start - thread::sleep(time::Duration::from_millis(5000)); - - let _ = thread::spawn(move || { - server_one.run_server(120); - }); - - // Let them do a handshake and properly update their peer relay - thread::sleep(time::Duration::from_millis(30000)); - - //Wait until we have some funds to send - let mut coinbase_info = - LocalServerContainer::get_wallet_info(&coinbase_wallet_config, &coinbase_seed); - let mut slept_time = 0; - while coinbase_info.amount_currently_spendable < 100000000000 { - thread::sleep(time::Duration::from_millis(500)); - slept_time += 500; - if slept_time > 10000 { - panic!("Coinbase not confirming in time"); - } - coinbase_info = - LocalServerContainer::get_wallet_info(&coinbase_wallet_config, &coinbase_seed); - } - - warn!("Sending 50 Grins to recipient wallet"); - - // Sending stem transaction - LocalServerContainer::send_amount_to( - &coinbase_wallet_config, - "50.00", - 1, - "not_all", - "http://127.0.0.1:20002", - false, - ); - - let coinbase_info = - LocalServerContainer::get_wallet_info(&coinbase_wallet_config, &coinbase_seed); - println!("Coinbase wallet info: {:?}", coinbase_info); - - let recipient_info = LocalServerContainer::get_wallet_info(&recp_wallet_config, &recp_seed); - - // The transaction should be waiting in the node stempool thus cannot be mined. - println!("Recipient wallet info: {:?}", recipient_info); - assert!(recipient_info.amount_awaiting_confirmation == 50000000000); - - // Wait for stem timeout - thread::sleep(time::Duration::from_millis(35000)); - println!("Recipient wallet info: {:?}", recipient_info); - let recipient_info = LocalServerContainer::get_wallet_info(&recp_wallet_config, &recp_seed); - assert!(recipient_info.amount_currently_spendable == 50000000000); -} diff --git a/integration/tests/framework.rs b/integration/tests/framework.rs deleted file mode 100644 index 3e0b883c5..000000000 --- a/integration/tests/framework.rs +++ /dev/null @@ -1,694 +0,0 @@ -// Copyright 2021 The Grin Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -extern crate grin_apiwallet as apiwallet; -extern crate grin_libwallet as libwallet; -extern crate grin_refwallet as wallet; -extern crate grin_wallet_config as wallet_config; - -use self::keychain::Keychain; -use self::util::Mutex; -use self::wallet::{HTTPNodeClient, HTTPWalletCommAdapter, LMDBBackend}; -use self::wallet_config::WalletConfig; -use blake2_rfc as blake2; -use grin_api as api; -use grin_core as core; -use grin_keychain as keychain; -use grin_p2p as p2p; -use grin_servers as servers; -use grin_util as util; -use p2p::PeerAddr; -use std::default::Default; -use std::ops::Deref; -use std::sync::Arc; -use std::{fs, thread, time}; - -/// Just removes all results from previous runs -pub fn clean_all_output(test_name_dir: &str) { - let target_dir = format!("target/tmp/{}", test_name_dir); - if let Err(e) = fs::remove_dir_all(target_dir) { - println!("can't remove output from previous test :{}, may be ok", e); - } -} - -/// Errors that can be returned by LocalServerContainer -#[derive(Debug)] -#[allow(dead_code)] -pub enum Error { - Internal(String), - Argument(String), - NotFound, -} - -/// All-in-one server configuration struct, for convenience -/// -#[derive(Clone)] -pub struct LocalServerContainerConfig { - // user friendly name for the server, also denotes what dir - // the data files will appear in - pub name: String, - - // Base IP address - pub base_addr: String, - - // Port the server (p2p) is running on - pub p2p_server_port: u16, - - // Port the API server is running on - pub api_server_port: u16, - - // Port the wallet server is running on - pub wallet_port: u16, - - // Port the wallet owner API is running on - pub owner_port: u16, - - // Whether to include the foreign API endpoints in the owner API - pub owner_api_include_foreign: bool, - - // Whether we're going to mine - pub start_miner: bool, - - // time in millis by which to artificially slow down the mining loop - // in this container - pub miner_slowdown_in_millis: u64, - - // Whether we're going to run a wallet as well, - // can use same server instance as a validating node for convenience - pub start_wallet: bool, - - // address of a server to use as a seed - pub seed_addr: String, - - // keep track of whether this server is supposed to be seeding - pub is_seeding: bool, - - // Whether to burn mining rewards - pub burn_mining_rewards: bool, - - // full address to send coinbase rewards to - pub coinbase_wallet_address: String, - - // When running a wallet, the address to check inputs and send - // finalised transactions to, - pub wallet_validating_node_url: String, -} - -/// Default server config -impl Default for LocalServerContainerConfig { - fn default() -> LocalServerContainerConfig { - LocalServerContainerConfig { - name: String::from("test_host"), - base_addr: String::from("127.0.0.1"), - api_server_port: 13413, - p2p_server_port: 13414, - wallet_port: 13415, - owner_port: 13420, - owner_api_include_foreign: false, - seed_addr: String::from(""), - is_seeding: false, - start_miner: false, - start_wallet: false, - burn_mining_rewards: false, - coinbase_wallet_address: String::from(""), - wallet_validating_node_url: String::from(""), - miner_slowdown_in_millis: 0, - } - } -} - -/// A top-level container to hold everything that might be running -/// on a server, i.e. server, wallet in send or receive mode - -#[allow(dead_code)] -pub struct LocalServerContainer { - // Configuration - config: LocalServerContainerConfig, - - // Structure of references to the - // internal server data - pub p2p_server_stats: Option, - - // The API server instance - api_server: Option, - - // whether the server is running - pub server_is_running: bool, - - // Whether the server is mining - pub server_is_mining: bool, - - // Whether the server is also running a wallet - // Not used if running wallet without server - pub wallet_is_running: bool, - - // the list of peers to connect to - pub peer_list: Vec, - - // base directory for the server instance - pub working_dir: String, - - // Wallet configuration - pub wallet_config: WalletConfig, -} - -impl LocalServerContainer { - /// Create a new local server container with defaults, with the given name - /// all related files will be created in the directory - /// target/tmp/{name} - - pub fn new(config: LocalServerContainerConfig) -> Result { - let working_dir = format!("target/tmp/{}", config.name); - let mut wallet_config = WalletConfig::default(); - - wallet_config.api_listen_port = config.wallet_port; - wallet_config.check_node_api_http_addr = config.wallet_validating_node_url.clone(); - wallet_config.owner_api_include_foreign = Some(config.owner_api_include_foreign); - wallet_config.data_file_dir = working_dir.clone(); - Ok(LocalServerContainer { - config: config, - p2p_server_stats: None, - api_server: None, - server_is_running: false, - server_is_mining: false, - wallet_is_running: false, - working_dir: working_dir, - peer_list: Vec::new(), - wallet_config: wallet_config, - }) - } - - pub fn run_server(&mut self, duration_in_seconds: u64) -> servers::Server { - let api_addr = format!("{}:{}", self.config.base_addr, self.config.api_server_port); - - let mut seeding_type = p2p::Seeding::None; - let mut seeds = Vec::new(); - - if self.config.seed_addr.len() > 0 { - seeding_type = p2p::Seeding::List; - seeds = vec![PeerAddr::from_ip( - self.config.seed_addr.to_string().parse().unwrap(), - )]; - } - - let s = servers::Server::new(servers::ServerConfig { - api_http_addr: api_addr, - api_secret_path: None, - db_root: format!("{}/.grin", self.working_dir), - p2p_config: p2p::P2PConfig { - port: self.config.p2p_server_port, - seeds: Some(seeds), - seeding_type: seeding_type, - ..p2p::P2PConfig::default() - }, - chain_type: core::global::ChainTypes::AutomatedTesting, - skip_sync_wait: Some(true), - stratum_mining_config: None, - ..Default::default() - }) - .unwrap(); - - self.p2p_server_stats = Some(s.get_server_stats().unwrap()); - - let mut wallet_url = None; - - if self.config.start_wallet == true { - self.run_wallet(duration_in_seconds + 5); - // give a second to start wallet before continuing - thread::sleep(time::Duration::from_millis(1000)); - wallet_url = Some(format!( - "http://{}:{}", - self.config.base_addr, self.config.wallet_port - )); - } - - if self.config.start_miner == true { - println!( - "starting test Miner on port {}", - self.config.p2p_server_port - ); - s.start_test_miner(wallet_url, s.stop_state.clone()); - } - - for p in &mut self.peer_list { - println!("{} connecting to peer: {}", self.config.p2p_server_port, p); - let _ = s.connect_peer(PeerAddr::from_ip(p.parse().unwrap())); - } - - if self.wallet_is_running { - self.stop_wallet(); - } - - s - } - - /// Make a wallet for use in test endpoints (run_wallet and run_owner). - fn make_wallet_for_tests( - &mut self, - ) -> Arc>> { - // URL on which to start the wallet listener (i.e. api server) - let _url = format!("{}:{}", self.config.base_addr, self.config.wallet_port); - - // Just use the name of the server for a seed for now - let seed = format!("{}", self.config.name); - - let _seed = blake2::blake2b::blake2b(32, &[], seed.as_bytes()); - - println!( - "Starting the Grin wallet receiving daemon on {} ", - self.config.wallet_port - ); - - self.wallet_config = WalletConfig::default(); - - self.wallet_config.api_listen_port = self.config.wallet_port; - self.wallet_config.check_node_api_http_addr = - self.config.wallet_validating_node_url.clone(); - self.wallet_config.data_file_dir = self.working_dir.clone(); - self.wallet_config.owner_api_include_foreign = Some(self.config.owner_api_include_foreign); - - let _ = fs::create_dir_all(self.wallet_config.clone().data_file_dir); - let r = wallet::WalletSeed::init_file(&self.wallet_config, 32, None, ""); - - let client_n = HTTPNodeClient::new( - &self.wallet_config.check_node_api_http_addr, - None, - std::time::Duration::from_secs(60), - ); - - if let Err(_e) = r { - //panic!("Error initializing wallet seed: {}", e); - } - - let wallet: LMDBBackend = - LMDBBackend::new(self.wallet_config.clone(), "", client_n).unwrap_or_else(|e| { - panic!( - "Error creating wallet: {:?} Config: {:?}", - e, self.wallet_config - ) - }); - - Arc::new(Mutex::new(wallet)) - } - - /// Starts a wallet daemon to receive - pub fn run_wallet(&mut self, _duration_in_mills: u64) { - let wallet = self.make_wallet_for_tests(); - - wallet::controller::foreign_listener(wallet, &self.wallet_config.api_listen_addr(), None) - .unwrap_or_else(|e| { - panic!( - "Error creating wallet listener: {:?} Config: {:?}", - e, self.wallet_config - ) - }); - - self.wallet_is_running = true; - } - - /// Starts a wallet owner daemon - #[allow(dead_code)] - pub fn run_owner(&mut self) { - let wallet = self.make_wallet_for_tests(); - - // WalletConfig doesn't allow changing the owner API path, so we build - // the path ourselves - let owner_listen_addr = format!("127.0.0.1:{}", self.config.owner_port); - - wallet::controller::owner_listener( - wallet, - &owner_listen_addr, - None, - None, - self.wallet_config.owner_api_include_foreign.clone(), - ) - .unwrap_or_else(|e| { - panic!( - "Error creating wallet owner listener: {:?} Config: {:?}", - e, self.wallet_config - ) - }); - } - - #[allow(dead_code)] - pub fn get_wallet_seed(config: &WalletConfig) -> wallet::WalletSeed { - let _ = fs::create_dir_all(config.clone().data_file_dir); - wallet::WalletSeed::init_file(config, 32, None, "").unwrap(); - let wallet_seed = - wallet::WalletSeed::from_file(config, "").expect("Failed to read wallet seed file."); - wallet_seed - } - - #[allow(dead_code)] - pub fn get_wallet_info( - config: &WalletConfig, - wallet_seed: &wallet::WalletSeed, - ) -> wallet::WalletInfo { - let keychain: keychain::ExtKeychain = wallet_seed - .derive_keychain(false) - .expect("Failed to derive keychain from seed file and passphrase."); - let client_n = HTTPNodeClient::new( - &config.check_node_api_http_addr, - None, - std::time::Duration::from_secs(60), - ); - let mut wallet = LMDBBackend::new(config.clone(), "", client_n) - .unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config)); - wallet.keychain = Some(keychain); - let parent_id = keychain::ExtKeychain::derive_key_id(2, 0, 0, 0, 0); - let _ = libwallet::internal::updater::refresh_outputs(&mut wallet, &parent_id, false); - libwallet::internal::updater::retrieve_info(&mut wallet, &parent_id, 1).unwrap() - } - - #[allow(dead_code)] - pub fn send_amount_to( - config: &WalletConfig, - amount: &str, - minimum_confirmations: u64, - selection_strategy: &str, - dest: &str, - _fluff: bool, - ) { - let amount = core::core::amount_from_hr_string(amount) - .expect("Could not parse amount as a number with optional decimal point."); - - let wallet_seed = - wallet::WalletSeed::from_file(config, "").expect("Failed to read wallet seed file."); - - let keychain: keychain::ExtKeychain = wallet_seed - .derive_keychain(false) - .expect("Failed to derive keychain from seed file and passphrase."); - - let client_n = HTTPNodeClient::new( - &config.check_node_api_http_addr, - None, - std::time::Duration::from_secs(60), - ); - let client_w = HTTPWalletCommAdapter::new(); - - let max_outputs = 500; - let change_outputs = 1; - - let mut wallet = LMDBBackend::new(config.clone(), "", client_n) - .unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config)); - wallet.keychain = Some(keychain); - let _ = wallet::controller::owner_single_use(Arc::new(Mutex::new(wallet)), |api| { - let (mut slate, lock_fn) = api.initiate_tx( - None, - amount, - minimum_confirmations, - max_outputs, - change_outputs, - selection_strategy == "all", - None, - )?; - slate = client_w.send_tx_sync(dest, &slate)?; - slate = api.finalize_tx(&slate)?; - api.tx_lock_outputs(&slate, lock_fn)?; - println!( - "Tx sent: {} grin to {} (strategy '{}')", - core::core::amount_to_hr_string(amount, false), - dest, - selection_strategy, - ); - Ok(()) - }) - .unwrap_or_else(|e| panic!("Error creating wallet: {:?} Config: {:?}", e, config)); - } - - /// Stops the running wallet server - pub fn stop_wallet(&mut self) { - println!("Stop wallet!"); - let api_server = self.api_server.as_mut().unwrap(); - api_server.stop(); - } - - /// Adds a peer to this server to connect to upon running - - #[allow(dead_code)] - pub fn add_peer(&mut self, addr: String) { - self.peer_list.push(addr); - } -} - -/// Configuration values for container pool - -pub struct LocalServerContainerPoolConfig { - // Base name to append to all the servers in this pool - pub base_name: String, - - // Base http address for all of the servers in this pool - pub base_http_addr: String, - - // Base port server for all of the servers in this pool - // Increment the number by 1 for each new server - pub base_p2p_port: u16, - - // Base api port for all of the servers in this pool - // Increment this number by 1 for each new server - pub base_api_port: u16, - - // Base wallet port for this server - // - pub base_wallet_port: u16, - - // Base wallet owner port for this server - // - pub base_owner_port: u16, - - // How long the servers in the pool are going to run - pub run_length_in_seconds: u64, -} - -/// Default server config -/// -impl Default for LocalServerContainerPoolConfig { - fn default() -> LocalServerContainerPoolConfig { - LocalServerContainerPoolConfig { - base_name: String::from("test_pool"), - base_http_addr: String::from("127.0.0.1"), - base_p2p_port: 10000, - base_api_port: 11000, - base_wallet_port: 12000, - base_owner_port: 13000, - run_length_in_seconds: 30, - } - } -} - -/// A convenience pool for running many servers simultaneously -/// without necessarily having to configure each one manually - -#[allow(dead_code)] -pub struct LocalServerContainerPool { - // configuration - pub config: LocalServerContainerPoolConfig, - - // keep ahold of all the created servers thread-safely - server_containers: Vec, - - // Keep track of what the last ports a server was opened on - next_p2p_port: u16, - - next_api_port: u16, - - next_wallet_port: u16, - - next_owner_port: u16, - - // keep track of whether a seed exists, and pause a bit if so - is_seeding: bool, -} - -#[allow(dead_code)] -impl LocalServerContainerPool { - pub fn new(config: LocalServerContainerPoolConfig) -> LocalServerContainerPool { - (LocalServerContainerPool { - next_api_port: config.base_api_port, - next_p2p_port: config.base_p2p_port, - next_wallet_port: config.base_wallet_port, - next_owner_port: config.base_owner_port, - config: config, - server_containers: Vec::new(), - is_seeding: false, - }) - } - - /// adds a single server on the next available port - /// overriding passed-in values as necessary. Config object is an OUT value - /// with - /// ports/addresses filled in - /// - - #[allow(dead_code)] - pub fn create_server(&mut self, server_config: &mut LocalServerContainerConfig) { - // If we're calling it this way, need to override these - server_config.p2p_server_port = self.next_p2p_port; - server_config.api_server_port = self.next_api_port; - server_config.wallet_port = self.next_wallet_port; - server_config.owner_port = self.next_owner_port; - - server_config.name = String::from(format!( - "{}/{}-{}", - self.config.base_name, self.config.base_name, server_config.p2p_server_port - )); - - // Use self as coinbase wallet - server_config.coinbase_wallet_address = String::from(format!( - "http://{}:{}", - server_config.base_addr, server_config.wallet_port - )); - - self.next_p2p_port += 1; - self.next_api_port += 1; - self.next_wallet_port += 1; - self.next_owner_port += 1; - - if server_config.is_seeding { - self.is_seeding = true; - } - - let _server_address = format!( - "{}:{}", - server_config.base_addr, server_config.p2p_server_port - ); - - let server_container = LocalServerContainer::new(server_config.clone()).unwrap(); - // self.server_containers.push(server_arc); - - // Create a future that runs the server for however many seconds - // collect them all and run them in the run_all_servers - let _run_time = self.config.run_length_in_seconds; - - self.server_containers.push(server_container); - } - - /// adds n servers, ready to run - /// - /// - #[allow(dead_code)] - pub fn create_servers(&mut self, number: u16) { - for _ in 0..number { - // self.create_server(); - } - } - - /// runs all servers, and returns a vector of references to the servers - /// once they've all been run - /// - - #[allow(dead_code)] - pub fn run_all_servers(self) -> Arc>> { - let run_length = self.config.run_length_in_seconds; - let mut handles = vec![]; - - // return handles to all of the servers, wrapped in mutexes, handles, etc - let return_containers = Arc::new(Mutex::new(Vec::new())); - - let is_seeding = self.is_seeding.clone(); - - for mut s in self.server_containers { - let return_container_ref = return_containers.clone(); - let handle = thread::spawn(move || { - if is_seeding && !s.config.is_seeding { - // there's a seed and we're not it, so hang around longer and give the seed - // a chance to start - thread::sleep(time::Duration::from_millis(2000)); - } - let server_ref = s.run_server(run_length); - return_container_ref.lock().push(server_ref); - }); - // Not a big fan of sleeping hack here, but there appears to be a - // concurrency issue when creating files in rocksdb that causes - // failure if we don't pause a bit before starting the next server - thread::sleep(time::Duration::from_millis(500)); - handles.push(handle); - } - - for handle in handles { - match handle.join() { - Ok(_) => {} - Err(e) => { - println!("Error starting server thread: {:?}", e); - panic!(e); - } - } - } - - // return a much simplified version of the results - return_containers.clone() - } - - #[allow(dead_code)] - pub fn connect_all_peers(&mut self) { - // just pull out all currently active servers, build a list, - // and feed into all servers - let mut server_addresses: Vec = Vec::new(); - for s in &self.server_containers { - let server_address = format!("{}:{}", s.config.base_addr, s.config.p2p_server_port); - server_addresses.push(server_address); - } - - for a in server_addresses { - for s in &mut self.server_containers { - if format!("{}:{}", s.config.base_addr, s.config.p2p_server_port) != a { - s.add_peer(a.clone()); - } - } - } - } -} - -#[allow(dead_code)] -pub fn stop_all_servers(servers: Arc>>) { - let locked_servs = servers.lock(); - for s in locked_servs.deref() { - s.stop(); - } -} - -/// Create and return a ServerConfig -#[allow(dead_code)] -pub fn config(n: u16, test_name_dir: &str, seed_n: u16) -> servers::ServerConfig { - servers::ServerConfig { - api_http_addr: format!("127.0.0.1:{}", 20000 + n), - api_secret_path: None, - db_root: format!("target/tmp/{}/grin-sync-{}", test_name_dir, n), - p2p_config: p2p::P2PConfig { - port: 10000 + n, - seeding_type: p2p::Seeding::List, - seeds: Some(vec![PeerAddr::from_ip( - format!("127.0.0.1:{}", 10000 + seed_n).parse().unwrap(), - )]), - ..p2p::P2PConfig::default() - }, - chain_type: core::global::ChainTypes::AutomatedTesting, - archive_mode: Some(true), - skip_sync_wait: Some(true), - ..Default::default() - } -} - -/// return stratum mining config -#[allow(dead_code)] -pub fn stratum_config() -> servers::common::types::StratumServerConfig { - servers::common::types::StratumServerConfig { - enable_stratum_server: Some(true), - stratum_server_addr: Some(String::from("127.0.0.1:13416")), - attempt_time_per_block: 60, - minimum_share_difficulty: 1, - wallet_listener_url: String::from("http://127.0.0.1:13415"), - burn_reward: false, - } -} diff --git a/integration/tests/simulnet.rs b/integration/tests/simulnet.rs deleted file mode 100644 index 2675f33ae..000000000 --- a/integration/tests/simulnet.rs +++ /dev/null @@ -1,1007 +0,0 @@ -// Copyright 2021 The Grin Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -extern crate grin_apiwallet as apiwallet; -extern crate grin_libwallet as libwallet; -extern crate grin_refwallet as wallet; -extern crate grin_wallet_config as wallet_config; -#[macro_use] -extern crate log; - -mod framework; - -use self::core::core::hash::Hashed; -use self::core::global::{self, ChainTypes}; -use self::libwallet::types::{WalletBackend, WalletInst}; -use self::util::{Mutex, StopState}; -use self::wallet::controller; -use self::wallet::lmdb_wallet::LMDBBackend; -use self::wallet::{HTTPNodeClient, HTTPWalletCommAdapter}; -use self::wallet_config::WalletConfig; -use grin_api as api; -use grin_core as core; -use grin_keychain as keychain; -use grin_p2p as p2p; -use grin_servers as servers; -use grin_util as util; -use p2p::PeerAddr; -use std::cmp; -use std::default::Default; -use std::process::exit; -use std::sync::Arc; -use std::{thread, time}; - -use crate::framework::{ - config, stop_all_servers, LocalServerContainerConfig, LocalServerContainerPool, - LocalServerContainerPoolConfig, -}; - -/// Testing the frameworks by starting a fresh server, creating a genesis -/// Block and mining into a wallet for a bit -#[test] -fn basic_genesis_mine() { - util::init_test_logger(); - global::set_local_chain_type(ChainTypes::AutomatedTesting); - - let test_name_dir = "genesis_mine"; - framework::clean_all_output(test_name_dir); - - // Create a server pool - let mut pool_config = LocalServerContainerPoolConfig::default(); - pool_config.base_name = String::from(test_name_dir); - pool_config.run_length_in_seconds = 10; - - pool_config.base_api_port = 30000; - pool_config.base_p2p_port = 31000; - pool_config.base_wallet_port = 32000; - - let mut pool = LocalServerContainerPool::new(pool_config); - - // Create a server to add into the pool - let mut server_config = LocalServerContainerConfig::default(); - server_config.start_miner = true; - server_config.start_wallet = false; - server_config.burn_mining_rewards = true; - - pool.create_server(&mut server_config); - let servers = pool.run_all_servers(); - stop_all_servers(servers); -} - -/// Creates 5 servers, first being a seed and check that through peer address -/// messages they all end up connected. -#[test] -fn simulate_seeding() { - util::init_test_logger(); - global::set_local_chain_type(ChainTypes::AutomatedTesting); - - let test_name_dir = "simulate_seeding"; - framework::clean_all_output(test_name_dir); - - // Create a server pool - let mut pool_config = LocalServerContainerPoolConfig::default(); - pool_config.base_name = test_name_dir.to_string(); - pool_config.run_length_in_seconds = 30; - - // have to use different ports because of tests being run in parallel - pool_config.base_api_port = 30020; - pool_config.base_p2p_port = 31020; - pool_config.base_wallet_port = 32020; - - let mut pool = LocalServerContainerPool::new(pool_config); - - // Create a first seed server to add into the pool - let mut server_config = LocalServerContainerConfig::default(); - // server_config.start_miner = true; - server_config.start_wallet = false; - server_config.burn_mining_rewards = true; - server_config.is_seeding = true; - - pool.create_server(&mut server_config); - - // wait the seed server fully start up before start remaining servers - thread::sleep(time::Duration::from_millis(1_000)); - - // point next servers at first seed - server_config.is_seeding = false; - server_config.seed_addr = format!( - "{}:{}", - server_config.base_addr, server_config.p2p_server_port - ); - - for _ in 0..4 { - pool.create_server(&mut server_config); - } - - let servers = pool.run_all_servers(); - thread::sleep(time::Duration::from_secs(5)); - - // Check they all end up connected. - let url = format!( - "http://{}:{}/v1/peers/connected", - &server_config.base_addr, 30020 - ); - let peers_all = api::client::get::>(url.as_str(), None); - assert!(peers_all.is_ok()); - assert_eq!(peers_all.unwrap().len(), 4); - - stop_all_servers(servers); - - // wait servers fully stop before start next automated test - thread::sleep(time::Duration::from_millis(1_000)); -} - -/// Create 1 server, start it mining, then connect 4 other peers mining and -/// using the first as a seed. Meant to test the evolution of mining difficulty with miners -/// running at different rates. -/// -/// TODO: Just going to comment this out as an automatically run test for the time -/// being, As it's more for actively testing and hurts CI a lot -#[ignore] -#[test] -fn simulate_parallel_mining() { - global::set_local_chain_type(ChainTypes::AutomatedTesting); - - let test_name_dir = "simulate_parallel_mining"; - // framework::clean_all_output(test_name_dir); - - // Create a server pool - let mut pool_config = LocalServerContainerPoolConfig::default(); - pool_config.base_name = test_name_dir.to_string(); - pool_config.run_length_in_seconds = 60; - // have to use different ports because of tests being run in parallel - pool_config.base_api_port = 30040; - pool_config.base_p2p_port = 31040; - pool_config.base_wallet_port = 32040; - - let mut pool = LocalServerContainerPool::new(pool_config); - - // Create a first seed server to add into the pool - let mut server_config = LocalServerContainerConfig::default(); - server_config.start_miner = true; - server_config.start_wallet = true; - server_config.is_seeding = true; - - pool.create_server(&mut server_config); - - // point next servers at first seed - server_config.is_seeding = false; - server_config.seed_addr = format!( - "{}:{}", - server_config.base_addr, server_config.p2p_server_port - ); - - // And create 4 more, then let them run for a while - for i in 1..4 { - // fudge in some slowdown - server_config.miner_slowdown_in_millis = i * 2; - pool.create_server(&mut server_config); - } - - // pool.connect_all_peers(); - - let servers = pool.run_all_servers(); - stop_all_servers(servers); - - // Check mining difficulty here?, though I'd think it's more valuable - // to simply output it. Can at least see the evolution of the difficulty target - // in the debug log output for now -} - -// TODO: Convert these tests to newer framework format -/// Create a network of 5 servers and mine a block, verifying that the block -/// gets propagated to all. -#[test] -fn simulate_block_propagation() { - util::init_test_logger(); - - // we actually set the chain_type in the ServerConfig below - // TODO - avoid needing to set it in two places? - global::set_local_chain_type(ChainTypes::AutomatedTesting); - - let test_name_dir = "grin-prop"; - framework::clean_all_output(test_name_dir); - - // instantiates 5 servers on different ports - let mut servers = vec![]; - for n in 0..5 { - let s = servers::Server::new(framework::config(10 * n, test_name_dir, 0)).unwrap(); - servers.push(s); - thread::sleep(time::Duration::from_millis(100)); - } - - // start mining - let stop = Arc::new(Mutex::new(StopState::new())); - servers[0].start_test_miner(None, stop.clone()); - - // monitor for a change of head on a different server and check whether - // chain height has changed - let mut success = false; - let mut time_spent = 0; - loop { - let mut count = 0; - for n in 0..5 { - if servers[n].head().height > 3 { - count += 1; - } - } - if count == 5 { - success = true; - break; - } - thread::sleep(time::Duration::from_millis(1_000)); - time_spent += 1; - if time_spent >= 30 { - info!("simulate_block_propagation - fail on timeout",); - break; - } - - // stop mining after 8s - if time_spent == 8 { - servers[0].stop_test_miner(stop.clone()); - } - } - for n in 0..5 { - servers[n].stop(); - } - assert_eq!(true, success); - - // wait servers fully stop before start next automated test - thread::sleep(time::Duration::from_millis(1_000)); -} - -/// Creates 2 different disconnected servers, mine a few blocks on one, connect -/// them and check that the 2nd gets all the blocks -#[test] -fn simulate_full_sync() { - util::init_test_logger(); - - // we actually set the chain_type in the ServerConfig below - global::set_local_chain_type(ChainTypes::AutomatedTesting); - - let test_name_dir = "grin-sync"; - framework::clean_all_output(test_name_dir); - - let s1 = servers::Server::new(framework::config(1000, "grin-sync", 1000)).unwrap(); - // mine a few blocks on server 1 - let stop = Arc::new(Mutex::new(StopState::new())); - s1.start_test_miner(None, stop.clone()); - thread::sleep(time::Duration::from_secs(8)); - s1.stop_test_miner(stop); - - let s2 = servers::Server::new(framework::config(1001, "grin-sync", 1000)).unwrap(); - - // Get the current header from s1. - let s1_header = s1.chain.head_header().unwrap(); - info!( - "simulate_full_sync - s1 header head: {} at {}", - s1_header.hash(), - s1_header.height - ); - - // Wait for s2 to sync up to and including the header from s1. - let mut time_spent = 0; - while s2.head().height < s1_header.height { - thread::sleep(time::Duration::from_millis(1_000)); - time_spent += 1; - if time_spent >= 30 { - info!( - "sync fail. s2.head().height: {}, s1_header.height: {}", - s2.head().height, - s1_header.height - ); - break; - } - } - - // Confirm both s1 and s2 see a consistent header at that height. - let s2_header = s2.chain.get_block_header(&s1_header.hash()).unwrap(); - assert_eq!(s1_header, s2_header); - - // Stop our servers cleanly. - s1.stop(); - s2.stop(); - - // wait servers fully stop before start next automated test - thread::sleep(time::Duration::from_millis(1_000)); -} - -/// Creates 2 different disconnected servers, mine a few blocks on one, connect -/// them and check that the 2nd gets all using fast sync algo -#[test] -fn simulate_fast_sync() { - util::init_test_logger(); - - // we actually set the chain_type in the ServerConfig below - global::set_local_chain_type(ChainTypes::AutomatedTesting); - - let test_name_dir = "grin-fast"; - framework::clean_all_output(test_name_dir); - - // start s1 and mine enough blocks to get beyond the fast sync horizon - let s1 = servers::Server::new(framework::config(2000, "grin-fast", 2000)).unwrap(); - let stop = Arc::new(Mutex::new(StopState::new())); - s1.start_test_miner(None, stop.clone()); - - while s1.head().height < 20 { - thread::sleep(time::Duration::from_millis(1_000)); - } - s1.stop_test_miner(stop); - - let mut conf = config(2001, "grin-fast", 2000); - conf.archive_mode = Some(false); - - let s2 = servers::Server::new(conf).unwrap(); - - // Get the current header from s1. - let s1_header = s1.chain.head_header().unwrap(); - - // Wait for s2 to sync up to and including the header from s1. - let mut total_wait = 0; - while s2.head().height < s1_header.height { - thread::sleep(time::Duration::from_millis(1_000)); - total_wait += 1; - if total_wait >= 30 { - error!( - "simulate_fast_sync test fail on timeout! s2 height: {}, s1 height: {}", - s2.head().height, - s1_header.height, - ); - break; - } - } - - // Confirm both s1 and s2 see a consistent header at that height. - let s2_header = s2.chain.get_block_header(&s1_header.hash()).unwrap(); - assert_eq!(s1_header, s2_header); - - // Stop our servers cleanly. - s1.stop(); - s2.stop(); - - // wait servers fully stop before start next automated test - thread::sleep(time::Duration::from_millis(1_000)); -} - -/// Preparation: -/// Creates 6 disconnected servers: A, B, C, D, E and F, mine 80 blocks on A, -/// Compact server A. -/// Connect all servers, check all get state_sync_threshold full blocks using fast sync. -/// Disconnect all servers from each other. -/// -/// Test case 1: nodes that just synced is able to handle forks of up to state_sync_threshold -/// Mine state_sync_threshold-7 blocks on A -/// Mine state_sync_threshold-1 blocks on C (long fork), connect C to server A -/// check server A can sync to C without txhashset download. -/// -/// Test case 2: nodes with history in between state_sync_threshold and cut_through_horizon will -/// be able to handle forks larger than state_sync_threshold but not as large as cut_through_horizon. -/// Mine 20 blocks on A (then A has 59 blocks in local chain) -/// Mine cut_through_horizon-1 blocks on D (longer fork), connect D to servers A, then fork point -/// is at A's body head.height - 39, and 20 < 39 < 70. -/// check server A can sync without txhashset download. -/// -/// Test case 3: nodes that have enough history is able to handle forks of up to cut_through_horizon -/// Mine cut_through_horizon+10 blocks on E, connect E to servers A and B -/// check server A can sync to E without txhashset download. -/// check server B can sync to E but need txhashset download. -/// -/// Test case 4: nodes which had a success state sync can have a new state sync if needed. -/// Mine cut_through_horizon+20 blocks on F (longer fork than E), connect F to servers B -/// check server B can sync to F with txhashset download. -/// -/// Test case 5: normal sync (not a fork) should not trigger a txhashset download -/// Mine cut_through_horizon-10 blocks on F, connect F to servers B -/// check server B can sync to F without txhashset download. -/// -/// Test case 6: far behind sync (not a fork) should trigger a txhashset download -/// Mine cut_through_horizon+1 blocks on F, connect F to servers B -/// check server B can sync to F with txhashset download. -/// -/// -#[ignore] -#[test] -fn simulate_long_fork() { - util::init_test_logger(); - println!("starting simulate_long_fork"); - - // we actually set the chain_type in the ServerConfig below - global::set_local_chain_type(ChainTypes::AutomatedTesting); - - let test_name_dir = "grin-long-fork"; - framework::clean_all_output(test_name_dir); - - let s = long_fork_test_preparation(); - for si in &s { - si.pause(); - } - thread::sleep(time::Duration::from_millis(1_000)); - - long_fork_test_case_1(&s); - thread::sleep(time::Duration::from_millis(1_000)); - - long_fork_test_case_2(&s); - thread::sleep(time::Duration::from_millis(1_000)); - - long_fork_test_case_3(&s); - thread::sleep(time::Duration::from_millis(1_000)); - - long_fork_test_case_4(&s); - thread::sleep(time::Duration::from_millis(1_000)); - - long_fork_test_case_5(&s); - - // Clean up - for si in &s { - si.stop(); - } - - // wait servers fully stop before start next automated test - thread::sleep(time::Duration::from_millis(1_000)); -} - -fn long_fork_test_preparation() -> Vec { - println!("preparation: mine 80 blocks, create 6 servers and sync all of them"); - - let mut s: Vec = vec![]; - - // start server A and mine 80 blocks to get beyond the fast sync horizon - let mut conf = framework::config(2100, "grin-long-fork", 2100); - conf.archive_mode = Some(false); - conf.api_secret_path = None; - let s0 = servers::Server::new(conf).unwrap(); - thread::sleep(time::Duration::from_millis(1_000)); - s.push(s0); - let stop = Arc::new(Mutex::new(StopState::new())); - s[0].start_test_miner(None, stop.clone()); - - while s[0].head().height < global::cut_through_horizon() as u64 + 10 { - thread::sleep(time::Duration::from_millis(1_000)); - } - s[0].stop_test_miner(stop); - thread::sleep(time::Duration::from_millis(1_000)); - - // Get the current header from s0. - let s0_header = s[0].chain.head().unwrap(); - - // check the tail after compacting - let _ = s[0].chain.compact(); - let s0_tail = s[0].chain.tail().unwrap(); - assert_eq!( - s0_header.height - global::cut_through_horizon() as u64, - s0_tail.height - ); - - for i in 1..6 { - let mut conf = config(2100 + i, "grin-long-fork", 2100); - conf.archive_mode = Some(false); - conf.api_secret_path = None; - let si = servers::Server::new(conf).unwrap(); - s.push(si); - } - thread::sleep(time::Duration::from_millis(1_000)); - - // Wait for s[1..5] to sync up to and including the header from s0. - let mut total_wait = 0; - let mut min_height = 0; - while min_height < s0_header.height { - thread::sleep(time::Duration::from_millis(1_000)); - total_wait += 1; - if total_wait >= 60 { - println!( - "simulate_long_fork (preparation) test fail on timeout! minimum height: {}, s0 height: {}", - min_height, - s0_header.height, - ); - exit(1); - } - min_height = s0_header.height; - for i in 1..6 { - min_height = cmp::min(s[i].head().height, min_height); - } - } - - // Confirm both s0 and s1 see a consistent header at that height. - let s1_header = s[1].chain.head().unwrap(); - assert_eq!(s0_header, s1_header); - println!( - "preparation done. all 5 servers head.height: {}", - s0_header.height - ); - - // Wait for peers fully connection - let mut total_wait = 0; - let mut min_peers = 0; - while min_peers < 4 { - thread::sleep(time::Duration::from_millis(1_000)); - total_wait += 1; - if total_wait >= 60 { - println!( - "simulate_long_fork (preparation) test fail on timeout! minimum connected peers: {}", - min_peers, - ); - exit(1); - } - min_peers = 4; - for i in 0..5 { - let peers_connected = get_connected_peers(&"127.0.0.1".to_owned(), 22100 + i); - min_peers = cmp::min(min_peers, peers_connected.len()); - } - } - - return s; -} - -fn long_fork_test_mining(blocks: u64, n: u16, s: &servers::Server) { - // Get the current header from node. - let sn_header = s.chain.head().unwrap(); - - // Mining - let stop = Arc::new(Mutex::new(StopState::new())); - s.start_test_miner(None, stop.clone()); - - while s.head().height < sn_header.height + blocks { - thread::sleep(time::Duration::from_millis(1)); - } - s.stop_test_miner(stop); - thread::sleep(time::Duration::from_millis(1_000)); - println!( - "{} blocks mined on s{}. s{}.height: {} (old height: {})", - s.head().height - sn_header.height, - n, - n, - s.head().height, - sn_header.height, - ); - - let _ = s.chain.compact(); - let sn_header = s.chain.head().unwrap(); - let sn_tail = s.chain.tail().unwrap(); - println!( - "after compacting, s{}.head().height: {}, s{}.tail().height: {}", - n, sn_header.height, n, sn_tail.height, - ); -} - -fn long_fork_test_case_1(s: &[servers::Server]) { - println!("\ntest case 1 start"); - - // Mine state_sync_threshold-7 blocks on s0 - long_fork_test_mining(global::state_sync_threshold() as u64 - 7, 0, &s[0]); - - // Mine state_sync_threshold-1 blocks on s2 (long fork), a fork with more work than s0 chain - long_fork_test_mining(global::state_sync_threshold() as u64 - 1, 2, &s[2]); - - let s2_header = s[2].chain.head().unwrap(); - let s0_header = s[0].chain.head().unwrap(); - let s0_tail = s[0].chain.tail().unwrap(); - println!( - "test case 1: s0 start syncing with s2... s0.head().height: {}, s2.head().height: {}", - s0_header.height, s2_header.height, - ); - s[0].resume(); - s[2].resume(); - - // Check server s0 can sync to s2 without txhashset download. - let mut total_wait = 0; - while s[0].head().height < s2_header.height { - thread::sleep(time::Duration::from_millis(1_000)); - total_wait += 1; - if total_wait >= 120 { - println!( - "test case 1: test fail on timeout! s0 height: {}, s2 height: {}", - s[0].head().height, - s2_header.height, - ); - exit(1); - } - } - let s0_tail_new = s[0].chain.tail().unwrap(); - assert_eq!(s0_tail_new.height, s0_tail.height); - println!( - "test case 1: s0.head().height: {}, s2_header.height: {}", - s[0].head().height, - s2_header.height, - ); - assert_eq!(s[0].head().last_block_h, s2_header.last_block_h); - - s[0].pause(); - s[2].stop(); - println!("test case 1 passed") -} - -fn long_fork_test_case_2(s: &[servers::Server]) { - println!("\ntest case 2 start"); - - // Mine 20 blocks on s0 - long_fork_test_mining(20, 0, &s[0]); - - // Mine cut_through_horizon-1 blocks on s3 (longer fork) - long_fork_test_mining(global::cut_through_horizon() as u64 - 1, 3, &s[3]); - let s3_header = s[3].chain.head().unwrap(); - let s0_header = s[0].chain.head().unwrap(); - let s0_tail = s[0].chain.tail().unwrap(); - println!( - "test case 2: s0 start syncing with s3. s0.head().height: {}, s3.head().height: {}", - s0_header.height, s3_header.height, - ); - s[0].resume(); - s[3].resume(); - - // Check server s0 can sync to s3 without txhashset download. - let mut total_wait = 0; - while s[0].head().height < s3_header.height { - thread::sleep(time::Duration::from_millis(1_000)); - total_wait += 1; - if total_wait >= 120 { - println!( - "test case 2: test fail on timeout! s0 height: {}, s3 height: {}", - s[0].head().height, - s3_header.height, - ); - exit(1); - } - } - let s0_tail_new = s[0].chain.tail().unwrap(); - assert_eq!(s0_tail_new.height, s0_tail.height); - assert_eq!(s[0].head().hash(), s3_header.hash()); - - let _ = s[0].chain.compact(); - let s0_header = s[0].chain.head().unwrap(); - let s0_tail = s[0].chain.tail().unwrap(); - println!( - "test case 2: after compacting, s0.head().height: {}, s0.tail().height: {}", - s0_header.height, s0_tail.height, - ); - - s[0].pause(); - s[3].stop(); - println!("test case 2 passed") -} - -fn long_fork_test_case_3(s: &[servers::Server]) { - println!("\ntest case 3 start"); - - // Mine cut_through_horizon+1 blocks on s4 - long_fork_test_mining(global::cut_through_horizon() as u64 + 10, 4, &s[4]); - - let s4_header = s[4].chain.head().unwrap(); - let s0_header = s[0].chain.head().unwrap(); - let s0_tail = s[0].chain.tail().unwrap(); - let s1_header = s[1].chain.head().unwrap(); - let s1_tail = s[1].chain.tail().unwrap(); - println!( - "test case 3: s0/1 start syncing with s4. s0.head().height: {}, s0.tail().height: {}, s1.head().height: {}, s1.tail().height: {}, s4.head().height: {}", - s0_header.height, s0_tail.height, - s1_header.height, s1_tail.height, - s4_header.height, - ); - s[0].resume(); - s[4].resume(); - - // Check server s0 can sync to s4. - let mut total_wait = 0; - while s[0].head().height < s4_header.height { - thread::sleep(time::Duration::from_millis(1_000)); - total_wait += 1; - if total_wait >= 120 { - println!( - "test case 3: test fail on timeout! s0 height: {}, s4 height: {}", - s[0].head().height, - s4_header.height, - ); - exit(1); - } - } - assert_eq!(s[0].head().hash(), s4_header.hash()); - - s[0].stop(); - s[1].resume(); - - // Check server s1 can sync to s4 but with txhashset download. - let mut total_wait = 0; - while s[1].head().height < s4_header.height { - thread::sleep(time::Duration::from_millis(1_000)); - total_wait += 1; - if total_wait >= 120 { - println!( - "test case 3: test fail on timeout! s1 height: {}, s4 height: {}", - s[1].head().height, - s4_header.height, - ); - exit(1); - } - } - let s1_tail_new = s[1].chain.tail().unwrap(); - println!( - "test case 3: s[1].tail().height: {}, old height: {}", - s1_tail_new.height, s1_tail.height - ); - assert_ne!(s1_tail_new.height, s1_tail.height); - assert_eq!(s[1].head().hash(), s4_header.hash()); - - s[1].pause(); - s[4].pause(); - println!("test case 3 passed") -} - -fn long_fork_test_case_4(s: &[servers::Server]) { - println!("\ntest case 4 start"); - - let _ = s[1].chain.compact(); - - // Mine cut_through_horizon+20 blocks on s5 (longer fork than s4) - long_fork_test_mining(global::cut_through_horizon() as u64 + 20, 5, &s[5]); - - let s5_header = s[5].chain.head().unwrap(); - let s1_header = s[1].chain.head().unwrap(); - let s1_tail = s[1].chain.tail().unwrap(); - println!( - "test case 4: s1 start syncing with s5. s1.head().height: {}, s1.tail().height: {}, s5.head().height: {}", - s1_header.height, s1_tail.height, - s5_header.height, - ); - s[1].resume(); - s[5].resume(); - - // Check server s1 can sync to s5 with a new txhashset download. - let mut total_wait = 0; - while s[1].head().height < s5_header.height { - thread::sleep(time::Duration::from_millis(1_000)); - total_wait += 1; - if total_wait >= 120 { - println!( - "test case 4: test fail on timeout! s1 height: {}, s5 height: {}", - s[1].head().height, - s5_header.height, - ); - exit(1); - } - } - let s1_tail_new = s[1].chain.tail().unwrap(); - println!( - "test case 4: s[1].tail().height: {}, old height: {}", - s1_tail_new.height, s1_tail.height - ); - assert_ne!(s1_tail_new.height, s1_tail.height); - assert_eq!(s[1].head().hash(), s5_header.hash()); - - s[1].pause(); - s[5].pause(); - - println!("test case 4 passed") -} - -fn long_fork_test_case_5(s: &[servers::Server]) { - println!("\ntest case 5 start"); - - let _ = s[1].chain.compact(); - - // Mine cut_through_horizon-10 blocks on s5 - long_fork_test_mining(global::cut_through_horizon() as u64 - 10, 5, &s[5]); - - let s5_header = s[5].chain.head().unwrap(); - let s1_header = s[1].chain.head().unwrap(); - let s1_tail = s[1].chain.tail().unwrap(); - println!( - "test case 5: s1 start syncing with s5. s1.head().height: {}, s1.tail().height: {}, s5.head().height: {}", - s1_header.height, s1_tail.height, - s5_header.height, - ); - s[1].resume(); - s[5].resume(); - - // Check server s1 can sync to s5 without a txhashset download (normal body sync) - let mut total_wait = 0; - while s[1].head().height < s5_header.height { - thread::sleep(time::Duration::from_millis(1_000)); - total_wait += 1; - if total_wait >= 120 { - println!( - "test case 5: test fail on timeout! s1 height: {}, s5 height: {}", - s[1].head().height, - s5_header.height, - ); - exit(1); - } - } - let s1_tail_new = s[1].chain.tail().unwrap(); - println!( - "test case 5: s[1].tail().height: {}, old height: {}", - s1_tail_new.height, s1_tail.height - ); - assert_eq!(s1_tail_new.height, s1_tail.height); - assert_eq!(s[1].head().hash(), s5_header.hash()); - - s[1].pause(); - s[5].pause(); - - println!("test case 5 passed") -} - -#[allow(dead_code)] -fn long_fork_test_case_6(s: &[servers::Server]) { - println!("\ntest case 6 start"); - - let _ = s[1].chain.compact(); - - // Mine cut_through_horizon+1 blocks on s5 - long_fork_test_mining(global::cut_through_horizon() as u64 + 1, 5, &s[5]); - - let s5_header = s[5].chain.head().unwrap(); - let s1_header = s[1].chain.head().unwrap(); - let s1_tail = s[1].chain.tail().unwrap(); - println!( - "test case 6: s1 start syncing with s5. s1.head().height: {}, s1.tail().height: {}, s5.head().height: {}", - s1_header.height, s1_tail.height, - s5_header.height, - ); - s[1].resume(); - s[5].resume(); - - // Check server s1 can sync to s5 without a txhashset download (normal body sync) - let mut total_wait = 0; - while s[1].head().height < s5_header.height { - thread::sleep(time::Duration::from_millis(1_000)); - total_wait += 1; - if total_wait >= 120 { - println!( - "test case 6: test fail on timeout! s1 height: {}, s5 height: {}", - s[1].head().height, - s5_header.height, - ); - exit(1); - } - } - let s1_tail_new = s[1].chain.tail().unwrap(); - println!( - "test case 6: s[1].tail().height: {}, old height: {}", - s1_tail_new.height, s1_tail.height - ); - assert_eq!(s1_tail_new.height, s1_tail.height); - assert_eq!(s[1].head().hash(), s5_header.hash()); - - s[1].pause(); - s[5].pause(); - - println!("test case 6 passed") -} - -pub fn create_wallet( - dir: &str, - client_n: HTTPNodeClient, -) -> Arc>> { - let mut wallet_config = WalletConfig::default(); - wallet_config.data_file_dir = String::from(dir); - let _ = wallet::WalletSeed::init_file(&wallet_config, 32, None, ""); - let mut wallet: LMDBBackend = - LMDBBackend::new(wallet_config.clone(), "", client_n).unwrap_or_else(|e| { - panic!("Error creating wallet: {:?} Config: {:?}", e, wallet_config) - }); - wallet.open_with_credentials().unwrap_or_else(|e| { - panic!( - "Error initializing wallet: {:?} Config: {:?}", - e, wallet_config - ) - }); - Arc::new(Mutex::new(wallet)) -} - -/// Intended to replicate https://github.com/mimblewimble/grin/issues/1325 -#[ignore] -#[test] -fn replicate_tx_fluff_failure() { - util::init_test_logger(); - global::set_local_chain_type(ChainTypes::UserTesting); - framework::clean_all_output("tx_fluff"); - - // Create Wallet 1 (Mining Input) and start it listening - // Wallet 1 post to another node, just for fun - let client1 = HTTPNodeClient::new("http://127.0.0.1:23003", None, std::time::Duration::from_secs(60)); - let client1_w = HTTPWalletCommAdapter::new(); - let wallet1 = create_wallet("target/tmp/tx_fluff/wallet1", client1.clone()); - let _wallet1_handle = thread::spawn(move || { - controller::foreign_listener(wallet1, "127.0.0.1:33000", None) - .unwrap_or_else(|e| panic!("Error creating wallet1 listener: {:?}", e,)); - }); - - // Create Wallet 2 (Recipient) and launch - let client2 = HTTPNodeClient::new("http://127.0.0.1:23001", None, std::time::Duration::from_secs(60)); - let wallet2 = create_wallet("target/tmp/tx_fluff/wallet2", client2.clone()); - let _wallet2_handle = thread::spawn(move || { - controller::foreign_listener(wallet2, "127.0.0.1:33001", None) - .unwrap_or_else(|e| panic!("Error creating wallet2 listener: {:?}", e,)); - }); - - // Server 1 (mines into wallet 1) - let mut s1_config = framework::config(3000, "tx_fluff", 3000); - s1_config.test_miner_wallet_url = Some("http://127.0.0.1:33000".to_owned()); - s1_config.dandelion_config.embargo_secs = Some(10); - s1_config.dandelion_config.patience_secs = Some(1); - s1_config.dandelion_config.relay_secs = Some(1); - let s1 = servers::Server::new(s1_config.clone()).unwrap(); - // Mine off of server 1 - s1.start_test_miner(s1_config.test_miner_wallet_url, s1.stop_state.clone()); - thread::sleep(time::Duration::from_secs(5)); - - // Server 2 (another node) - let mut s2_config = framework::config(3001, "tx_fluff", 3001); - s2_config.p2p_config.seeds = Some(vec![PeerAddr::from_ip("127.0.0.1:13000".parse().unwrap())]); - s2_config.dandelion_config.embargo_secs = Some(10); - s2_config.dandelion_config.patience_secs = Some(1); - s2_config.dandelion_config.relay_secs = Some(1); - let _s2 = servers::Server::new(s2_config.clone()).unwrap(); - - let dl_nodes = 5; - - for i in 0..dl_nodes { - // (create some stem nodes) - let mut s_config = framework::config(3002 + i, "tx_fluff", 3002 + i); - s_config.p2p_config.seeds = - Some(vec![PeerAddr::from_ip("127.0.0.1:13000".parse().unwrap())]); - s_config.dandelion_config.embargo_secs = Some(10); - s_config.dandelion_config.patience_secs = Some(1); - s_config.dandelion_config.relay_secs = Some(1); - let _ = servers::Server::new(s_config.clone()).unwrap(); - } - - thread::sleep(time::Duration::from_secs(10)); - - // get another instance of wallet1 (to update contents and perform a send) - let wallet1 = create_wallet("target/tmp/tx_fluff/wallet1", client1.clone()); - - let amount = 30_000_000_000; - let dest = "http://127.0.0.1:33001"; - - wallet::controller::owner_single_use(wallet1, |api| { - let (mut slate, lock_fn) = api.initiate_tx( - None, amount, // amount - 2, // minimum confirmations - 500, // max outputs - 1000, // num change outputs - true, // select all outputs - None, - )?; - slate = client1_w.send_tx_sync(dest, &slate)?; - slate = api.finalize_tx(&slate)?; - api.tx_lock_outputs(&slate, lock_fn)?; - api.post_tx(&slate.tx, false)?; - Ok(()) - }) - .unwrap(); - - // Give some time for propagation and mining - thread::sleep(time::Duration::from_secs(200)); - - // get another instance of wallet (to check contents) - let wallet2 = create_wallet("target/tmp/tx_fluff/wallet2", client2.clone()); - - wallet::controller::owner_single_use(wallet2, |api| { - let res = api.retrieve_summary_info(true, 1).unwrap(); - assert_eq!(res.1.amount_currently_spendable, amount); - Ok(()) - }) - .unwrap(); -} - -fn get_connected_peers( - base_addr: &String, - api_server_port: u16, -) -> Vec { - let url = format!( - "http://{}:{}/v1/peers/connected", - base_addr, api_server_port - ); - api::client::get::>(url.as_str(), None).unwrap() -} diff --git a/integration/tests/stratum.rs b/integration/tests/stratum.rs deleted file mode 100644 index ac19e790c..000000000 --- a/integration/tests/stratum.rs +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2021 The Grin Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#[macro_use] -extern crate log; - -mod framework; - -use self::core::global::{self, ChainTypes}; -use crate::framework::{config, stratum_config}; -use bufstream::BufStream; -use grin_core as core; -use grin_servers as servers; -use grin_util as util; -use grin_util::{Mutex, StopState}; -use serde_json::Value; -use std::io::prelude::{BufRead, Write}; -use std::net::TcpStream; -use std::process; -use std::sync::Arc; -use std::{thread, time}; - -// Create a grin server, and a stratum server. -// Simulate a few JSONRpc requests and verify the results. -// Validate disconnected workers -// Validate broadcasting new jobs -#[test] -fn basic_stratum_server() { - util::init_test_logger(); - global::set_local_chain_type(ChainTypes::AutomatedTesting); - - let test_name_dir = "stratum_server"; - framework::clean_all_output(test_name_dir); - - // Create a server - let s = servers::Server::new(config(4000, test_name_dir, 0)).unwrap(); - - // Get mining config with stratumserver enabled - let mut stratum_cfg = stratum_config(); - stratum_cfg.burn_reward = true; - stratum_cfg.attempt_time_per_block = 999; - stratum_cfg.enable_stratum_server = Some(true); - stratum_cfg.stratum_server_addr = Some(String::from("127.0.0.1:11101")); - - // Start stratum server - s.start_stratum_server(stratum_cfg); - - // Wait for stratum server to start and - // Verify stratum server accepts connections - loop { - if let Ok(_stream) = TcpStream::connect("127.0.0.1:11101") { - break; - } else { - thread::sleep(time::Duration::from_millis(500)); - } - // As this stream falls out of scope it will be disconnected - } - info!("stratum server connected"); - - // Create a few new worker connections - let mut workers = vec![]; - for _n in 0..5 { - let w = TcpStream::connect("127.0.0.1:11101").unwrap(); - w.set_nonblocking(true) - .expect("Failed to set TcpStream to non-blocking"); - let stream = BufStream::new(w); - workers.push(stream); - } - assert!(workers.len() == 5); - info!("workers length verification ok"); - - // Simulate a worker lost connection - workers.remove(4); - - // Swallow the genesis block - thread::sleep(time::Duration::from_secs(5)); // Wait for the server to broadcast - let mut response = String::new(); - for n in 0..workers.len() { - let _result = workers[n].read_line(&mut response); - } - - // Verify a few stratum JSONRpc commands - // getjobtemplate - expected block template result - let mut response = String::new(); - let job_req = "{\"id\": \"Stratum\", \"jsonrpc\": \"2.0\", \"method\": \"getjobtemplate\"}\n"; - workers[2].write(job_req.as_bytes()).unwrap(); - workers[2].flush().unwrap(); - thread::sleep(time::Duration::from_secs(1)); // Wait for the server to reply - match workers[2].read_line(&mut response) { - Ok(_) => { - let r: Value = serde_json::from_str(&response).unwrap(); - assert_eq!(r["error"], serde_json::Value::Null); - assert_ne!(r["result"], serde_json::Value::Null); - } - Err(_e) => { - assert!(false); - } - } - info!("a few stratum JSONRpc commands verification ok"); - - // keepalive - expected "ok" result - let mut response = String::new(); - let job_req = "{\"id\":\"3\",\"jsonrpc\":\"2.0\",\"method\":\"keepalive\"}\n"; - let ok_resp = "{\"id\":\"3\",\"jsonrpc\":\"2.0\",\"method\":\"keepalive\",\"result\":\"ok\",\"error\":null}\n"; - workers[2].write(job_req.as_bytes()).unwrap(); - workers[2].flush().unwrap(); - thread::sleep(time::Duration::from_secs(1)); // Wait for the server to reply - let _st = workers[2].read_line(&mut response); - assert_eq!(response.as_str(), ok_resp); - info!("keepalive test ok"); - - // "doesnotexist" - error expected - let mut response = String::new(); - let job_req = "{\"id\":\"4\",\"jsonrpc\":\"2.0\",\"method\":\"doesnotexist\"}\n"; - let ok_resp = "{\"id\":\"4\",\"jsonrpc\":\"2.0\",\"method\":\"doesnotexist\",\"result\":null,\"error\":{\"code\":-32601,\"message\":\"Method not found\"}}\n"; - workers[3].write(job_req.as_bytes()).unwrap(); - workers[3].flush().unwrap(); - thread::sleep(time::Duration::from_secs(1)); // Wait for the server to reply - let _st = workers[3].read_line(&mut response); - assert_eq!(response.as_str(), ok_resp); - info!("worker doesnotexist test ok"); - - // Verify stratum server and worker stats - let stats = s.get_server_stats().unwrap(); - assert_eq!(stats.stratum_stats.block_height, 1); // just 1 genesis block - assert_eq!(stats.stratum_stats.num_workers, 4); // 5 - 1 = 4 - assert_eq!(stats.stratum_stats.worker_stats[5].is_connected, false); // worker was removed - assert_eq!(stats.stratum_stats.worker_stats[1].is_connected, true); - info!("stratum server and worker stats verification ok"); - - // Start mining blocks - let stop = Arc::new(Mutex::new(StopState::new())); - s.start_test_miner(None, stop.clone()); - info!("test miner started"); - - // This test is supposed to complete in 3 seconds, - // so let's set a timeout on 10s to avoid infinite waiting happened in Travis-CI. - let _handler = thread::spawn(|| { - thread::sleep(time::Duration::from_secs(10)); - error!("basic_stratum_server test fail on timeout!"); - thread::sleep(time::Duration::from_millis(100)); - process::exit(1); - }); - - // Simulate a worker lost connection - workers.remove(1); - - // Wait for a few mined blocks - thread::sleep(time::Duration::from_secs(3)); - s.stop_test_miner(stop); - - // Verify blocks are being broadcast to workers - let expected = String::from("job"); - let mut jobtemplate = String::new(); - let _st = workers[2].read_line(&mut jobtemplate); - let job_template: Value = serde_json::from_str(&jobtemplate).unwrap(); - assert_eq!(job_template["method"], expected); - info!("blocks broadcasting to workers test ok"); - - // Verify stratum server and worker stats - let stats = s.get_server_stats().unwrap(); - assert_eq!(stats.stratum_stats.num_workers, 3); // 5 - 2 = 3 - assert_eq!(stats.stratum_stats.worker_stats[2].is_connected, false); // worker was removed - assert_ne!(stats.stratum_stats.block_height, 1); - info!("basic_stratum_server test done and ok."); -} diff --git a/integration/tests/wallet_node.rs b/integration/tests/wallet_node.rs new file mode 100644 index 000000000..1bcbe47ca --- /dev/null +++ b/integration/tests/wallet_node.rs @@ -0,0 +1,254 @@ +// Copyright 2026 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Live node + wallet: mine coinbase into a foreign listener and HTTP slate send/receive. + +#[macro_use] +extern crate log; + +mod common; + +use crate::common::{ + clean_all_output, create_wallet, init_chain, node_config, receive_tx_via_http, settle, + start_foreign_listener, start_node, start_test_miner, +}; +use grin_core::consensus; +use grin_core::global; +use grin_util as util; +use grin_wallet_controller as controller; +use grin_wallet_libwallet::InitTxArgs; +use std::{thread, time}; + +/// Start a node, attach a foreign wallet for coinbase, mine, then assert balance. +#[test] +fn mine_to_wallet_and_summary() { + util::init_test_logger(); + init_chain(); + + let test_dir = "mine_to_wallet"; + clean_all_output(test_dir); + + // Node on ports derived from n=5000 → p2p 15000, api 25000 + let n = 5000u16; + let node = start_node(node_config(n, test_dir)); + let node_url = format!("http://127.0.0.1:{}", 20000 + n); + let wallet_port = 35000u16; + let wallet_addr = format!("127.0.0.1:{}", wallet_port); + let wallet_url = format!("http://{}", wallet_addr); + + let (wallet, mask, config_path) = create_wallet(test_dir, "coinbase", &node_url, wallet_port); + let foreign = start_foreign_listener( + wallet.clone(), + mask.clone(), + config_path.clone(), + &wallet_addr, + ); + + // Mine past coinbase maturity so we can assert exact spendable vs immature. + // Coinbase from block h locks until height >= h + maturity, so at chain + // height H there are max(0, H - maturity) mature coinbases and + // min(H, maturity) immature ones. Do not race stop_test_miner against + // the fast test miner (CI was mining through maturity before stop). + let maturity = global::coinbase_maturity(); + let target = maturity + 3; + let miner_stop = start_test_miner(&node, Some(wallet_url)); + let mut waited = 0; + while node.head().unwrap().height < target { + thread::sleep(time::Duration::from_millis(100)); + waited += 1; + assert!(waited < 600, "node did not mine enough blocks"); + } + node.stop_test_miner(miner_stop); + // Allow foreign coinbase processing and miner exit. + thread::sleep(time::Duration::from_secs(2)); + + let height = node.head().unwrap().height; + info!("mined to height {}", height); + assert!( + height >= target, + "expected height {} >= target {}", + height, + target + ); + + // Coinbase lock_height = output_height + maturity → spendable iff h <= H - maturity. + let mature_blocks = height.saturating_sub(maturity); + let immature_blocks = height - mature_blocks; + assert!( + mature_blocks > 0, + "need at least one mature coinbase at height {}", + height + ); + assert_eq!( + immature_blocks, maturity, + "last {} coinbases should still be immature", + maturity + ); + + let mask_ref = mask.as_ref(); + controller::controller::owner_single_use( + wallet.clone(), + mask_ref, + config_path.clone(), + |api, m| { + let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?; + assert!(refreshed, "wallet should refresh against live node"); + assert_eq!(info.last_confirmed_height, height); + // All coinbase from mining goes to this wallet (blocks 1..=height). + assert_eq!( + info.total, + height * consensus::REWARD, + "total should equal all mined coinbase" + ); + assert_eq!( + info.amount_currently_spendable, + mature_blocks * consensus::REWARD, + "spendable should equal mature coinbase only" + ); + assert_eq!( + info.amount_immature, + immature_blocks * consensus::REWARD, + "immature should equal recent coinbase still locked" + ); + Ok(()) + }, + ) + .expect("owner summary"); + + foreign.stop(); + node.stop(); + settle(); +} + +/// Two wallets against one node: mine to A, send slate to B over foreign HTTP, post tx. +#[test] +fn wallet_send_receive_via_http() { + util::init_test_logger(); + init_chain(); + + let test_dir = "wallet_http_send"; + clean_all_output(test_dir); + + let n = 5100u16; + let node = start_node(node_config(n, test_dir)); + let node_url = format!("http://127.0.0.1:{}", 20000 + n); + + let sender_port = 35100u16; + let rec_port = 35101u16; + let sender_addr = format!("127.0.0.1:{}", sender_port); + let rec_addr = format!("127.0.0.1:{}", rec_port); + let rec_url = format!("http://{}", rec_addr); + + let (wallet_a, mask_a, config_a) = create_wallet(test_dir, "wallet_a", &node_url, sender_port); + let (wallet_b, mask_b, config_b) = create_wallet(test_dir, "wallet_b", &node_url, rec_port); + + // Foreign on A for coinbase; foreign on B for receive (real HTTP). + let foreign_a = start_foreign_listener( + wallet_a.clone(), + mask_a.clone(), + config_a.clone(), + &sender_addr, + ); + let foreign_b = start_foreign_listener( + wallet_b.clone(), + mask_b.clone(), + config_b.clone(), + &rec_addr, + ); + + let miner_stop = start_test_miner(&node, Some(format!("http://{}", sender_addr))); + + // Need mature coinbase: AutomatedTesting maturity is 3 blocks. + let target = global::coinbase_maturity() + 3; + let mut waited = 0; + while node.head().unwrap().height < target { + thread::sleep(time::Duration::from_secs(1)); + waited += 1; + assert!(waited < 60, "timeout mining for mature coinbase"); + } + node.stop_test_miner(miner_stop); + thread::sleep(time::Duration::from_secs(2)); + + let amount = consensus::REWARD; + let mask_a_ref = mask_a.as_ref(); + + controller::controller::owner_single_use( + wallet_a.clone(), + mask_a_ref, + config_a.clone(), + |api, m| { + let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?; + assert!(refreshed); + assert!(info.amount_currently_spendable >= amount); + + let args = InitTxArgs { + src_acct_name: None, + amount, + minimum_confirmations: 1, + max_outputs: 500, + num_change_outputs: 1, + selection_strategy_is_use_all: false, + ..Default::default() + }; + let mut slate = api.init_send_tx(m, args)?; + api.tx_lock_outputs(m, &slate)?; + + // Receive on B via its foreign HTTP listener (not an in-process call). + slate = receive_tx_via_http(&rec_url, &slate) + .map_err(|e| grin_wallet_libwallet::Error::GenericError(e))?; + + slate = api.finalize_tx(m, &slate)?; + api.post_tx(m, &slate, false)?; + Ok(()) + }, + ) + .expect("send/receive"); + + // Mine a confirmation block (burn rewards — tx already in pool/chain via post_tx). + let miner_stop = start_test_miner(&node, None); + let h0 = node.head().unwrap().height; + let mut waited = 0; + while node.head().unwrap().height <= h0 { + thread::sleep(time::Duration::from_secs(1)); + waited += 1; + assert!(waited < 30, "timeout mining confirmation"); + } + node.stop_test_miner(miner_stop); + thread::sleep(time::Duration::from_secs(2)); + + // B should hold the received amount. + let mask_b_ref = mask_b.as_ref(); + controller::controller::owner_single_use( + wallet_b.clone(), + mask_b_ref, + config_b.clone(), + |api, m| { + let (refreshed, info) = api.retrieve_summary_info(m, true, 1)?; + assert!(refreshed); + assert!( + info.total >= amount, + "receiver total {} < {}", + info.total, + amount + ); + Ok(()) + }, + ) + .expect("receiver summary"); + + foreign_a.stop(); + foreign_b.stop(); + node.stop(); + settle(); +}