From c4cbcbe4d2971d2108cfe18be13145680ae79305 Mon Sep 17 00:00:00 2001 From: daywalker90 <8257956+daywalker90@users.noreply.github.com> Date: Thu, 23 Apr 2026 00:17:36 +0200 Subject: [PATCH 01/56] tests: update python min to 3.10 --- tests/pyproject.toml | 8 +- tests/uv.lock | 749 ++++++++++--------------------------------- 2 files changed, 173 insertions(+), 584 deletions(-) diff --git a/tests/pyproject.toml b/tests/pyproject.toml index 22e8170..874fadb 100644 --- a/tests/pyproject.toml +++ b/tests/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "cln-nip47" version = "0.1.0" -requires-python = ">=3.9.2" +requires-python = ">=3.10" description = "python dependencies for running tests" [dependency-groups] @@ -11,7 +11,7 @@ dev = [ "pytest-xdist>=3.7,<4", "pytest-timeout>=2.4,<3", "nostr-sdk>=0.44", - "pyln-testing>=25.2", - "pyln-client>=25.2", - "pyln-proto>=25.2", + "pyln-testing>=25.5", + "pyln-client>=25.5", + "pyln-proto>=25.5", ] diff --git a/tests/uv.lock b/tests/uv.lock index dcf72d3..6c3395a 100644 --- a/tests/uv.lock +++ b/tests/uv.lock @@ -1,10 +1,6 @@ version = 1 revision = 3 -requires-python = ">=3.9.2" -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version < '3.10'", -] +requires-python = ">=3.10" [[package]] name = "asn1crypto" @@ -126,19 +122,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a4/a0/fe5dbdfadcba2314551614d023db100e3aea7b09da2cdcbf1386f5c797a6/bitarray-3.8.1-cp314-cp314t-win32.whl", hash = "sha256:cf99e36c0f6ae5643ecef7ad7e1194aeb4a9798d9cff60b20ac041533fa6db0a", size = 144160, upload-time = "2026-04-02T16:28:12.702Z" }, { url = "https://files.pythonhosted.org/packages/4e/0a/e95ed44f6d89e63ed666755fc0773223b10df0058d5de30d529f4cf35948/bitarray-3.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:9befda0dbd27ed95fba1c26be4bf98a49ba166b3c91beb5fc04364c130ce950c", size = 150852, upload-time = "2026-04-02T16:28:14.149Z" }, { url = "https://files.pythonhosted.org/packages/0f/90/454b88b193743b4cd0fce0819a11b1c43b7f629cb2533f6ddc62cbb5e097/bitarray-3.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:4b7d7d10a1c82050efbb9a83d7a43974f70cf8f021afb86463b42e4ac4e5a46b", size = 147343, upload-time = "2026-04-02T16:28:15.632Z" }, - { url = "https://files.pythonhosted.org/packages/f7/f8/ea06b93a45cfe8ee5354872901cce708a13df9c7f39dfd2ef15f1ae95110/bitarray-3.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cac0145491619287ff893853bf3ca4d98d5ef94b617271184a5af68a06ac301a", size = 149077, upload-time = "2026-04-02T16:28:38.465Z" }, - { url = "https://files.pythonhosted.org/packages/bb/1b/b23c5dae859dfff47b29df58c55a6ab8321ef5ea62000624f5fdb11de166/bitarray-3.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fd68db1a0f5d9374a7b735414efe48d2b3ecbf0adea39299bb48030988f16149", size = 146168, upload-time = "2026-04-02T16:28:40.05Z" }, - { url = "https://files.pythonhosted.org/packages/3b/db/4634c969ec9302f13a410a051a25482beb1122729e92ac67267092b43023/bitarray-3.8.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9adacf6fdadeeb96e6c902aef08d02d2f45429fdbf0a75b80307e435156066f8", size = 322386, upload-time = "2026-04-02T16:28:41.715Z" }, - { url = "https://files.pythonhosted.org/packages/81/2e/750330e9896f95fb231f5ae915359bacddb936ff34604fa2a367743b38eb/bitarray-3.8.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d7d5f7f6f80388ce94849775da5f4082ab5e123e259972961970e190d60f5d2b", size = 350659, upload-time = "2026-04-02T16:28:43.39Z" }, - { url = "https://files.pythonhosted.org/packages/86/40/67181c6482d04c720e11947bd2d280d65f136383a2595aa599e738796757/bitarray-3.8.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3a5e594b4be2dbfe021cee8d6d7d96e9bb19dee7ed7be351f43bca7a0619b978", size = 361573, upload-time = "2026-04-02T16:28:45.503Z" }, - { url = "https://files.pythonhosted.org/packages/c4/38/e634dd0256ec0b1bd6c321e6ac207f19e540a9ca0efcabbfcbe21b97331a/bitarray-3.8.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:190a3482818d69faef176171c7cae10d55cb4dd0c686b5aced7f592b5e5591c1", size = 329626, upload-time = "2026-04-02T16:28:47.576Z" }, - { url = "https://files.pythonhosted.org/packages/82/22/a7cf6e9406c5e16cace3fdcbb1c8bc03fb7a538c682e2c1115d3af243503/bitarray-3.8.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4fb869faf4b484cb213199ced1e2732091559107637d429fc25d0a9731f5f630", size = 320007, upload-time = "2026-04-02T16:28:49.226Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e4/22b843b4955eeceabfe564d069ff8569d6365e9d0d435a3bd10c344d50f9/bitarray-3.8.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:70f70ea138e69ec3159e4a38fef52443cb8eb81388aeb241b273265ea16387c5", size = 348717, upload-time = "2026-04-02T16:28:50.996Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8f/a90f460c8034f2e39f577c016b3c01757c7d418b1184e837a15299ec92ff/bitarray-3.8.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:0256d57e294414bfe4fec4f852fd1d9ae361228c71b082332bf81c8b8fc69f80", size = 345234, upload-time = "2026-04-02T16:28:52.695Z" }, - { url = "https://files.pythonhosted.org/packages/d2/ca/8b525f2cb1378d263b8c4f532d06243d34bdcce83a18125b4f51a31624b3/bitarray-3.8.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ef123b6aead12e0784f72970e8d94a96ac0d0aa4438c7ab9235e2f8669a0a5ae", size = 326386, upload-time = "2026-04-02T16:28:54.638Z" }, - { url = "https://files.pythonhosted.org/packages/54/c5/3f0e58127a23be13ac7041579a41ba3a31bbea1659ea26dd3251c942cb3c/bitarray-3.8.1-cp39-cp39-win32.whl", hash = "sha256:c263ed9922942353a954cfbcd5f81b7626c0e20dc7f3e53d4926e8bc560ab845", size = 143145, upload-time = "2026-04-02T16:28:56.431Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2b/b8b125b65891563ddca90d4a53cf47d7128fda40a881825fe863d2e39da6/bitarray-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c133052737c7c75bfa49f5ba71918166fe988995b26a0d2f263a79bf8fed58a", size = 149888, upload-time = "2026-04-02T16:28:58.046Z" }, - { url = "https://files.pythonhosted.org/packages/b0/8a/132afaf9deb6802b51eb2c72f004348c440bbb082d9766788f32a8741604/bitarray-3.8.1-cp39-cp39-win_arm64.whl", hash = "sha256:20e412527ec1aac7e3a6542b32a9c34bb852c954676b05008f0e3d58c390a0ac", size = 146697, upload-time = "2026-04-02T16:28:59.797Z" }, ] [[package]] @@ -165,11 +148,11 @@ wheels = [ [[package]] name = "certifi" -version = "2026.2.25" +version = "2026.4.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, + { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, ] [[package]] @@ -177,8 +160,7 @@ name = "cffi" version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pycparser", version = "2.23", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and implementation_name != 'PyPy'" }, - { name = "pycparser", version = "3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' and implementation_name != 'PyPy'" }, + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } wheels = [ @@ -253,18 +235,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cc/08ed5a43f2996a16b462f64a7055c6e962803534924b9b2f1371d8c00b7b/cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf", size = 184288, upload-time = "2025-09-08T23:23:48.404Z" }, - { url = "https://files.pythonhosted.org/packages/3d/de/38d9726324e127f727b4ecc376bc85e505bfe61ef130eaf3f290c6847dd4/cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7", size = 180509, upload-time = "2025-09-08T23:23:49.73Z" }, - { url = "https://files.pythonhosted.org/packages/9b/13/c92e36358fbcc39cf0962e83223c9522154ee8630e1df7c0b3a39a8124e2/cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c", size = 208813, upload-time = "2025-09-08T23:23:51.263Z" }, - { url = "https://files.pythonhosted.org/packages/15/12/a7a79bd0df4c3bff744b2d7e52cc1b68d5e7e427b384252c42366dc1ecbc/cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165", size = 216498, upload-time = "2025-09-08T23:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/a3/ad/5c51c1c7600bdd7ed9a24a203ec255dccdd0ebf4527f7b922a0bde2fb6ed/cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534", size = 203243, upload-time = "2025-09-08T23:23:53.836Z" }, - { url = "https://files.pythonhosted.org/packages/32/f2/81b63e288295928739d715d00952c8c6034cb6c6a516b17d37e0c8be5600/cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f", size = 203158, upload-time = "2025-09-08T23:23:55.169Z" }, - { url = "https://files.pythonhosted.org/packages/1f/74/cc4096ce66f5939042ae094e2e96f53426a979864aa1f96a621ad128be27/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63", size = 216548, upload-time = "2025-09-08T23:23:56.506Z" }, - { url = "https://files.pythonhosted.org/packages/e8/be/f6424d1dc46b1091ffcc8964fa7c0ab0cd36839dd2761b49c90481a6ba1b/cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2", size = 218897, upload-time = "2025-09-08T23:23:57.825Z" }, - { url = "https://files.pythonhosted.org/packages/f7/e0/dda537c2309817edf60109e39265f24f24aa7f050767e22c98c53fe7f48b/cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65", size = 211249, upload-time = "2025-09-08T23:23:59.139Z" }, - { url = "https://files.pythonhosted.org/packages/2b/e7/7c769804eb75e4c4b35e658dba01de1640a351a9653c3d49ca89d16ccc91/cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322", size = 218041, upload-time = "2025-09-08T23:24:00.496Z" }, - { url = "https://files.pythonhosted.org/packages/aa/d9/6218d78f920dcd7507fc16a766b5ef8f3b913cc7aa938e7fc80b9978d089/cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a", size = 172138, upload-time = "2025-09-08T23:24:01.7Z" }, - { url = "https://files.pythonhosted.org/packages/54/8f/a1e836f82d8e32a97e6b29cc8f641779181ac7363734f12df27db803ebda/cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9", size = 182794, upload-time = "2025-09-08T23:24:02.943Z" }, ] [[package]] @@ -369,22 +339,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/01/1b/ef725f8eb19b5a261b30f78efa9252ef9d017985cb499102f6f49834cd12/charset_normalizer-3.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217", size = 299121, upload-time = "2026-04-02T09:28:14.372Z" }, - { url = "https://files.pythonhosted.org/packages/a3/22/2f12878fbc680fbbb52386cd39a379801f62eaca74fc8b323381325f0f04/charset_normalizer-3.4.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5", size = 200612, upload-time = "2026-04-02T09:28:16.162Z" }, - { url = "https://files.pythonhosted.org/packages/bc/b6/10c84e789126ca97d4a7228863a30481e786980a8b8cfcbf4f30658ca63c/charset_normalizer-3.4.7-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9", size = 221041, upload-time = "2026-04-02T09:28:17.554Z" }, - { url = "https://files.pythonhosted.org/packages/21/7b/c414866a138400b2e81973d006da7f694cfeaf895ef07d2cba9a8743841a/charset_normalizer-3.4.7-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a", size = 216323, upload-time = "2026-04-02T09:28:18.863Z" }, - { url = "https://files.pythonhosted.org/packages/2e/92/bdcf94997e06b223d826df3abed45a5ad6e17f609b7df9d25cd23b5bde30/charset_normalizer-3.4.7-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc", size = 208419, upload-time = "2026-04-02T09:28:20.332Z" }, - { url = "https://files.pythonhosted.org/packages/1a/64/3f9142293c88b1b10e199649ed1330f070c2a68e305335a5819fa7f25fa7/charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00", size = 195016, upload-time = "2026-04-02T09:28:21.657Z" }, - { url = "https://files.pythonhosted.org/packages/c1/d1/d8a6b7dd5c5636b76ce0d080bc57d8e56c7bbd6bc2ac941529a35e41d84a/charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776", size = 206115, upload-time = "2026-04-02T09:28:23.259Z" }, - { url = "https://files.pythonhosted.org/packages/dd/8c/60ebe912379627d023eb96995b40bc50308729f210f43d66109ca0a7bbd2/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319", size = 204022, upload-time = "2026-04-02T09:28:24.779Z" }, - { url = "https://files.pythonhosted.org/packages/d5/2a/41816ceda78a551cbfdfbeab6f3891152b0e3f758ce6580c2c18c829f774/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24", size = 195914, upload-time = "2026-04-02T09:28:26.181Z" }, - { url = "https://files.pythonhosted.org/packages/8f/9b/7c7f4b7f11525fcbdfba752455314ac60646bae91cdd671d531c1f7a97c6/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42", size = 222159, upload-time = "2026-04-02T09:28:27.504Z" }, - { url = "https://files.pythonhosted.org/packages/9f/57/301682e7469bdbfa2ce219a804f0668b2266ab8520570d85d3b3ef483ea3/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4", size = 206154, upload-time = "2026-04-02T09:28:28.848Z" }, - { url = "https://files.pythonhosted.org/packages/20/ec/90339ff5cdc598b265748c1f231c7d7fbd9123a92cee10f757e0b1448de4/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67", size = 217423, upload-time = "2026-04-02T09:28:30.248Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e7/a7a6147f8e3375676309cf584b25c72a3bab784ea4085b0011fa07b23aeb/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274", size = 210604, upload-time = "2026-04-02T09:28:31.736Z" }, - { url = "https://files.pythonhosted.org/packages/1a/62/d9340c7a79c393e57807d7fb6c57e82060687891f81b74d3201958b919c1/charset_normalizer-3.4.7-cp39-cp39-win32.whl", hash = "sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366", size = 144631, upload-time = "2026-04-02T09:28:33.158Z" }, - { url = "https://files.pythonhosted.org/packages/21/e7/92901117e2ddc8facfe8235a3ecd4eb482185b2ad5d5b6606b37c1afea06/charset_normalizer-3.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444", size = 154710, upload-time = "2026-04-02T09:28:34.557Z" }, - { url = "https://files.pythonhosted.org/packages/cc/4f/e1fb138201ad9a32499dd9a98aa4a5a5441fbf7f56b52b619a54b7ee8777/charset_normalizer-3.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c", size = 143716, upload-time = "2026-04-02T09:28:35.908Z" }, { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, ] @@ -394,8 +348,7 @@ version = "10.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jaraco-functools" }, - { name = "more-itertools", version = "10.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "more-itertools", version = "11.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "more-itertools" }, ] sdist = { url = "https://files.pythonhosted.org/packages/08/7c/95c154177b16077de0fec1b821b0d8b3df2b59c5c7b3575a9c1bf52a437e/cheroot-10.0.0.tar.gz", hash = "sha256:59c4a1877fef9969b3c3c080caaaf377e2780919437853fc0d32a9df40b311f0", size = 148461, upload-time = "2023-05-20T15:34:09.67Z" } wheels = [ @@ -404,32 +357,14 @@ wheels = [ [[package]] name = "click" -version = "8.1.8" +version = "8.3.3" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] dependencies = [ - { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, + { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, ] [[package]] @@ -444,8 +379,7 @@ dev = [ { name = "pyln-proto" }, { name = "pyln-testing" }, { name = "pytest" }, - { name = "pytest-asyncio", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pytest-asyncio", version = "1.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pytest-asyncio" }, { name = "pytest-timeout" }, { name = "pytest-xdist" }, ] @@ -455,9 +389,9 @@ dev = [ [package.metadata.requires-dev] dev = [ { name = "nostr-sdk", specifier = ">=0.44" }, - { name = "pyln-client", specifier = ">=25.2" }, - { name = "pyln-proto", specifier = ">=25.2" }, - { name = "pyln-testing", specifier = ">=25.2" }, + { name = "pyln-client", specifier = ">=25.5" }, + { name = "pyln-proto", specifier = ">=25.5" }, + { name = "pyln-testing", specifier = ">=25.5" }, { name = "pytest", specifier = ">=7.4,<9" }, { name = "pytest-asyncio", specifier = ">=0.23.8,<2" }, { name = "pytest-timeout", specifier = ">=2.4,<3" }, @@ -504,16 +438,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/cd/e9bd4ca7d931653a35c74194da04191a9aecc54b8f48a554cd538dc810e4/coincurve-20.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0530b9dd02fc6f6c2916716974b79bdab874227f560c422801ade290e3fc5013", size = 1209392, upload-time = "2024-06-02T18:15:08.663Z" }, { url = "https://files.pythonhosted.org/packages/99/54/260053f14f74b99b645084231e1c76994134ded49407a3bba23a8ffc0ff6/coincurve-20.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:eacf9c0ce8739c84549a89c083b1f3526c8780b84517ee75d6b43d276e55f8a0", size = 1198932, upload-time = "2024-06-02T18:15:10.786Z" }, { url = "https://files.pythonhosted.org/packages/b4/b5/c465e09345dd38b9415f5d47ae7683b3f461db02fcc03e699b6b5687ab2b/coincurve-20.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:52a67bfddbd6224dfa42085c88ad176559801b57d6a8bd30d92ee040de88b7b3", size = 1193324, upload-time = "2024-06-02T18:15:12.511Z" }, - { url = "https://files.pythonhosted.org/packages/96/af/bcb965cfa8248035303e22c73ff481cd65d41b5bc3115b95cc5ca49f67e6/coincurve-20.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4870047704cddaae7f0266a549c927407c2ba0ec92d689e3d2b511736812a905", size = 1255631, upload-time = "2024-06-02T18:15:35.257Z" }, - { url = "https://files.pythonhosted.org/packages/8c/44/ce5011566ad0969d199fa2cf9f413700505d1ec7a9cb2a6e707b22e5dc50/coincurve-20.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81ce41263517b0a9f43cd570c87720b3c13324929584fa28d2e4095969b6015d", size = 1255531, upload-time = "2024-06-02T18:15:36.699Z" }, - { url = "https://files.pythonhosted.org/packages/11/bf/9ebd8689991bbef2b3e67e87b394e54d77476de0eb2949199e08a1a11433/coincurve-20.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:572083ccce6c7b514d482f25f394368f4ae888f478bd0b067519d33160ea2fcc", size = 1191926, upload-time = "2024-06-02T18:15:38.147Z" }, - { url = "https://files.pythonhosted.org/packages/b9/5a/c24218c701096eaa65ad3f1f87f674d2339c81f898c392cdfcb8a51065ca/coincurve-20.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee5bc78a31a2f1370baf28aaff3949bc48f940a12b0359d1cd2c4115742874e6", size = 1194361, upload-time = "2024-06-02T18:15:39.712Z" }, - { url = "https://files.pythonhosted.org/packages/d2/af/ec0ae57975cb9f1c8b207058a94f86c9526f3e96df799f8833acdcfed250/coincurve-20.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2895d032e281c4e747947aae4bcfeef7c57eabfd9be22886c0ca4e1365c7c1f", size = 1204656, upload-time = "2024-06-02T18:15:41.534Z" }, - { url = "https://files.pythonhosted.org/packages/ce/cd/c40e9419e7a9ac35f754b120aa7bdeac929f544079ade5d31b5e1c496cf6/coincurve-20.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d3e2f21957ada0e1742edbde117bb41758fa8691b69c8d186c23e9e522ea71cd", size = 1215294, upload-time = "2024-06-02T18:15:42.887Z" }, - { url = "https://files.pythonhosted.org/packages/55/c2/1cedcf828b35ea89e2501cb64ccc441c3f17bce9c95b2537a2c136258d24/coincurve-20.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c2baa26b1aad1947ca07b3aa9e6a98940c5141c6bdd0f9b44d89e36da7282ffa", size = 1204502, upload-time = "2024-06-02T18:15:44.759Z" }, - { url = "https://files.pythonhosted.org/packages/49/ef/7a6b50d8a14bae6974482900b06281fee3f82228401ae01efbf8d87ef4e4/coincurve-20.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7eacc7944ddf9e2b7448ecbe84753841ab9874b8c332a4f5cc3b2f184db9f4a2", size = 1209295, upload-time = "2024-06-02T18:15:46.162Z" }, - { url = "https://files.pythonhosted.org/packages/26/c5/f010fe5377d775084e91a213f63417df4b77d833ae69bfbcf00aa7ae7020/coincurve-20.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:c293c095dc690178b822cadaaeb81de3cc0d28f8bdf8216ed23551dcce153a26", size = 1198937, upload-time = "2024-06-02T18:15:48.049Z" }, - { url = "https://files.pythonhosted.org/packages/9e/a8/491506c17c911b69b171aa6340e8b70ec4ee42f6f42f93cd53db33c5d1ac/coincurve-20.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:11a47083a0b7092d3eb50929f74ffd947c4a5e7035796b81310ea85289088c7a", size = 1193323, upload-time = "2024-06-02T18:15:49.598Z" }, ] [[package]] @@ -527,62 +451,62 @@ wheels = [ [[package]] name = "cryptography" -version = "46.0.6" +version = "46.0.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a4/ba/04b1bd4218cbc58dc90ce967106d51582371b898690f3ae0402876cc4f34/cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759", size = 750542, upload-time = "2026-03-25T23:34:53.396Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", size = 750652, upload-time = "2026-04-08T01:57:54.692Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/23/9285e15e3bc57325b0a72e592921983a701efc1ee8f91c06c5f0235d86d9/cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8", size = 7176401, upload-time = "2026-03-25T23:33:22.096Z" }, - { url = "https://files.pythonhosted.org/packages/60/f8/e61f8f13950ab6195b31913b42d39f0f9afc7d93f76710f299b5ec286ae6/cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30", size = 4275275, upload-time = "2026-03-25T23:33:23.844Z" }, - { url = "https://files.pythonhosted.org/packages/19/69/732a736d12c2631e140be2348b4ad3d226302df63ef64d30dfdb8db7ad1c/cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a", size = 4425320, upload-time = "2026-03-25T23:33:25.703Z" }, - { url = "https://files.pythonhosted.org/packages/d4/12/123be7292674abf76b21ac1fc0e1af50661f0e5b8f0ec8285faac18eb99e/cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175", size = 4278082, upload-time = "2026-03-25T23:33:27.423Z" }, - { url = "https://files.pythonhosted.org/packages/5b/ba/d5e27f8d68c24951b0a484924a84c7cdaed7502bac9f18601cd357f8b1d2/cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463", size = 4926514, upload-time = "2026-03-25T23:33:29.206Z" }, - { url = "https://files.pythonhosted.org/packages/34/71/1ea5a7352ae516d5512d17babe7e1b87d9db5150b21f794b1377eac1edc0/cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97", size = 4457766, upload-time = "2026-03-25T23:33:30.834Z" }, - { url = "https://files.pythonhosted.org/packages/01/59/562be1e653accee4fdad92c7a2e88fced26b3fdfce144047519bbebc299e/cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c", size = 3986535, upload-time = "2026-03-25T23:33:33.02Z" }, - { url = "https://files.pythonhosted.org/packages/d6/8b/b1ebfeb788bf4624d36e45ed2662b8bd43a05ff62157093c1539c1288a18/cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507", size = 4277618, upload-time = "2026-03-25T23:33:34.567Z" }, - { url = "https://files.pythonhosted.org/packages/dd/52/a005f8eabdb28df57c20f84c44d397a755782d6ff6d455f05baa2785bd91/cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19", size = 4890802, upload-time = "2026-03-25T23:33:37.034Z" }, - { url = "https://files.pythonhosted.org/packages/ec/4d/8e7d7245c79c617d08724e2efa397737715ca0ec830ecb3c91e547302555/cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738", size = 4457425, upload-time = "2026-03-25T23:33:38.904Z" }, - { url = "https://files.pythonhosted.org/packages/1d/5c/f6c3596a1430cec6f949085f0e1a970638d76f81c3ea56d93d564d04c340/cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c", size = 4405530, upload-time = "2026-03-25T23:33:40.842Z" }, - { url = "https://files.pythonhosted.org/packages/7e/c9/9f9cea13ee2dbde070424e0c4f621c091a91ffcc504ffea5e74f0e1daeff/cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f", size = 4667896, upload-time = "2026-03-25T23:33:42.781Z" }, - { url = "https://files.pythonhosted.org/packages/ad/b5/1895bc0821226f129bc74d00eccfc6a5969e2028f8617c09790bf89c185e/cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2", size = 3026348, upload-time = "2026-03-25T23:33:45.021Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f8/c9bcbf0d3e6ad288b9d9aa0b1dee04b063d19e8c4f871855a03ab3a297ab/cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124", size = 3483896, upload-time = "2026-03-25T23:33:46.649Z" }, - { url = "https://files.pythonhosted.org/packages/01/41/3a578f7fd5c70611c0aacba52cd13cb364a5dee895a5c1d467208a9380b0/cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275", size = 7117147, upload-time = "2026-03-25T23:33:48.249Z" }, - { url = "https://files.pythonhosted.org/packages/fa/87/887f35a6fca9dde90cad08e0de0c89263a8e59b2d2ff904fd9fcd8025b6f/cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4", size = 4266221, upload-time = "2026-03-25T23:33:49.874Z" }, - { url = "https://files.pythonhosted.org/packages/aa/a8/0a90c4f0b0871e0e3d1ed126aed101328a8a57fd9fd17f00fb67e82a51ca/cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b", size = 4408952, upload-time = "2026-03-25T23:33:52.128Z" }, - { url = "https://files.pythonhosted.org/packages/16/0b/b239701eb946523e4e9f329336e4ff32b1247e109cbab32d1a7b61da8ed7/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707", size = 4270141, upload-time = "2026-03-25T23:33:54.11Z" }, - { url = "https://files.pythonhosted.org/packages/0f/a8/976acdd4f0f30df7b25605f4b9d3d89295351665c2091d18224f7ad5cdbf/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361", size = 4904178, upload-time = "2026-03-25T23:33:55.725Z" }, - { url = "https://files.pythonhosted.org/packages/b1/1b/bf0e01a88efd0e59679b69f42d4afd5bced8700bb5e80617b2d63a3741af/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b", size = 4441812, upload-time = "2026-03-25T23:33:57.364Z" }, - { url = "https://files.pythonhosted.org/packages/bb/8b/11df86de2ea389c65aa1806f331cae145f2ed18011f30234cc10ca253de8/cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca", size = 3963923, upload-time = "2026-03-25T23:33:59.361Z" }, - { url = "https://files.pythonhosted.org/packages/91/e0/207fb177c3a9ef6a8108f234208c3e9e76a6aa8cf20d51932916bd43bda0/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013", size = 4269695, upload-time = "2026-03-25T23:34:00.909Z" }, - { url = "https://files.pythonhosted.org/packages/21/5e/19f3260ed1e95bced52ace7501fabcd266df67077eeb382b79c81729d2d3/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4", size = 4869785, upload-time = "2026-03-25T23:34:02.796Z" }, - { url = "https://files.pythonhosted.org/packages/10/38/cd7864d79aa1d92ef6f1a584281433419b955ad5a5ba8d1eb6c872165bcb/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a", size = 4441404, upload-time = "2026-03-25T23:34:04.35Z" }, - { url = "https://files.pythonhosted.org/packages/09/0a/4fe7a8d25fed74419f91835cf5829ade6408fd1963c9eae9c4bce390ecbb/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d", size = 4397549, upload-time = "2026-03-25T23:34:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/5f/a0/7d738944eac6513cd60a8da98b65951f4a3b279b93479a7e8926d9cd730b/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736", size = 4651874, upload-time = "2026-03-25T23:34:07.916Z" }, - { url = "https://files.pythonhosted.org/packages/cb/f1/c2326781ca05208845efca38bf714f76939ae446cd492d7613808badedf1/cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed", size = 3001511, upload-time = "2026-03-25T23:34:09.892Z" }, - { url = "https://files.pythonhosted.org/packages/c9/57/fe4a23eb549ac9d903bd4698ffda13383808ef0876cc912bcb2838799ece/cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4", size = 3471692, upload-time = "2026-03-25T23:34:11.613Z" }, - { url = "https://files.pythonhosted.org/packages/c4/cc/f330e982852403da79008552de9906804568ae9230da8432f7496ce02b71/cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a", size = 7162776, upload-time = "2026-03-25T23:34:13.308Z" }, - { url = "https://files.pythonhosted.org/packages/49/b3/dc27efd8dcc4bff583b3f01d4a3943cd8b5821777a58b3a6a5f054d61b79/cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8", size = 4270529, upload-time = "2026-03-25T23:34:15.019Z" }, - { url = "https://files.pythonhosted.org/packages/e6/05/e8d0e6eb4f0d83365b3cb0e00eb3c484f7348db0266652ccd84632a3d58d/cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77", size = 4414827, upload-time = "2026-03-25T23:34:16.604Z" }, - { url = "https://files.pythonhosted.org/packages/2f/97/daba0f5d2dc6d855e2dcb70733c812558a7977a55dd4a6722756628c44d1/cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290", size = 4271265, upload-time = "2026-03-25T23:34:18.586Z" }, - { url = "https://files.pythonhosted.org/packages/89/06/fe1fce39a37ac452e58d04b43b0855261dac320a2ebf8f5260dd55b201a9/cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410", size = 4916800, upload-time = "2026-03-25T23:34:20.561Z" }, - { url = "https://files.pythonhosted.org/packages/ff/8a/b14f3101fe9c3592603339eb5d94046c3ce5f7fc76d6512a2d40efd9724e/cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d", size = 4448771, upload-time = "2026-03-25T23:34:22.406Z" }, - { url = "https://files.pythonhosted.org/packages/01/b3/0796998056a66d1973fd52ee89dc1bb3b6581960a91ad4ac705f182d398f/cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70", size = 3978333, upload-time = "2026-03-25T23:34:24.281Z" }, - { url = "https://files.pythonhosted.org/packages/c5/3d/db200af5a4ffd08918cd55c08399dc6c9c50b0bc72c00a3246e099d3a849/cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d", size = 4271069, upload-time = "2026-03-25T23:34:25.895Z" }, - { url = "https://files.pythonhosted.org/packages/d7/18/61acfd5b414309d74ee838be321c636fe71815436f53c9f0334bf19064fa/cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa", size = 4878358, upload-time = "2026-03-25T23:34:27.67Z" }, - { url = "https://files.pythonhosted.org/packages/8b/65/5bf43286d566f8171917cae23ac6add941654ccf085d739195a4eacf1674/cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58", size = 4448061, upload-time = "2026-03-25T23:34:29.375Z" }, - { url = "https://files.pythonhosted.org/packages/e0/25/7e49c0fa7205cf3597e525d156a6bce5b5c9de1fd7e8cb01120e459f205a/cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb", size = 4399103, upload-time = "2026-03-25T23:34:32.036Z" }, - { url = "https://files.pythonhosted.org/packages/44/46/466269e833f1c4718d6cd496ffe20c56c9c8d013486ff66b4f69c302a68d/cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72", size = 4659255, upload-time = "2026-03-25T23:34:33.679Z" }, - { url = "https://files.pythonhosted.org/packages/0a/09/ddc5f630cc32287d2c953fc5d32705e63ec73e37308e5120955316f53827/cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c", size = 3010660, upload-time = "2026-03-25T23:34:35.418Z" }, - { url = "https://files.pythonhosted.org/packages/1b/82/ca4893968aeb2709aacfb57a30dec6fa2ab25b10fa9f064b8882ce33f599/cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f", size = 3471160, upload-time = "2026-03-25T23:34:37.191Z" }, - { url = "https://files.pythonhosted.org/packages/2e/84/7ccff00ced5bac74b775ce0beb7d1be4e8637536b522b5df9b73ada42da2/cryptography-46.0.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:2ea0f37e9a9cf0df2952893ad145fd9627d326a59daec9b0802480fa3bcd2ead", size = 3475444, upload-time = "2026-03-25T23:34:38.944Z" }, - { url = "https://files.pythonhosted.org/packages/bc/1f/4c926f50df7749f000f20eede0c896769509895e2648db5da0ed55db711d/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a3e84d5ec9ba01f8fd03802b2147ba77f0c8f2617b2aff254cedd551844209c8", size = 4218227, upload-time = "2026-03-25T23:34:40.871Z" }, - { url = "https://files.pythonhosted.org/packages/c6/65/707be3ffbd5f786028665c3223e86e11c4cda86023adbc56bd72b1b6bab5/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:12f0fa16cc247b13c43d56d7b35287ff1569b5b1f4c5e87e92cc4fcc00cd10c0", size = 4381399, upload-time = "2026-03-25T23:34:42.609Z" }, - { url = "https://files.pythonhosted.org/packages/f3/6d/73557ed0ef7d73d04d9aba745d2c8e95218213687ee5e76b7d236a5030fc/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:50575a76e2951fe7dbd1f56d181f8c5ceeeb075e9ff88e7ad997d2f42af06e7b", size = 4217595, upload-time = "2026-03-25T23:34:44.205Z" }, - { url = "https://files.pythonhosted.org/packages/9e/c5/e1594c4eec66a567c3ac4400008108a415808be2ce13dcb9a9045c92f1a0/cryptography-46.0.6-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:90e5f0a7b3be5f40c3a0a0eafb32c681d8d2c181fc2a1bdabe9b3f611d9f6b1a", size = 4380912, upload-time = "2026-03-25T23:34:46.328Z" }, - { url = "https://files.pythonhosted.org/packages/1a/89/843b53614b47f97fe1abc13f9a86efa5ec9e275292c457af1d4a60dc80e0/cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e", size = 3409955, upload-time = "2026-03-25T23:34:48.465Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/4a8f770695d73be252331e60e526291e3df0c9b27556a90a6b47bccca4c2/cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4", size = 7179869, upload-time = "2026-04-08T01:56:17.157Z" }, + { url = "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", size = 4275492, upload-time = "2026-04-08T01:56:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", size = 4426670, upload-time = "2026-04-08T01:56:21.415Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", size = 4280275, upload-time = "2026-04-08T01:56:23.539Z" }, + { url = "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", size = 4928402, upload-time = "2026-04-08T01:56:25.624Z" }, + { url = "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", size = 4459985, upload-time = "2026-04-08T01:56:27.309Z" }, + { url = "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de", size = 3990652, upload-time = "2026-04-08T01:56:29.095Z" }, + { url = "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", size = 4279805, upload-time = "2026-04-08T01:56:30.928Z" }, + { url = "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", size = 4892883, upload-time = "2026-04-08T01:56:32.614Z" }, + { url = "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", size = 4459756, upload-time = "2026-04-08T01:56:34.306Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", size = 4410244, upload-time = "2026-04-08T01:56:35.977Z" }, + { url = "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", size = 4674868, upload-time = "2026-04-08T01:56:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/1a/bb/a5c213c19ee94b15dfccc48f363738633a493812687f5567addbcbba9f6f/cryptography-46.0.7-cp311-abi3-win32.whl", hash = "sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457", size = 3026504, upload-time = "2026-04-08T01:56:39.666Z" }, + { url = "https://files.pythonhosted.org/packages/2b/02/7788f9fefa1d060ca68717c3901ae7fffa21ee087a90b7f23c7a603c32ae/cryptography-46.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b", size = 3488363, upload-time = "2026-04-08T01:56:41.893Z" }, + { url = "https://files.pythonhosted.org/packages/7b/56/15619b210e689c5403bb0540e4cb7dbf11a6bf42e483b7644e471a2812b3/cryptography-46.0.7-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842", size = 7119671, upload-time = "2026-04-08T01:56:44Z" }, + { url = "https://files.pythonhosted.org/packages/74/66/e3ce040721b0b5599e175ba91ab08884c75928fbeb74597dd10ef13505d2/cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c", size = 4268551, upload-time = "2026-04-08T01:56:46.071Z" }, + { url = "https://files.pythonhosted.org/packages/03/11/5e395f961d6868269835dee1bafec6a1ac176505a167f68b7d8818431068/cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902", size = 4408887, upload-time = "2026-04-08T01:56:47.718Z" }, + { url = "https://files.pythonhosted.org/packages/40/53/8ed1cf4c3b9c8e611e7122fb56f1c32d09e1fff0f1d77e78d9ff7c82653e/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d", size = 4271354, upload-time = "2026-04-08T01:56:49.312Z" }, + { url = "https://files.pythonhosted.org/packages/50/46/cf71e26025c2e767c5609162c866a78e8a2915bbcfa408b7ca495c6140c4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022", size = 4905845, upload-time = "2026-04-08T01:56:50.916Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ea/01276740375bac6249d0a971ebdf6b4dc9ead0ee0a34ef3b5a88c1a9b0d4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce", size = 4444641, upload-time = "2026-04-08T01:56:52.882Z" }, + { url = "https://files.pythonhosted.org/packages/3d/4c/7d258f169ae71230f25d9f3d06caabcff8c3baf0978e2b7d65e0acac3827/cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f", size = 3967749, upload-time = "2026-04-08T01:56:54.597Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2a/2ea0767cad19e71b3530e4cad9605d0b5e338b6a1e72c37c9c1ceb86c333/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99", size = 4270942, upload-time = "2026-04-08T01:56:56.416Z" }, + { url = "https://files.pythonhosted.org/packages/41/3d/fe14df95a83319af25717677e956567a105bb6ab25641acaa093db79975d/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1", size = 4871079, upload-time = "2026-04-08T01:56:58.31Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/4a479e0f36f8f378d397f4eab4c850b4ffb79a2f0d58704b8fa0703ddc11/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2", size = 4443999, upload-time = "2026-04-08T01:57:00.508Z" }, + { url = "https://files.pythonhosted.org/packages/28/17/b59a741645822ec6d04732b43c5d35e4ef58be7bfa84a81e5ae6f05a1d33/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e", size = 4399191, upload-time = "2026-04-08T01:57:02.654Z" }, + { url = "https://files.pythonhosted.org/packages/59/6a/bb2e166d6d0e0955f1e9ff70f10ec4b2824c9cfcdb4da772c7dd69cc7d80/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee", size = 4655782, upload-time = "2026-04-08T01:57:04.592Z" }, + { url = "https://files.pythonhosted.org/packages/95/b6/3da51d48415bcb63b00dc17c2eff3a651b7c4fed484308d0f19b30e8cb2c/cryptography-46.0.7-cp314-cp314t-win32.whl", hash = "sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298", size = 3002227, upload-time = "2026-04-08T01:57:06.91Z" }, + { url = "https://files.pythonhosted.org/packages/32/a8/9f0e4ed57ec9cebe506e58db11ae472972ecb0c659e4d52bbaee80ca340a/cryptography-46.0.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb", size = 3475332, upload-time = "2026-04-08T01:57:08.807Z" }, + { url = "https://files.pythonhosted.org/packages/a7/7f/cd42fc3614386bc0c12f0cb3c4ae1fc2bbca5c9662dfed031514911d513d/cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4", size = 7165618, upload-time = "2026-04-08T01:57:10.645Z" }, + { url = "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", size = 4270628, upload-time = "2026-04-08T01:57:12.885Z" }, + { url = "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", size = 4415405, upload-time = "2026-04-08T01:57:14.923Z" }, + { url = "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", size = 4272715, upload-time = "2026-04-08T01:57:16.638Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", size = 4918400, upload-time = "2026-04-08T01:57:19.021Z" }, + { url = "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", size = 4450634, upload-time = "2026-04-08T01:57:21.185Z" }, + { url = "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", size = 3985233, upload-time = "2026-04-08T01:57:22.862Z" }, + { url = "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", size = 4271955, upload-time = "2026-04-08T01:57:24.814Z" }, + { url = "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", size = 4879888, upload-time = "2026-04-08T01:57:26.88Z" }, + { url = "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", size = 4449961, upload-time = "2026-04-08T01:57:29.068Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", size = 4401696, upload-time = "2026-04-08T01:57:31.029Z" }, + { url = "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", size = 4664256, upload-time = "2026-04-08T01:57:33.144Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fa/f0ab06238e899cc3fb332623f337a7364f36f4bb3f2534c2bb95a35b132c/cryptography-46.0.7-cp38-abi3-win32.whl", hash = "sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246", size = 3013001, upload-time = "2026-04-08T01:57:34.933Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f1/00ce3bde3ca542d1acd8f8cfa38e446840945aa6363f9b74746394b14127/cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3", size = 3472985, upload-time = "2026-04-08T01:57:36.714Z" }, + { url = "https://files.pythonhosted.org/packages/63/0c/dca8abb64e7ca4f6b2978769f6fea5ad06686a190cec381f0a796fdcaaba/cryptography-46.0.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f", size = 3476879, upload-time = "2026-04-08T01:57:38.664Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ea/075aac6a84b7c271578d81a2f9968acb6e273002408729f2ddff517fed4a/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15", size = 4219700, upload-time = "2026-04-08T01:57:40.625Z" }, + { url = "https://files.pythonhosted.org/packages/6c/7b/1c55db7242b5e5612b29fc7a630e91ee7a6e3c8e7bf5406d22e206875fbd/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455", size = 4385982, upload-time = "2026-04-08T01:57:42.725Z" }, + { url = "https://files.pythonhosted.org/packages/cb/da/9870eec4b69c63ef5925bf7d8342b7e13bc2ee3d47791461c4e49ca212f4/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65", size = 4219115, upload-time = "2026-04-08T01:57:44.939Z" }, + { url = "https://files.pythonhosted.org/packages/f4/72/05aa5832b82dd341969e9a734d1812a6aadb088d9eb6f0430fc337cc5a8f/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968", size = 4385479, upload-time = "2026-04-08T01:57:46.86Z" }, + { url = "https://files.pythonhosted.org/packages/20/2a/1b016902351a523aa2bd446b50a5bc1175d7a7d1cf90fe2ef904f9b84ebc/cryptography-46.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4", size = 3412829, upload-time = "2026-04-08T01:57:48.874Z" }, ] [[package]] @@ -621,9 +545,7 @@ version = "3.1.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "blinker" }, - { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "click", version = "8.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "click" }, { name = "itsdangerous" }, { name = "jinja2" }, { name = "markupsafe" }, @@ -636,44 +558,17 @@ wheels = [ [[package]] name = "idna" -version = "3.11" +version = "3.13" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/cc/762dfb036166873f0059f3b7de4565e1b5bc3d6f28a414c13da27e442f99/idna-3.13.tar.gz", hash = "sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242", size = 194210, upload-time = "2026-04-22T16:42:42.314Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "importlib-metadata" -version = "8.7.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, + { url = "https://files.pythonhosted.org/packages/5d/13/ad7d7ca3808a898b4612b6fe93cde56b53f3034dcde235acb1f0e1df24c6/idna-3.13-py3-none-any.whl", hash = "sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3", size = 68629, upload-time = "2026-04-22T16:42:40.909Z" }, ] [[package]] name = "iniconfig" version = "2.3.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, @@ -693,8 +588,7 @@ name = "jaraco-functools" version = "4.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "more-itertools", version = "10.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "more-itertools", version = "11.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "more-itertools" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } wheels = [ @@ -713,36 +607,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] -[[package]] -name = "jsonschema" -version = "4.25.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "attrs", marker = "python_full_version < '3.10'" }, - { name = "jsonschema-specifications", marker = "python_full_version < '3.10'" }, - { name = "referencing", version = "0.36.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "rpds-py", version = "0.27.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342, upload-time = "2025-08-18T17:03:50.038Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" }, -] - [[package]] name = "jsonschema" version = "4.26.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] dependencies = [ - { name = "attrs", marker = "python_full_version >= '3.10'" }, - { name = "jsonschema-specifications", marker = "python_full_version >= '3.10'" }, - { name = "referencing", version = "0.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } wheels = [ @@ -754,8 +627,7 @@ name = "jsonschema-specifications" version = "2025.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "referencing", version = "0.36.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "referencing", version = "0.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "referencing" }, ] sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } wheels = [ @@ -845,41 +717,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, - { url = "https://files.pythonhosted.org/packages/56/23/0d8c13a44bde9154821586520840643467aee574d8ce79a17da539ee7fed/markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26", size = 11623, upload-time = "2025-09-27T18:37:29.296Z" }, - { url = "https://files.pythonhosted.org/packages/fd/23/07a2cb9a8045d5f3f0890a8c3bc0859d7a47bfd9a560b563899bec7b72ed/markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc", size = 12049, upload-time = "2025-09-27T18:37:30.234Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e4/6be85eb81503f8e11b61c0b6369b6e077dcf0a74adbd9ebf6b349937b4e9/markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c", size = 21923, upload-time = "2025-09-27T18:37:31.177Z" }, - { url = "https://files.pythonhosted.org/packages/6f/bc/4dc914ead3fe6ddaef035341fee0fc956949bbd27335b611829292b89ee2/markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42", size = 20543, upload-time = "2025-09-27T18:37:32.168Z" }, - { url = "https://files.pythonhosted.org/packages/89/6e/5fe81fbcfba4aef4093d5f856e5c774ec2057946052d18d168219b7bd9f9/markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b", size = 20585, upload-time = "2025-09-27T18:37:33.166Z" }, - { url = "https://files.pythonhosted.org/packages/f6/f6/e0e5a3d3ae9c4020f696cd055f940ef86b64fe88de26f3a0308b9d3d048c/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758", size = 21387, upload-time = "2025-09-27T18:37:34.185Z" }, - { url = "https://files.pythonhosted.org/packages/c8/25/651753ef4dea08ea790f4fbb65146a9a44a014986996ca40102e237aa49a/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2", size = 20133, upload-time = "2025-09-27T18:37:35.138Z" }, - { url = "https://files.pythonhosted.org/packages/dc/0a/c3cf2b4fef5f0426e8a6d7fce3cb966a17817c568ce59d76b92a233fdbec/markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d", size = 20588, upload-time = "2025-09-27T18:37:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/cd/1b/a7782984844bd519ad4ffdbebbba2671ec5d0ebbeac34736c15fb86399e8/markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7", size = 14566, upload-time = "2025-09-27T18:37:37.09Z" }, - { url = "https://files.pythonhosted.org/packages/18/1f/8d9c20e1c9440e215a44be5ab64359e207fcb4f675543f1cf9a2a7f648d0/markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e", size = 15053, upload-time = "2025-09-27T18:37:38.054Z" }, - { url = "https://files.pythonhosted.org/packages/4e/d3/fe08482b5cd995033556d45041a4f4e76e7f0521112a9c9991d40d39825f/markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8", size = 13928, upload-time = "2025-09-27T18:37:39.037Z" }, +] + +[[package]] +name = "mnemonic" +version = "0.21" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/77/e6232ed59fbd7b90208bb8d4f89ed5aabcf30a524bc2fb8f0dafbe8e7df9/mnemonic-0.21.tar.gz", hash = "sha256:1fe496356820984f45559b1540c80ff10de448368929b9c60a2b55744cc88acf", size = 152462, upload-time = "2024-01-05T10:46:14.895Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/48/5abb16ce7f9d97b728e6b97c704ceaa614362e0847651f379ed0511942a0/mnemonic-0.21-py3-none-any.whl", hash = "sha256:72dc9de16ec5ef47287237b9b6943da11647a03fe7cf1f139fc3d7c4a7439288", size = 92701, upload-time = "2024-01-05T10:46:12.703Z" }, ] [[package]] name = "more-itertools" -version = "10.8.0" +version = "11.0.2" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/f7/139d22fef48ac78127d18e01d80cf1be40236ae489769d17f35c3d425293/more_itertools-11.0.2.tar.gz", hash = "sha256:392a9e1e362cbc106a2457d37cabf9b36e5e12efd4ebff1654630e76597df804", size = 144659, upload-time = "2026-04-09T15:01:33.297Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, -] - -[[package]] -name = "more-itertools" -version = "11.0.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/24/24/e0acc4bf54cba50c1d432c70a72a3df96db4a321b2c4c68432a60759044f/more_itertools-11.0.1.tar.gz", hash = "sha256:fefaf25b7ab08f0b45fa9f1892cae93b9fc0089ef034d39213bce15f1cc9e199", size = 144739, upload-time = "2026-04-02T16:17:45.061Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d8/f4/5e52c7319b8087acef603ed6e50dc325c02eaa999355414830468611f13c/more_itertools-11.0.1-py3-none-any.whl", hash = "sha256:eaf287826069452a8f61026c597eae2428b2d1ba2859083abbf240b46842ce6d", size = 72182, upload-time = "2026-04-02T16:17:43.724Z" }, + { url = "https://files.pythonhosted.org/packages/cb/98/6af411189d9413534c3eb691182bff1f5c6d44ed2f93f2edfe52a1bbceb8/more_itertools-11.0.2-py3-none-any.whl", hash = "sha256:6e35b35f818b01f691643c6c611bc0902f2e92b46c18fffa77ae1e7c46e912e4", size = 71939, upload-time = "2026-04-09T15:01:32.21Z" }, ] [[package]] @@ -904,11 +759,11 @@ wheels = [ [[package]] name = "packaging" -version = "26.0" +version = "26.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, + { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" }, ] [[package]] @@ -950,97 +805,71 @@ wheels = [ [[package]] name = "psycopg2-binary" -version = "2.9.11" +version = "2.9.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ac/6c/8767aaa597ba424643dc87348c6f1754dd9f48e80fdc1b9f7ca5c3a7c213/psycopg2-binary-2.9.11.tar.gz", hash = "sha256:b6aed9e096bf63f9e75edf2581aa9a7e7186d97ab5c177aa6c87797cd591236c", size = 379620, upload-time = "2025-10-10T11:14:48.041Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/60/a3624f79acea344c16fbef3a94d28b89a8042ddfb8f3e4ca83f538671409/psycopg2_binary-2.9.12.tar.gz", hash = "sha256:5ac9444edc768c02a6b6a591f070b8aae28ff3a99be57560ac996001580f294c", size = 379686, upload-time = "2026-04-21T09:40:34.304Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/f2/8e377d29c2ecf99f6062d35ea606b036e8800720eccfec5fe3dd672c2b24/psycopg2_binary-2.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d6fe6b47d0b42ce1c9f1fa3e35bb365011ca22e39db37074458f27921dca40f2", size = 3756506, upload-time = "2025-10-10T11:10:30.144Z" }, - { url = "https://files.pythonhosted.org/packages/24/cc/dc143ea88e4ec9d386106cac05023b69668bd0be20794c613446eaefafe5/psycopg2_binary-2.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a6c0e4262e089516603a09474ee13eabf09cb65c332277e39af68f6233911087", size = 3863943, upload-time = "2025-10-10T11:10:34.586Z" }, - { url = "https://files.pythonhosted.org/packages/8c/df/16848771155e7c419c60afeb24950b8aaa3ab09c0a091ec3ccca26a574d0/psycopg2_binary-2.9.11-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c47676e5b485393f069b4d7a811267d3168ce46f988fa602658b8bb901e9e64d", size = 4410873, upload-time = "2025-10-10T11:10:38.951Z" }, - { url = "https://files.pythonhosted.org/packages/43/79/5ef5f32621abd5a541b89b04231fe959a9b327c874a1d41156041c75494b/psycopg2_binary-2.9.11-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a28d8c01a7b27a1e3265b11250ba7557e5f72b5ee9e5f3a2fa8d2949c29bf5d2", size = 4468016, upload-time = "2025-10-10T11:10:43.319Z" }, - { url = "https://files.pythonhosted.org/packages/f0/9b/d7542d0f7ad78f57385971f426704776d7b310f5219ed58da5d605b1892e/psycopg2_binary-2.9.11-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f3f2732cf504a1aa9e9609d02f79bea1067d99edf844ab92c247bbca143303b", size = 4164996, upload-time = "2025-10-10T11:10:46.705Z" }, - { url = "https://files.pythonhosted.org/packages/14/ed/e409388b537fa7414330687936917c522f6a77a13474e4238219fcfd9a84/psycopg2_binary-2.9.11-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:865f9945ed1b3950d968ec4690ce68c55019d79e4497366d36e090327ce7db14", size = 3981881, upload-time = "2025-10-30T02:54:57.182Z" }, - { url = "https://files.pythonhosted.org/packages/bf/30/50e330e63bb05efc6fa7c1447df3e08954894025ca3dcb396ecc6739bc26/psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:91537a8df2bde69b1c1db01d6d944c831ca793952e4f57892600e96cee95f2cd", size = 3650857, upload-time = "2025-10-10T11:10:50.112Z" }, - { url = "https://files.pythonhosted.org/packages/f0/e0/4026e4c12bb49dd028756c5b0bc4c572319f2d8f1c9008e0dad8cc9addd7/psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4dca1f356a67ecb68c81a7bc7809f1569ad9e152ce7fd02c2f2036862ca9f66b", size = 3296063, upload-time = "2025-10-10T11:10:54.089Z" }, - { url = "https://files.pythonhosted.org/packages/2c/34/eb172be293c886fef5299fe5c3fcf180a05478be89856067881007934a7c/psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0da4de5c1ac69d94ed4364b6cbe7190c1a70d325f112ba783d83f8440285f152", size = 3043464, upload-time = "2025-10-30T02:55:02.483Z" }, - { url = "https://files.pythonhosted.org/packages/18/1c/532c5d2cb11986372f14b798a95f2eaafe5779334f6a80589a68b5fcf769/psycopg2_binary-2.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37d8412565a7267f7d79e29ab66876e55cb5e8e7b3bbf94f8206f6795f8f7e7e", size = 3345378, upload-time = "2025-10-10T11:11:01.039Z" }, - { url = "https://files.pythonhosted.org/packages/70/e7/de420e1cf16f838e1fa17b1120e83afff374c7c0130d088dba6286fcf8ea/psycopg2_binary-2.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:c665f01ec8ab273a61c62beeb8cce3014c214429ced8a308ca1fc410ecac3a39", size = 2713904, upload-time = "2025-10-10T11:11:04.81Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ae/8d8266f6dd183ab4d48b95b9674034e1b482a3f8619b33a0d86438694577/psycopg2_binary-2.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0e8480afd62362d0a6a27dd09e4ca2def6fa50ed3a4e7c09165266106b2ffa10", size = 3756452, upload-time = "2025-10-10T11:11:11.583Z" }, - { url = "https://files.pythonhosted.org/packages/4b/34/aa03d327739c1be70e09d01182619aca8ebab5970cd0cfa50dd8b9cec2ac/psycopg2_binary-2.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:763c93ef1df3da6d1a90f86ea7f3f806dc06b21c198fa87c3c25504abec9404a", size = 3863957, upload-time = "2025-10-10T11:11:16.932Z" }, - { url = "https://files.pythonhosted.org/packages/48/89/3fdb5902bdab8868bbedc1c6e6023a4e08112ceac5db97fc2012060e0c9a/psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e164359396576a3cc701ba8af4751ae68a07235d7a380c631184a611220d9a4", size = 4410955, upload-time = "2025-10-10T11:11:21.21Z" }, - { url = "https://files.pythonhosted.org/packages/ce/24/e18339c407a13c72b336e0d9013fbbbde77b6fd13e853979019a1269519c/psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:d57c9c387660b8893093459738b6abddbb30a7eab058b77b0d0d1c7d521ddfd7", size = 4468007, upload-time = "2025-10-10T11:11:24.831Z" }, - { url = "https://files.pythonhosted.org/packages/91/7e/b8441e831a0f16c159b5381698f9f7f7ed54b77d57bc9c5f99144cc78232/psycopg2_binary-2.9.11-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2c226ef95eb2250974bf6fa7a842082b31f68385c4f3268370e3f3870e7859ee", size = 4165012, upload-time = "2025-10-10T11:11:29.51Z" }, - { url = "https://files.pythonhosted.org/packages/0d/61/4aa89eeb6d751f05178a13da95516c036e27468c5d4d2509bb1e15341c81/psycopg2_binary-2.9.11-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a311f1edc9967723d3511ea7d2708e2c3592e3405677bf53d5c7246753591fbb", size = 3981881, upload-time = "2025-10-30T02:55:07.332Z" }, - { url = "https://files.pythonhosted.org/packages/76/a1/2f5841cae4c635a9459fe7aca8ed771336e9383b6429e05c01267b0774cf/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ebb415404821b6d1c47353ebe9c8645967a5235e6d88f914147e7fd411419e6f", size = 3650985, upload-time = "2025-10-10T11:11:34.975Z" }, - { url = "https://files.pythonhosted.org/packages/84/74/4defcac9d002bca5709951b975173c8c2fa968e1a95dc713f61b3a8d3b6a/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f07c9c4a5093258a03b28fab9b4f151aa376989e7f35f855088234e656ee6a94", size = 3296039, upload-time = "2025-10-10T11:11:40.432Z" }, - { url = "https://files.pythonhosted.org/packages/6d/c2/782a3c64403d8ce35b5c50e1b684412cf94f171dc18111be8c976abd2de1/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:00ce1830d971f43b667abe4a56e42c1e2d594b32da4802e44a73bacacb25535f", size = 3043477, upload-time = "2025-10-30T02:55:11.182Z" }, - { url = "https://files.pythonhosted.org/packages/c8/31/36a1d8e702aa35c38fc117c2b8be3f182613faa25d794b8aeaab948d4c03/psycopg2_binary-2.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cffe9d7697ae7456649617e8bb8d7a45afb71cd13f7ab22af3e5c61f04840908", size = 3345842, upload-time = "2025-10-10T11:11:45.366Z" }, - { url = "https://files.pythonhosted.org/packages/6e/b4/a5375cda5b54cb95ee9b836930fea30ae5a8f14aa97da7821722323d979b/psycopg2_binary-2.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:304fd7b7f97eef30e91b8f7e720b3db75fee010b520e434ea35ed1ff22501d03", size = 2713894, upload-time = "2025-10-10T11:11:48.775Z" }, - { url = "https://files.pythonhosted.org/packages/d8/91/f870a02f51be4a65987b45a7de4c2e1897dd0d01051e2b559a38fa634e3e/psycopg2_binary-2.9.11-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be9b840ac0525a283a96b556616f5b4820e0526addb8dcf6525a0fa162730be4", size = 3756603, upload-time = "2025-10-10T11:11:52.213Z" }, - { url = "https://files.pythonhosted.org/packages/27/fa/cae40e06849b6c9a95eb5c04d419942f00d9eaac8d81626107461e268821/psycopg2_binary-2.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f090b7ddd13ca842ebfe301cd587a76a4cf0913b1e429eb92c1be5dbeb1a19bc", size = 3864509, upload-time = "2025-10-10T11:11:56.452Z" }, - { url = "https://files.pythonhosted.org/packages/2d/75/364847b879eb630b3ac8293798e380e441a957c53657995053c5ec39a316/psycopg2_binary-2.9.11-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ab8905b5dcb05bf3fb22e0cf90e10f469563486ffb6a96569e51f897c750a76a", size = 4411159, upload-time = "2025-10-10T11:12:00.49Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a0/567f7ea38b6e1c62aafd58375665a547c00c608a471620c0edc364733e13/psycopg2_binary-2.9.11-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bf940cd7e7fec19181fdbc29d76911741153d51cab52e5c21165f3262125685e", size = 4468234, upload-time = "2025-10-10T11:12:04.892Z" }, - { url = "https://files.pythonhosted.org/packages/30/da/4e42788fb811bbbfd7b7f045570c062f49e350e1d1f3df056c3fb5763353/psycopg2_binary-2.9.11-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fa0f693d3c68ae925966f0b14b8edda71696608039f4ed61b1fe9ffa468d16db", size = 4166236, upload-time = "2025-10-10T11:12:11.674Z" }, - { url = "https://files.pythonhosted.org/packages/3c/94/c1777c355bc560992af848d98216148be5f1be001af06e06fc49cbded578/psycopg2_binary-2.9.11-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a1cf393f1cdaf6a9b57c0a719a1068ba1069f022a59b8b1fe44b006745b59757", size = 3983083, upload-time = "2025-10-30T02:55:15.73Z" }, - { url = "https://files.pythonhosted.org/packages/bd/42/c9a21edf0e3daa7825ed04a4a8588686c6c14904344344a039556d78aa58/psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef7a6beb4beaa62f88592ccc65df20328029d721db309cb3250b0aae0fa146c3", size = 3652281, upload-time = "2025-10-10T11:12:17.713Z" }, - { url = "https://files.pythonhosted.org/packages/12/22/dedfbcfa97917982301496b6b5e5e6c5531d1f35dd2b488b08d1ebc52482/psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:31b32c457a6025e74d233957cc9736742ac5a6cb196c6b68499f6bb51390bd6a", size = 3298010, upload-time = "2025-10-10T11:12:22.671Z" }, - { url = "https://files.pythonhosted.org/packages/66/ea/d3390e6696276078bd01b2ece417deac954dfdd552d2edc3d03204416c0c/psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:edcb3aeb11cb4bf13a2af3c53a15b3d612edeb6409047ea0b5d6a21a9d744b34", size = 3044641, upload-time = "2025-10-30T02:55:19.929Z" }, - { url = "https://files.pythonhosted.org/packages/12/9a/0402ded6cbd321da0c0ba7d34dc12b29b14f5764c2fc10750daa38e825fc/psycopg2_binary-2.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b6d93d7c0b61a1dd6197d208ab613eb7dcfdcca0a49c42ceb082257991de9d", size = 3347940, upload-time = "2025-10-10T11:12:26.529Z" }, - { url = "https://files.pythonhosted.org/packages/b1/d2/99b55e85832ccde77b211738ff3925a5d73ad183c0b37bcbbe5a8ff04978/psycopg2_binary-2.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:b33fabeb1fde21180479b2d4667e994de7bbf0eec22832ba5d9b5e4cf65b6c6d", size = 2714147, upload-time = "2025-10-10T11:12:29.535Z" }, - { url = "https://files.pythonhosted.org/packages/ff/a8/a2709681b3ac11b0b1786def10006b8995125ba268c9a54bea6f5ae8bd3e/psycopg2_binary-2.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b8fb3db325435d34235b044b199e56cdf9ff41223a4b9752e8576465170bb38c", size = 3756572, upload-time = "2025-10-10T11:12:32.873Z" }, - { url = "https://files.pythonhosted.org/packages/62/e1/c2b38d256d0dafd32713e9f31982a5b028f4a3651f446be70785f484f472/psycopg2_binary-2.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:366df99e710a2acd90efed3764bb1e28df6c675d33a7fb40df9b7281694432ee", size = 3864529, upload-time = "2025-10-10T11:12:36.791Z" }, - { url = "https://files.pythonhosted.org/packages/11/32/b2ffe8f3853c181e88f0a157c5fb4e383102238d73c52ac6d93a5c8bffe6/psycopg2_binary-2.9.11-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c55b385daa2f92cb64b12ec4536c66954ac53654c7f15a203578da4e78105c0", size = 4411242, upload-time = "2025-10-10T11:12:42.388Z" }, - { url = "https://files.pythonhosted.org/packages/10/04/6ca7477e6160ae258dc96f67c371157776564679aefd247b66f4661501a2/psycopg2_binary-2.9.11-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c0377174bf1dd416993d16edc15357f6eb17ac998244cca19bc67cdc0e2e5766", size = 4468258, upload-time = "2025-10-10T11:12:48.654Z" }, - { url = "https://files.pythonhosted.org/packages/3c/7e/6a1a38f86412df101435809f225d57c1a021307dd0689f7a5e7fe83588b1/psycopg2_binary-2.9.11-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5c6ff3335ce08c75afaed19e08699e8aacf95d4a260b495a4a8545244fe2ceb3", size = 4166295, upload-time = "2025-10-10T11:12:52.525Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7d/c07374c501b45f3579a9eb761cbf2604ddef3d96ad48679112c2c5aa9c25/psycopg2_binary-2.9.11-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84011ba3109e06ac412f95399b704d3d6950e386b7994475b231cf61eec2fc1f", size = 3983133, upload-time = "2025-10-30T02:55:24.329Z" }, - { url = "https://files.pythonhosted.org/packages/82/56/993b7104cb8345ad7d4516538ccf8f0d0ac640b1ebd8c754a7b024e76878/psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ba34475ceb08cccbdd98f6b46916917ae6eeb92b5ae111df10b544c3a4621dc4", size = 3652383, upload-time = "2025-10-10T11:12:56.387Z" }, - { url = "https://files.pythonhosted.org/packages/2d/ac/eaeb6029362fd8d454a27374d84c6866c82c33bfc24587b4face5a8e43ef/psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b31e90fdd0f968c2de3b26ab014314fe814225b6c324f770952f7d38abf17e3c", size = 3298168, upload-time = "2025-10-10T11:13:00.403Z" }, - { url = "https://files.pythonhosted.org/packages/2b/39/50c3facc66bded9ada5cbc0de867499a703dc6bca6be03070b4e3b65da6c/psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:d526864e0f67f74937a8fce859bd56c979f5e2ec57ca7c627f5f1071ef7fee60", size = 3044712, upload-time = "2025-10-30T02:55:27.975Z" }, - { url = "https://files.pythonhosted.org/packages/9c/8e/b7de019a1f562f72ada81081a12823d3c1590bedc48d7d2559410a2763fe/psycopg2_binary-2.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04195548662fa544626c8ea0f06561eb6203f1984ba5b4562764fbeb4c3d14b1", size = 3347549, upload-time = "2025-10-10T11:13:03.971Z" }, - { url = "https://files.pythonhosted.org/packages/80/2d/1bb683f64737bbb1f86c82b7359db1eb2be4e2c0c13b947f80efefa7d3e5/psycopg2_binary-2.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:efff12b432179443f54e230fdf60de1f6cc726b6c832db8701227d089310e8aa", size = 2714215, upload-time = "2025-10-10T11:13:07.14Z" }, - { url = "https://files.pythonhosted.org/packages/64/12/93ef0098590cf51d9732b4f139533732565704f45bdc1ffa741b7c95fb54/psycopg2_binary-2.9.11-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:92e3b669236327083a2e33ccfa0d320dd01b9803b3e14dd986a4fc54aa00f4e1", size = 3756567, upload-time = "2025-10-10T11:13:11.885Z" }, - { url = "https://files.pythonhosted.org/packages/7c/a9/9d55c614a891288f15ca4b5209b09f0f01e3124056924e17b81b9fa054cc/psycopg2_binary-2.9.11-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e0deeb03da539fa3577fcb0b3f2554a97f7e5477c246098dbb18091a4a01c16f", size = 3864755, upload-time = "2025-10-10T11:13:17.727Z" }, - { url = "https://files.pythonhosted.org/packages/13/1e/98874ce72fd29cbde93209977b196a2edae03f8490d1bd8158e7f1daf3a0/psycopg2_binary-2.9.11-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b52a3f9bb540a3e4ec0f6ba6d31339727b2950c9772850d6545b7eae0b9d7c5", size = 4411646, upload-time = "2025-10-10T11:13:24.432Z" }, - { url = "https://files.pythonhosted.org/packages/5a/bd/a335ce6645334fb8d758cc358810defca14a1d19ffbc8a10bd38a2328565/psycopg2_binary-2.9.11-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:db4fd476874ccfdbb630a54426964959e58da4c61c9feba73e6094d51303d7d8", size = 4468701, upload-time = "2025-10-10T11:13:29.266Z" }, - { url = "https://files.pythonhosted.org/packages/44/d6/c8b4f53f34e295e45709b7568bf9b9407a612ea30387d35eb9fa84f269b4/psycopg2_binary-2.9.11-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47f212c1d3be608a12937cc131bd85502954398aaa1320cb4c14421a0ffccf4c", size = 4166293, upload-time = "2025-10-10T11:13:33.336Z" }, - { url = "https://files.pythonhosted.org/packages/4b/e0/f8cc36eadd1b716ab36bb290618a3292e009867e5c97ce4aba908cb99644/psycopg2_binary-2.9.11-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e35b7abae2b0adab776add56111df1735ccc71406e56203515e228a8dc07089f", size = 3983184, upload-time = "2025-10-30T02:55:32.483Z" }, - { url = "https://files.pythonhosted.org/packages/53/3e/2a8fe18a4e61cfb3417da67b6318e12691772c0696d79434184a511906dc/psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fcf21be3ce5f5659daefd2b3b3b6e4727b028221ddc94e6c1523425579664747", size = 3652650, upload-time = "2025-10-10T11:13:38.181Z" }, - { url = "https://files.pythonhosted.org/packages/76/36/03801461b31b29fe58d228c24388f999fe814dfc302856e0d17f97d7c54d/psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:9bd81e64e8de111237737b29d68039b9c813bdf520156af36d26819c9a979e5f", size = 3298663, upload-time = "2025-10-10T11:13:44.878Z" }, - { url = "https://files.pythonhosted.org/packages/97/77/21b0ea2e1a73aa5fa9222b2a6b8ba325c43c3a8d54272839c991f2345656/psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:32770a4d666fbdafab017086655bcddab791d7cb260a16679cc5a7338b64343b", size = 3044737, upload-time = "2025-10-30T02:55:35.69Z" }, - { url = "https://files.pythonhosted.org/packages/67/69/f36abe5f118c1dca6d3726ceae164b9356985805480731ac6712a63f24f0/psycopg2_binary-2.9.11-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c3cb3a676873d7506825221045bd70e0427c905b9c8ee8d6acd70cfcbd6e576d", size = 3347643, upload-time = "2025-10-10T11:13:53.499Z" }, - { url = "https://files.pythonhosted.org/packages/e1/36/9c0c326fe3a4227953dfb29f5d0c8ae3b8eb8c1cd2967aa569f50cb3c61f/psycopg2_binary-2.9.11-cp314-cp314-win_amd64.whl", hash = "sha256:4012c9c954dfaccd28f94e84ab9f94e12df76b4afb22331b1f0d3154893a6316", size = 2803913, upload-time = "2025-10-10T11:13:57.058Z" }, - { url = "https://files.pythonhosted.org/packages/b2/41/cb36a61146b3afed03e980477f6dd29c0263f15e4b4844660501a774dc0b/psycopg2_binary-2.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:20e7fb94e20b03dcc783f76c0865f9da39559dcc0c28dd1a3fce0d01902a6b9c", size = 3756418, upload-time = "2025-10-10T11:14:00.728Z" }, - { url = "https://files.pythonhosted.org/packages/f4/e3/8623be505c8921727277f22753092835b559543b078e83378bb74712dbc8/psycopg2_binary-2.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4bdab48575b6f870f465b397c38f1b415520e9879fdf10a53ee4f49dcbdf8a21", size = 3863981, upload-time = "2025-10-10T11:14:05.215Z" }, - { url = "https://files.pythonhosted.org/packages/bc/86/ec3682dc3550c65eff80384f603a6a55b798e1b86ccef262d454d19f96eb/psycopg2_binary-2.9.11-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9d3a9edcfbe77a3ed4bc72836d466dfce4174beb79eda79ea155cc77237ed9e8", size = 4410882, upload-time = "2025-10-10T11:14:09.552Z" }, - { url = "https://files.pythonhosted.org/packages/41/af/540ee7d56fb33408c57240d55904c95e4a30952c096f5e1542769cadc787/psycopg2_binary-2.9.11-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:44fc5c2b8fa871ce7f0023f619f1349a0aa03a0857f2c96fbc01c657dcbbdb49", size = 4468062, upload-time = "2025-10-10T11:14:15.225Z" }, - { url = "https://files.pythonhosted.org/packages/b4/d5/b95d47b2e67b2adfaba517c803a99a1ac41e84c8201d0f3b29d77b56e357/psycopg2_binary-2.9.11-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9c55460033867b4622cda1b6872edf445809535144152e5d14941ef591980edf", size = 4165036, upload-time = "2025-10-10T11:14:21.209Z" }, - { url = "https://files.pythonhosted.org/packages/af/f9/99e39882b70d9b0cfdcbad33bea2e5823843c3a7839c1aaf89fc1337c05c/psycopg2_binary-2.9.11-cp39-cp39-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2d11098a83cca92deaeaed3d58cfd150d49b3b06ee0d0852be466bf87596899e", size = 3981901, upload-time = "2025-10-30T02:55:39.325Z" }, - { url = "https://files.pythonhosted.org/packages/de/c3/8d2c97f1dfddedf5a06c6ad2eda83fba48555a7bc525c3150aedc6f2bedc/psycopg2_binary-2.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:691c807d94aecfbc76a14e1408847d59ff5b5906a04a23e12a89007672b9e819", size = 3650995, upload-time = "2025-10-10T11:14:27.733Z" }, - { url = "https://files.pythonhosted.org/packages/9c/89/afdf59b44b84ebb28111652485fab608429389f4051d22bc5a7bb43d5208/psycopg2_binary-2.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b81627b691f29c4c30a8f322546ad039c40c328373b11dff7490a3e1b517855", size = 3296106, upload-time = "2025-10-10T11:14:33.312Z" }, - { url = "https://files.pythonhosted.org/packages/94/21/851c9ecf0e9a699907d1c455dbbde7ef9b11dba28e7b7b132c7bb28391f2/psycopg2_binary-2.9.11-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:b637d6d941209e8d96a072d7977238eea128046effbf37d1d8b2c0764750017d", size = 3043491, upload-time = "2025-10-30T02:55:42.228Z" }, - { url = "https://files.pythonhosted.org/packages/9c/de/50f6eced439e7a131b268276c4b68cf8800fd55d8cef7b37109c44bf957a/psycopg2_binary-2.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:41360b01c140c2a03d346cec3280cf8a71aa07d94f3b1509fa0161c366af66b4", size = 3345816, upload-time = "2025-10-10T11:14:38.648Z" }, - { url = "https://files.pythonhosted.org/packages/45/3b/e0506f199dc8a90ff3b462f261f45d15c0703bb8c59f0da1add5f0c11a30/psycopg2_binary-2.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:875039274f8a2361e5207857899706da840768e2a775bf8c65e82f60b197df02", size = 2714968, upload-time = "2025-10-10T11:14:43.24Z" }, -] - -[[package]] -name = "pycparser" -version = "2.23" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, + { url = "https://files.pythonhosted.org/packages/78/80/49bacf9e51617d8309f6f0123e29edc793f6f5f6700c7d1f1b20782fbb37/psycopg2_binary-2.9.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9b818ceff717f98851a64bffd4c5eb5b3059ae280276dcecc52ac658dcf006a4", size = 3712314, upload-time = "2026-04-20T23:33:31.363Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f2/98eeac7d60c43df9338287834edf9b3e69be68a2db78a57b1b81d705e735/psycopg2_binary-2.9.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2fa0d7caca8635c56e373055094eeda3208d901d55dd0ff5abc1d4e47f82b56", size = 3822389, upload-time = "2026-04-20T23:33:34.178Z" }, + { url = "https://files.pythonhosted.org/packages/9f/7c/30575e75f14d5351a56a1971bb43fe7f8bf7edf1b654fb1bec65c42a8812/psycopg2_binary-2.9.12-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:864c261b3690e1207d14bbfe0a61e27567981b80c47a778561e49f676f7ce433", size = 4578448, upload-time = "2026-04-20T23:33:37.073Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/4df366d89f28c527dc39d0b6c98a5ca74e30d37ac097b73f3352147568ae/psycopg2_binary-2.9.12-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c5ee5213445dd45312459029b8c4c0a695461eb517b753d2582315bd07995f5e", size = 4273705, upload-time = "2026-04-20T23:33:39.291Z" }, + { url = "https://files.pythonhosted.org/packages/4e/8a/c566803818eb03161ba869b6ba612bf7ad56816d98b9e5121e0a22ad6b0b/psycopg2_binary-2.9.12-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6f9cae1f848779b5b01f417e762c40d026ea93eb0648249a604728cda991dde3", size = 5893784, upload-time = "2026-04-20T23:33:41.658Z" }, + { url = "https://files.pythonhosted.org/packages/63/fe/0dfa5797e0b229e0567bc378695224caf14d547f73b05be0c80549089772/psycopg2_binary-2.9.12-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:63a3ebbd543d3d1eda088ac99164e8c5bac15293ee91f20281fd17d050aee1c4", size = 4109306, upload-time = "2026-04-20T23:33:43.953Z" }, + { url = "https://files.pythonhosted.org/packages/3c/89/28063adf17a4ba501eedd9890feab0c649ee4d8bd0a97df0ff1e9584feab/psycopg2_binary-2.9.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d6fcbba8c9fed08a73b8ac61ea79e4821e45b1e92bb466230c5e746bbf3d5256", size = 3654400, upload-time = "2026-04-20T23:33:46.115Z" }, + { url = "https://files.pythonhosted.org/packages/84/94/5a01de0aa4ead0b8d8d1aa4ec18cec0bd36d03fa714eaa5bb8a0b1b50020/psycopg2_binary-2.9.12-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:36512911ebb2b60a0c3e44d0bb5048c1980aced91235d133b7874f3d1d93487c", size = 3299215, upload-time = "2026-04-20T23:33:48.202Z" }, + { url = "https://files.pythonhosted.org/packages/7a/85/723bb085a61c6ac2dc0a0043f375f2fe7365363e27b073bad56ca5bda979/psycopg2_binary-2.9.12-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:8ffdb59fe88f99589e34354a130217aa1fd2d615612402d6edc8b3dbc7a44463", size = 3047724, upload-time = "2026-04-20T23:33:50.74Z" }, + { url = "https://files.pythonhosted.org/packages/b4/67/4d8b1e0d2fc4166677380eac0edf9cdff91013aca2546e8ef7bc04b56158/psycopg2_binary-2.9.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a46fe069b65255df410f856d842bc235f90e22ffdf532dda625fd4213d3fd9b1", size = 3349183, upload-time = "2026-04-20T23:33:59.635Z" }, + { url = "https://files.pythonhosted.org/packages/73/99/21af7a5498637ea4dc91a17c281a53bc1d632fbafe00f6689fbfb32a9fed/psycopg2_binary-2.9.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab29414b25dcb698bf26bf213e3348abdcd07bbd5de032a5bec15bd75b298b03", size = 2757036, upload-time = "2026-04-20T23:34:01.606Z" }, + { url = "https://files.pythonhosted.org/packages/d5/19/d4ce60954f3bb9d8e3bc5e5c4d1f2487de2d3851bf2391d54954c9df12a6/psycopg2_binary-2.9.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5c8ce6c61bd1b1f6b9c24ee32211599f6166af2c55abb19456090a21fd16554b", size = 3712338, upload-time = "2026-04-20T23:34:03.961Z" }, + { url = "https://files.pythonhosted.org/packages/53/71/c85409ee0d78890f0660eff262e815e7dd2bb741a17611d82e9e8cd9dc5e/psycopg2_binary-2.9.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b4a9eaa6e7f4ff91bec10aa3fb296878e75187bced5cc4bafe17dc40915e1326", size = 3822407, upload-time = "2026-04-20T23:34:05.977Z" }, + { url = "https://files.pythonhosted.org/packages/3c/ed/60486c2c7f0d4d1ede2bfb1ed27e2498477ce646bc7f6b2759906303117e/psycopg2_binary-2.9.12-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c6528cefc8e50fcc6f4a107e27a672058b36cc5736d665476aeb413ba88dbb06", size = 4578425, upload-time = "2026-04-20T23:34:08.246Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b9/656cb03fad9f4f49f2145c334b1126ee75189929ca4e6187d485a2d59951/psycopg2_binary-2.9.12-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e4e184b1fb6072bf05388aa41c697e1b2d01b3473f107e7ec44f186a32cfd0b8", size = 4273709, upload-time = "2026-04-20T23:34:10.974Z" }, + { url = "https://files.pythonhosted.org/packages/99/66/08cf0da0e25cc6fb142c89be45fc8418792858f0c4cbff5e24530ff02cd6/psycopg2_binary-2.9.12-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4766ab678563054d3f1d064a4db19cc4b5f9e3a8d9018592a8285cf200c248f3", size = 5893779, upload-time = "2026-04-20T23:34:13.905Z" }, + { url = "https://files.pythonhosted.org/packages/17/d7/eecd9ce8e146d3721115d82d3836efdbb712187e4590325df549989d18f4/psycopg2_binary-2.9.12-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5a0253224780c978746cb9be55a946bcdaf40fe3519c0f622924cdabdafe2c39", size = 4109308, upload-time = "2026-04-20T23:34:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2e/b1dc289b362cc8d45697b57eefbd673186f49a4ea0906928988e3affcc98/psycopg2_binary-2.9.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0dc9228d47c46bda253d2ecd6bb93b56a9f2d7ad33b684a1fa3622bf74ffe30c", size = 3654405, upload-time = "2026-04-20T23:34:19.303Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e4/4c4aea6473214dbdbd0fbba11aa4691e76dc01722c55724c5951719865ff/psycopg2_binary-2.9.12-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f921f3cd87035ef7df233383011d7a53ea1d346224752c1385f1edfd790ceb6a", size = 3299187, upload-time = "2026-04-20T23:34:21.206Z" }, + { url = "https://files.pythonhosted.org/packages/ba/5d/b03b99986446a4f57b170ed9a2579fb7ff9783ca0fa5226b19db99737fee/psycopg2_binary-2.9.12-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3d999bd982a723113c1a45b55a7a6a90d64d0ed2278020ed625c490ff7bef96c", size = 3047716, upload-time = "2026-04-20T23:34:23.077Z" }, + { url = "https://files.pythonhosted.org/packages/14/86/382ee4afbd1d97500c9d2862b20c2fdeddf4b7335e984df3fb4309f64108/psycopg2_binary-2.9.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29d4d134bd0ab46ffb04e94aa3c5fa3ef582e9026609165e2f758ff76fc3a3be", size = 3349237, upload-time = "2026-04-20T23:34:25.211Z" }, + { url = "https://files.pythonhosted.org/packages/a8/16/9a57c75ba1eda7165c017342f526810d5f5a12647dde749c99ae9a7141d7/psycopg2_binary-2.9.12-cp311-cp311-win_amd64.whl", hash = "sha256:cb4a1dacdd48077150dc762a9e5ddbf32c256d66cb46f80839391aa458774936", size = 2757036, upload-time = "2026-04-20T23:34:27.77Z" }, + { url = "https://files.pythonhosted.org/packages/e2/9f/ef4ef3c8e15083df90ca35265cfd1a081a2f0cc07bb229c6314c6af817f4/psycopg2_binary-2.9.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5cdc05117180c5fa9c40eea8ea559ce64d73824c39d928b7da9fb5f6a9392433", size = 3712459, upload-time = "2026-04-20T23:34:30.549Z" }, + { url = "https://files.pythonhosted.org/packages/b5/01/3dd14e46ba48c1e1a6ec58ee599fa1b5efa00c246d5046cd903d0eeb1af1/psycopg2_binary-2.9.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d3227a3bc228c10d21011a99245edca923e4e8bf461857e869a507d9a41fe9f6", size = 3822936, upload-time = "2026-04-20T23:34:32.77Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f7/0640e4901119d8a9f7a1784b927f494e2198e213ceb593753d1f2c8b1b30/psycopg2_binary-2.9.12-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:995ce929eede89db6254b50827e2b7fd61e50d11f0b116b29fffe4a2e53c4580", size = 4578676, upload-time = "2026-04-20T23:34:35.18Z" }, + { url = "https://files.pythonhosted.org/packages/b0/55/44df3965b5f297c50cc0b1b594a31c67d6127a9d133045b8a66611b14dfb/psycopg2_binary-2.9.12-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9fe06d93e72f1c048e731a2e3e7854a5bfaa58fc736068df90b352cefe66f03f", size = 4274917, upload-time = "2026-04-20T23:34:37.982Z" }, + { url = "https://files.pythonhosted.org/packages/b0/4b/74535248b1eac0c9336862e8617c765ac94dac76f9e25d7c4a79588c8907/psycopg2_binary-2.9.12-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40e7b28b63aaf737cb3a1edc3a9bbc9a9f4ad3dcb7152e8c1130e4050eddcb7d", size = 5894843, upload-time = "2026-04-20T23:34:40.856Z" }, + { url = "https://files.pythonhosted.org/packages/f2/ba/f1bf8d2ae71868ad800b661099086ee52bc0f8d9f05be1acd8ebb06757cc/psycopg2_binary-2.9.12-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:89d19a9f7899e8eb0656a2b3a08e0da04c720a06db6e0033eab5928aabe60fa9", size = 4110556, upload-time = "2026-04-20T23:34:44.016Z" }, + { url = "https://files.pythonhosted.org/packages/45/46/c15706c338403b7c420bcc0c2905aad116cc064545686d8bf85f1999ea00/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:612b965daee295ae2da8f8218ce1d274645dc76ef3f1abf6a0a94fd57eff876d", size = 3655714, upload-time = "2026-04-20T23:34:46.233Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7c/a2d5dc09b64a4564db242a0fe418fde7d33f6f8259dd2c5b9d7def00fb5a/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b9a339b79d37c1b45f3235265f07cdeb0cb5ad7acd2ac7720a5920989c17c24e", size = 3301154, upload-time = "2026-04-20T23:34:49.528Z" }, + { url = "https://files.pythonhosted.org/packages/c0/e8/cc8c9a4ce71461f9ec548d38cadc41dc184b34c73e6455450775a9334ccd/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:3471336e1acfd9c7fe507b8bad5af9317b6a89294f9eb37bd9a030bb7bebcdc6", size = 3048882, upload-time = "2026-04-20T23:34:51.86Z" }, + { url = "https://files.pythonhosted.org/packages/19/6a/31e2296bc0787c5ab75d3d118e40b239db8151b5192b90b77c72bc9256e9/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7af18183109e23502c8b2ae7f6926c0882766f35b5175a4cd737ad825e4d7a1b", size = 3351298, upload-time = "2026-04-20T23:34:54.124Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a8/75f4e3e11203b590150abed2cf7794b9c9c9f7eceddae955191138b44dde/psycopg2_binary-2.9.12-cp312-cp312-win_amd64.whl", hash = "sha256:398fcd4db988c7d7d3713e2b8e18939776fd3fb447052daae4f24fa39daede4c", size = 2757230, upload-time = "2026-04-20T23:34:56.242Z" }, + { url = "https://files.pythonhosted.org/packages/91/bb/4608c96f970f6e0c56572e87027ef4404f709382a3503e9934526d7ba051/psycopg2_binary-2.9.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7c729a73c7b1b84de3582f73cdd27d905121dc2c531f3d9a3c32a3011033b965", size = 3712419, upload-time = "2026-04-20T23:34:58.754Z" }, + { url = "https://files.pythonhosted.org/packages/5e/af/48f76af9d50d61cf390f8cd657b503168b089e2e9298e48465d029fcc713/psycopg2_binary-2.9.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4413d0caef93c5cf50b96863df4c2efe8c269bf2267df353225595e7e15e8df7", size = 3822990, upload-time = "2026-04-20T23:35:00.821Z" }, + { url = "https://files.pythonhosted.org/packages/7a/df/aba0f99397cd811d32e06fc0cc781f1f3ce98bc0e729cb423925085d781a/psycopg2_binary-2.9.12-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4dfcf8e45ebb0c663be34a3442f65e17311f3367089cd4e5e3a3e8e62c978777", size = 4578696, upload-time = "2026-04-20T23:35:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/95/9c/eaa74021ac4e4d5c2f83d82fc6615a63f4fe6c94dc4e94c3990427053f67/psycopg2_binary-2.9.12-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c41321a14dd74aceb6a9a643b9253a334521babfa763fa873e33d89cfa122fb5", size = 4274982, upload-time = "2026-04-20T23:35:05.583Z" }, + { url = "https://files.pythonhosted.org/packages/35/ed/c25deff98bd26187ba48b3b250a3ffc3037c46c5b89362534a15d200e0db/psycopg2_binary-2.9.12-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83946ba43979ebfdc99a3cd0ee775c89f221df026984ba19d46133d8d75d3cd9", size = 5894867, upload-time = "2026-04-20T23:35:07.902Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/8d0e21ca77373c6c9589e5c4528f6e8f0c08c62cafc76fb0bddb7a2cee22/psycopg2_binary-2.9.12-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:411e85815652d13560fbe731878daa5d92378c4995a22302071890ec3397d019", size = 4110578, upload-time = "2026-04-20T23:35:10.149Z" }, + { url = "https://files.pythonhosted.org/packages/00/fc/f481e2435bd8f742d0123309174aae4165160ad3ef17c1b99c3622c241d2/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c8ad4c08e00f7679559eaed7aff1edfffc60c086b976f93972f686384a95e2c", size = 3655816, upload-time = "2026-04-20T23:35:12.56Z" }, + { url = "https://files.pythonhosted.org/packages/53/79/b9f46466bdbe9f239c96cde8be33c1aace4842f06013b47b730dc9759187/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:00814e40fa23c2b37ef0a1e3c749d89982c73a9cb5046137f0752a22d432e82f", size = 3301307, upload-time = "2026-04-20T23:35:15.029Z" }, + { url = "https://files.pythonhosted.org/packages/3f/19/7dc003b32fe35024df89b658104f7c8538a8b2dcbde7a4e746ce929742e7/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:98062447aebc20ed20add1f547a364fd0ef8933640d5372ff1873f8deb9b61be", size = 3048968, upload-time = "2026-04-20T23:35:16.757Z" }, + { url = "https://files.pythonhosted.org/packages/91/58/2dbd7db5c604d45f4950d988506aae672a14126ec22998ced5021cbb76bb/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:66a7685d7e548f10fb4ce32fb01a7b7f4aa702134de92a292c7bd9e0d3dbd290", size = 3351369, upload-time = "2026-04-20T23:35:18.933Z" }, + { url = "https://files.pythonhosted.org/packages/42/ee/dee8dcaad07f735824de3d6563bc67119fa6c28257b17977a8d624f02fab/psycopg2_binary-2.9.12-cp313-cp313-win_amd64.whl", hash = "sha256:b6937f5fe4e180aeee87de907a2fa982ded6f7f15d7218f78a083e4e1d68f2a0", size = 2757347, upload-time = "2026-04-20T23:35:21.283Z" }, + { url = "https://files.pythonhosted.org/packages/13/1b/708c0dca874acfad6d65314271859899a79007686f3a1f74e82a2ed4b645/psycopg2_binary-2.9.12-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6f3b3de8a74ef8db215f22edffb19e32dc6fa41340456de7ec99efdc8a7b3ec2", size = 3712428, upload-time = "2026-04-20T23:35:23.453Z" }, + { url = "https://files.pythonhosted.org/packages/d6/39/ddbea9d4b4de6aca9431b6ed253f530f8a02d3b8f9bcfd0dbfe2b3de6fe4/psycopg2_binary-2.9.12-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1006fb62f0f0bc5ce256a832356c6262e91be43f5e4eb15b5eaf38079464caf2", size = 3823184, upload-time = "2026-04-20T23:35:25.92Z" }, + { url = "https://files.pythonhosted.org/packages/bf/a0/bc2fef74b106fa345567122a0659e6d94512ed7dc0131ec44c9e5aba3725/psycopg2_binary-2.9.12-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:840066105706cd2eb29b9a1c2329620056582a4bf3e8169dec5c447042d0869f", size = 4579157, upload-time = "2026-04-20T23:35:28.542Z" }, + { url = "https://files.pythonhosted.org/packages/57/d7/d4e3b2005d3de607ca4fbb0e8742e248056e52184a6b94ebda3c1c2c329b/psycopg2_binary-2.9.12-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:863f5d12241ebe1c76a72a04c2113b6dc905f90b9cef0e9be0efd994affd9354", size = 4274970, upload-time = "2026-04-20T23:35:30.418Z" }, + { url = "https://files.pythonhosted.org/packages/2e/42/c9853f8db3967fe08bcde11f53d53b85d351750cae726ce001cb68afa9c1/psycopg2_binary-2.9.12-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a99eaab34a9010f1a086b126de467466620a750634d114d20455f3a824aae033", size = 5895175, upload-time = "2026-04-20T23:35:33.584Z" }, + { url = "https://files.pythonhosted.org/packages/eb/fd/b82b5601a97630308bef079f545ffec481bbbc795c2ba5ec416a01d03f60/psycopg2_binary-2.9.12-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ffdd7dc5463ccd61845ac37b7012d0f35a1548df9febe14f8dd549be4a0bc81e", size = 4110658, upload-time = "2026-04-20T23:35:35.638Z" }, + { url = "https://files.pythonhosted.org/packages/62/8c/32ca69b0389ef25dd22937bf9e8fbe2ce27aea20b05ded48c4ce4cb42475/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:54a0dfecab1b48731f934e06139dfe11e24219fb6d0ceb32177cf0375f14c7b5", size = 3656251, upload-time = "2026-04-20T23:35:37.854Z" }, + { url = "https://files.pythonhosted.org/packages/c4/29/96992a2b59e3b9d730fcf9612d0a387305025dc867a9fc490a9e496e074e/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:96937c9c5d891f772430f418a7a8b4691a90c3e6b93cf72b5bd7cad8cbca32a5", size = 3301810, upload-time = "2026-04-20T23:35:39.927Z" }, + { url = "https://files.pythonhosted.org/packages/56/ad/44b06659949b243ae10112cd3b20a197f9bf3e81d5651379b9eb889bfaad/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:77b348775efd4cdab410ec6609d81ccecd1139c90265fa583a7255c8064bc03d", size = 3048977, upload-time = "2026-04-20T23:35:41.806Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f2/10a1bcebadb6aa55e280e1f58975c36a7b560ea525184c7aa4064c466633/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:527e6342b3e44c2f0544f6b8e927d60de7f163f5723b8f1dfa7d2a84298738cd", size = 3351466, upload-time = "2026-04-20T23:35:43.993Z" }, + { url = "https://files.pythonhosted.org/packages/20/be/b732c8418ffa5bcfda002890f5dc4c869fc17db66ff11f53b17cfe44afc0/psycopg2_binary-2.9.12-cp314-cp314-win_amd64.whl", hash = "sha256:f12ae41fcafadb39b2785e64a40f9db05d6de2ac114077457e0e7c597f3af980", size = 2848762, upload-time = "2026-04-20T23:35:46.421Z" }, ] [[package]] name = "pycparser" version = "3.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, @@ -1066,20 +895,20 @@ wheels = [ [[package]] name = "pyln-client" -version = "25.12.1" +version = "26.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyln-bolt7" }, { name = "pyln-proto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/b1/28c46889cf1822f89a5d23644aa08806cedd17c2553afee2d72c4e101c62/pyln_client-25.12.1.tar.gz", hash = "sha256:4e92d18ce79879b891355d4a9bb79117466e98e12b83c1b5f321fc3e77039d5b", size = 92116, upload-time = "2026-01-15T00:04:15.621Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/14/0faf6b8b04f368629897a010357d3b8d21c8a800b972b433a358caebe32b/pyln_client-26.4.tar.gz", hash = "sha256:e696076fcf1329ee22198097b8139a096f653cbbfc6a03e471455ca6f5d68a46", size = 92160, upload-time = "2026-04-20T04:58:12.666Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/41/51/fb302f67a8e62eaa49d869b489cad61dac66fda9a3fd737ac56422b1b920/pyln_client-25.12.1-py3-none-any.whl", hash = "sha256:cb8c42bf4b432a3a2ec0b1ee94b96f5ad91fee1ddd0289930d55c988ab801293", size = 37633, upload-time = "2026-01-15T00:04:14.386Z" }, + { url = "https://files.pythonhosted.org/packages/60/41/f67df5064b4b9d258fb30e96e1d3ca8f0395c7c0098b24091dde6238fb8e/pyln_client-26.4-py3-none-any.whl", hash = "sha256:d5470e1ecccc1b6e46e39658c1019c62868c5aed4a0203635052803ddedcce2e", size = 37655, upload-time = "2026-04-20T04:58:11.429Z" }, ] [[package]] name = "pyln-proto" -version = "25.12.1" +version = "26.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "base58" }, @@ -1088,32 +917,31 @@ dependencies = [ { name = "cryptography" }, { name = "pysocks" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/b7/0be4a1b81e99eb9d4ad6b048ee7cdb2ae0b50fdad4e9f2e61b2e3f51efe3/pyln_proto-25.12.1.tar.gz", hash = "sha256:7167f361a6bdc9c225749c572fcc23f28f9734dee367252f7fbad13df200984b", size = 44860, upload-time = "2026-01-15T00:04:16.541Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/35/5d30332fcaa8d77be87290c5534d45c48822310611ae6ea6d3e48c242ded/pyln_proto-26.4.tar.gz", hash = "sha256:dde9e243c7bf6fbcf7904e0dbd18c4f05dc17e668b074bfab090a7376e3de6cf", size = 44861, upload-time = "2026-04-20T04:58:09.361Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/ab/f11c2f4d7a74c79f3a89f37e051461e5e93049263eb6dd9f98944aee90a0/pyln_proto-25.12.1-py3-none-any.whl", hash = "sha256:84990233ae7ba6a39e4ec9c44fc978efbf74dcfb82953f32921c242807c85764", size = 31834, upload-time = "2026-01-15T00:04:14.893Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c9/8a34960e1e9550f87697d1f05208d9cc916c14419f18f2d6013801079fc1/pyln_proto-26.4-py3-none-any.whl", hash = "sha256:97772d285aa0d0c8a1545577aeef8821e08d59676f63ae760d270e1b3015bc9d", size = 31811, upload-time = "2026-04-20T04:58:08.369Z" }, ] [[package]] name = "pyln-testing" -version = "25.12.1" +version = "26.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cheroot" }, { name = "ephemeral-port-reserve" }, { name = "flask" }, - { name = "jsonschema", version = "4.25.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "jsonschema", version = "4.26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "jsonschema" }, + { name = "mnemonic" }, { name = "psutil" }, { name = "psycopg2-binary" }, { name = "pyln-client" }, { name = "pytest" }, { name = "python-bitcoinlib" }, - { name = "requests", version = "2.32.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "requests", version = "2.33.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/d4/5ce2c805b6ab3297a00bfd8f759b1a511a91514832ab1238e24725f8db51/pyln_testing-25.12.1.tar.gz", hash = "sha256:1835e3450997c6266926487d190d14b367bcec23e4e6f04a70101b3c95c67443", size = 51333, upload-time = "2026-01-15T00:04:12.781Z" } +sdist = { url = "https://files.pythonhosted.org/packages/17/7e/275f0416e30faa3093ad31703f3d8702dc06569164a185813b23672ba6d9/pyln_testing-26.4.tar.gz", hash = "sha256:cca1f4fec65f12bfda809674ea7c231285a96ef0b8bc2c69ef4919f1b465d2ae", size = 52609, upload-time = "2026-04-20T04:58:07.746Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/83/276aa759def88a9ef79f639ee691aadf1cdbadfc4a0526cb536f68df4939/pyln_testing-25.12.1-py3-none-any.whl", hash = "sha256:2e2c7dfae4d03069f854237070a2567a31b68f625864950515bbb2d0a3d82f41", size = 53080, upload-time = "2026-01-15T00:04:11.882Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c4/3cb55bf86a2499510a24b310897903efafce847869eb1ec1c7b67b7fcfa4/pyln_testing-26.4-py3-none-any.whl", hash = "sha256:ccb7641bbbb1b6b650f1b8238aacf04d7ebeea944ebd2855a901e25d18846804", size = 54365, upload-time = "2026-04-20T04:58:06.793Z" }, ] [[package]] @@ -1132,8 +960,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, { name = "pygments" }, @@ -1144,34 +971,14 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, ] -[[package]] -name = "pytest-asyncio" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "backports-asyncio-runner", marker = "python_full_version < '3.10'" }, - { name = "pytest", marker = "python_full_version < '3.10'" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, -] - [[package]] name = "pytest-asyncio" version = "1.3.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] dependencies = [ - { name = "backports-asyncio-runner", marker = "python_full_version == '3.10.*'" }, - { name = "pytest", marker = "python_full_version >= '3.10'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" }, + { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } wheels = [ @@ -1212,248 +1019,39 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/75/46/b388d0885cf799f5bc66ca9995c83e5b7e17cb737f812a7c2591aa789ea6/python_bitcoinlib-0.12.2-py3-none-any.whl", hash = "sha256:2f29a9f475f21c12169b3a6cc8820f34f11362d7ff1200a5703dce3e4e903a44", size = 106954, upload-time = "2023-06-03T18:37:08.524Z" }, ] -[[package]] -name = "referencing" -version = "0.36.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "attrs", marker = "python_full_version < '3.10'" }, - { name = "rpds-py", version = "0.27.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "typing-extensions", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" }, -] - [[package]] name = "referencing" version = "0.37.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] dependencies = [ - { name = "attrs", marker = "python_full_version >= '3.10'" }, - { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" }, + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, ] -[[package]] -name = "requests" -version = "2.32.5" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "certifi", marker = "python_full_version < '3.10'" }, - { name = "charset-normalizer", marker = "python_full_version < '3.10'" }, - { name = "idna", marker = "python_full_version < '3.10'" }, - { name = "urllib3", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, -] - [[package]] name = "requests" version = "2.33.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] dependencies = [ - { name = "certifi", marker = "python_full_version >= '3.10'" }, - { name = "charset-normalizer", marker = "python_full_version >= '3.10'" }, - { name = "idna", marker = "python_full_version >= '3.10'" }, - { name = "urllib3", marker = "python_full_version >= '3.10'" }, + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, ] -[[package]] -name = "rpds-py" -version = "0.27.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/dd/2c0cbe774744272b0ae725f44032c77bdcab6e8bcf544bffa3b6e70c8dba/rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8", size = 27479, upload-time = "2025-08-27T12:16:36.024Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a5/ed/3aef893e2dd30e77e35d20d4ddb45ca459db59cead748cad9796ad479411/rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef", size = 371606, upload-time = "2025-08-27T12:12:25.189Z" }, - { url = "https://files.pythonhosted.org/packages/6d/82/9818b443e5d3eb4c83c3994561387f116aae9833b35c484474769c4a8faf/rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be", size = 353452, upload-time = "2025-08-27T12:12:27.433Z" }, - { url = "https://files.pythonhosted.org/packages/99/c7/d2a110ffaaa397fc6793a83c7bd3545d9ab22658b7cdff05a24a4535cc45/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61", size = 381519, upload-time = "2025-08-27T12:12:28.719Z" }, - { url = "https://files.pythonhosted.org/packages/5a/bc/e89581d1f9d1be7d0247eaef602566869fdc0d084008ba139e27e775366c/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb", size = 394424, upload-time = "2025-08-27T12:12:30.207Z" }, - { url = "https://files.pythonhosted.org/packages/ac/2e/36a6861f797530e74bb6ed53495f8741f1ef95939eed01d761e73d559067/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657", size = 523467, upload-time = "2025-08-27T12:12:31.808Z" }, - { url = "https://files.pythonhosted.org/packages/c4/59/c1bc2be32564fa499f988f0a5c6505c2f4746ef96e58e4d7de5cf923d77e/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013", size = 402660, upload-time = "2025-08-27T12:12:33.444Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ec/ef8bf895f0628dd0a59e54d81caed6891663cb9c54a0f4bb7da918cb88cf/rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a", size = 384062, upload-time = "2025-08-27T12:12:34.857Z" }, - { url = "https://files.pythonhosted.org/packages/69/f7/f47ff154be8d9a5e691c083a920bba89cef88d5247c241c10b9898f595a1/rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1", size = 401289, upload-time = "2025-08-27T12:12:36.085Z" }, - { url = "https://files.pythonhosted.org/packages/3b/d9/ca410363efd0615814ae579f6829cafb39225cd63e5ea5ed1404cb345293/rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10", size = 417718, upload-time = "2025-08-27T12:12:37.401Z" }, - { url = "https://files.pythonhosted.org/packages/e3/a0/8cb5c2ff38340f221cc067cc093d1270e10658ba4e8d263df923daa18e86/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808", size = 558333, upload-time = "2025-08-27T12:12:38.672Z" }, - { url = "https://files.pythonhosted.org/packages/6f/8c/1b0de79177c5d5103843774ce12b84caa7164dfc6cd66378768d37db11bf/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8", size = 589127, upload-time = "2025-08-27T12:12:41.48Z" }, - { url = "https://files.pythonhosted.org/packages/c8/5e/26abb098d5e01266b0f3a2488d299d19ccc26849735d9d2b95c39397e945/rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9", size = 554899, upload-time = "2025-08-27T12:12:42.925Z" }, - { url = "https://files.pythonhosted.org/packages/de/41/905cc90ced13550db017f8f20c6d8e8470066c5738ba480d7ba63e3d136b/rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4", size = 217450, upload-time = "2025-08-27T12:12:44.813Z" }, - { url = "https://files.pythonhosted.org/packages/75/3d/6bef47b0e253616ccdf67c283e25f2d16e18ccddd38f92af81d5a3420206/rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1", size = 228447, upload-time = "2025-08-27T12:12:46.204Z" }, - { url = "https://files.pythonhosted.org/packages/b5/c1/7907329fbef97cbd49db6f7303893bd1dd5a4a3eae415839ffdfb0762cae/rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881", size = 371063, upload-time = "2025-08-27T12:12:47.856Z" }, - { url = "https://files.pythonhosted.org/packages/11/94/2aab4bc86228bcf7c48760990273653a4900de89c7537ffe1b0d6097ed39/rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5", size = 353210, upload-time = "2025-08-27T12:12:49.187Z" }, - { url = "https://files.pythonhosted.org/packages/3a/57/f5eb3ecf434342f4f1a46009530e93fd201a0b5b83379034ebdb1d7c1a58/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e", size = 381636, upload-time = "2025-08-27T12:12:50.492Z" }, - { url = "https://files.pythonhosted.org/packages/ae/f4/ef95c5945e2ceb5119571b184dd5a1cc4b8541bbdf67461998cfeac9cb1e/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c", size = 394341, upload-time = "2025-08-27T12:12:52.024Z" }, - { url = "https://files.pythonhosted.org/packages/5a/7e/4bd610754bf492d398b61725eb9598ddd5eb86b07d7d9483dbcd810e20bc/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195", size = 523428, upload-time = "2025-08-27T12:12:53.779Z" }, - { url = "https://files.pythonhosted.org/packages/9f/e5/059b9f65a8c9149361a8b75094864ab83b94718344db511fd6117936ed2a/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52", size = 402923, upload-time = "2025-08-27T12:12:55.15Z" }, - { url = "https://files.pythonhosted.org/packages/f5/48/64cabb7daced2968dd08e8a1b7988bf358d7bd5bcd5dc89a652f4668543c/rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed", size = 384094, upload-time = "2025-08-27T12:12:57.194Z" }, - { url = "https://files.pythonhosted.org/packages/ae/e1/dc9094d6ff566bff87add8a510c89b9e158ad2ecd97ee26e677da29a9e1b/rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a", size = 401093, upload-time = "2025-08-27T12:12:58.985Z" }, - { url = "https://files.pythonhosted.org/packages/37/8e/ac8577e3ecdd5593e283d46907d7011618994e1d7ab992711ae0f78b9937/rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde", size = 417969, upload-time = "2025-08-27T12:13:00.367Z" }, - { url = "https://files.pythonhosted.org/packages/66/6d/87507430a8f74a93556fe55c6485ba9c259949a853ce407b1e23fea5ba31/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21", size = 558302, upload-time = "2025-08-27T12:13:01.737Z" }, - { url = "https://files.pythonhosted.org/packages/3a/bb/1db4781ce1dda3eecc735e3152659a27b90a02ca62bfeea17aee45cc0fbc/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9", size = 589259, upload-time = "2025-08-27T12:13:03.127Z" }, - { url = "https://files.pythonhosted.org/packages/7b/0e/ae1c8943d11a814d01b482e1f8da903f88047a962dff9bbdadf3bd6e6fd1/rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948", size = 554983, upload-time = "2025-08-27T12:13:04.516Z" }, - { url = "https://files.pythonhosted.org/packages/b2/d5/0b2a55415931db4f112bdab072443ff76131b5ac4f4dc98d10d2d357eb03/rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39", size = 217154, upload-time = "2025-08-27T12:13:06.278Z" }, - { url = "https://files.pythonhosted.org/packages/24/75/3b7ffe0d50dc86a6a964af0d1cc3a4a2cdf437cb7b099a4747bbb96d1819/rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15", size = 228627, upload-time = "2025-08-27T12:13:07.625Z" }, - { url = "https://files.pythonhosted.org/packages/8d/3f/4fd04c32abc02c710f09a72a30c9a55ea3cc154ef8099078fd50a0596f8e/rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746", size = 220998, upload-time = "2025-08-27T12:13:08.972Z" }, - { url = "https://files.pythonhosted.org/packages/bd/fe/38de28dee5df58b8198c743fe2bea0c785c6d40941b9950bac4cdb71a014/rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90", size = 361887, upload-time = "2025-08-27T12:13:10.233Z" }, - { url = "https://files.pythonhosted.org/packages/7c/9a/4b6c7eedc7dd90986bf0fab6ea2a091ec11c01b15f8ba0a14d3f80450468/rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5", size = 345795, upload-time = "2025-08-27T12:13:11.65Z" }, - { url = "https://files.pythonhosted.org/packages/6f/0e/e650e1b81922847a09cca820237b0edee69416a01268b7754d506ade11ad/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e", size = 385121, upload-time = "2025-08-27T12:13:13.008Z" }, - { url = "https://files.pythonhosted.org/packages/1b/ea/b306067a712988e2bff00dcc7c8f31d26c29b6d5931b461aa4b60a013e33/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881", size = 398976, upload-time = "2025-08-27T12:13:14.368Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0a/26dc43c8840cb8fe239fe12dbc8d8de40f2365e838f3d395835dde72f0e5/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec", size = 525953, upload-time = "2025-08-27T12:13:15.774Z" }, - { url = "https://files.pythonhosted.org/packages/22/14/c85e8127b573aaf3a0cbd7fbb8c9c99e735a4a02180c84da2a463b766e9e/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb", size = 407915, upload-time = "2025-08-27T12:13:17.379Z" }, - { url = "https://files.pythonhosted.org/packages/ed/7b/8f4fee9ba1fb5ec856eb22d725a4efa3deb47f769597c809e03578b0f9d9/rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5", size = 386883, upload-time = "2025-08-27T12:13:18.704Z" }, - { url = "https://files.pythonhosted.org/packages/86/47/28fa6d60f8b74fcdceba81b272f8d9836ac0340570f68f5df6b41838547b/rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a", size = 405699, upload-time = "2025-08-27T12:13:20.089Z" }, - { url = "https://files.pythonhosted.org/packages/d0/fd/c5987b5e054548df56953a21fe2ebed51fc1ec7c8f24fd41c067b68c4a0a/rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444", size = 423713, upload-time = "2025-08-27T12:13:21.436Z" }, - { url = "https://files.pythonhosted.org/packages/ac/ba/3c4978b54a73ed19a7d74531be37a8bcc542d917c770e14d372b8daea186/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a", size = 562324, upload-time = "2025-08-27T12:13:22.789Z" }, - { url = "https://files.pythonhosted.org/packages/b5/6c/6943a91768fec16db09a42b08644b960cff540c66aab89b74be6d4a144ba/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1", size = 593646, upload-time = "2025-08-27T12:13:24.122Z" }, - { url = "https://files.pythonhosted.org/packages/11/73/9d7a8f4be5f4396f011a6bb7a19fe26303a0dac9064462f5651ced2f572f/rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998", size = 558137, upload-time = "2025-08-27T12:13:25.557Z" }, - { url = "https://files.pythonhosted.org/packages/6e/96/6772cbfa0e2485bcceef8071de7821f81aeac8bb45fbfd5542a3e8108165/rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39", size = 221343, upload-time = "2025-08-27T12:13:26.967Z" }, - { url = "https://files.pythonhosted.org/packages/67/b6/c82f0faa9af1c6a64669f73a17ee0eeef25aff30bb9a1c318509efe45d84/rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594", size = 232497, upload-time = "2025-08-27T12:13:28.326Z" }, - { url = "https://files.pythonhosted.org/packages/e1/96/2817b44bd2ed11aebacc9251da03689d56109b9aba5e311297b6902136e2/rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502", size = 222790, upload-time = "2025-08-27T12:13:29.71Z" }, - { url = "https://files.pythonhosted.org/packages/cc/77/610aeee8d41e39080c7e14afa5387138e3c9fa9756ab893d09d99e7d8e98/rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b", size = 361741, upload-time = "2025-08-27T12:13:31.039Z" }, - { url = "https://files.pythonhosted.org/packages/3a/fc/c43765f201c6a1c60be2043cbdb664013def52460a4c7adace89d6682bf4/rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf", size = 345574, upload-time = "2025-08-27T12:13:32.902Z" }, - { url = "https://files.pythonhosted.org/packages/20/42/ee2b2ca114294cd9847d0ef9c26d2b0851b2e7e00bf14cc4c0b581df0fc3/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83", size = 385051, upload-time = "2025-08-27T12:13:34.228Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e8/1e430fe311e4799e02e2d1af7c765f024e95e17d651612425b226705f910/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf", size = 398395, upload-time = "2025-08-27T12:13:36.132Z" }, - { url = "https://files.pythonhosted.org/packages/82/95/9dc227d441ff2670651c27a739acb2535ccaf8b351a88d78c088965e5996/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2", size = 524334, upload-time = "2025-08-27T12:13:37.562Z" }, - { url = "https://files.pythonhosted.org/packages/87/01/a670c232f401d9ad461d9a332aa4080cd3cb1d1df18213dbd0d2a6a7ab51/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0", size = 407691, upload-time = "2025-08-27T12:13:38.94Z" }, - { url = "https://files.pythonhosted.org/packages/03/36/0a14aebbaa26fe7fab4780c76f2239e76cc95a0090bdb25e31d95c492fcd/rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418", size = 386868, upload-time = "2025-08-27T12:13:40.192Z" }, - { url = "https://files.pythonhosted.org/packages/3b/03/8c897fb8b5347ff6c1cc31239b9611c5bf79d78c984430887a353e1409a1/rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d", size = 405469, upload-time = "2025-08-27T12:13:41.496Z" }, - { url = "https://files.pythonhosted.org/packages/da/07/88c60edc2df74850d496d78a1fdcdc7b54360a7f610a4d50008309d41b94/rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274", size = 422125, upload-time = "2025-08-27T12:13:42.802Z" }, - { url = "https://files.pythonhosted.org/packages/6b/86/5f4c707603e41b05f191a749984f390dabcbc467cf833769b47bf14ba04f/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd", size = 562341, upload-time = "2025-08-27T12:13:44.472Z" }, - { url = "https://files.pythonhosted.org/packages/b2/92/3c0cb2492094e3cd9baf9e49bbb7befeceb584ea0c1a8b5939dca4da12e5/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2", size = 592511, upload-time = "2025-08-27T12:13:45.898Z" }, - { url = "https://files.pythonhosted.org/packages/10/bb/82e64fbb0047c46a168faa28d0d45a7851cd0582f850b966811d30f67ad8/rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002", size = 557736, upload-time = "2025-08-27T12:13:47.408Z" }, - { url = "https://files.pythonhosted.org/packages/00/95/3c863973d409210da7fb41958172c6b7dbe7fc34e04d3cc1f10bb85e979f/rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3", size = 221462, upload-time = "2025-08-27T12:13:48.742Z" }, - { url = "https://files.pythonhosted.org/packages/ce/2c/5867b14a81dc217b56d95a9f2a40fdbc56a1ab0181b80132beeecbd4b2d6/rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83", size = 232034, upload-time = "2025-08-27T12:13:50.11Z" }, - { url = "https://files.pythonhosted.org/packages/c7/78/3958f3f018c01923823f1e47f1cc338e398814b92d83cd278364446fac66/rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d", size = 222392, upload-time = "2025-08-27T12:13:52.587Z" }, - { url = "https://files.pythonhosted.org/packages/01/76/1cdf1f91aed5c3a7bf2eba1f1c4e4d6f57832d73003919a20118870ea659/rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228", size = 358355, upload-time = "2025-08-27T12:13:54.012Z" }, - { url = "https://files.pythonhosted.org/packages/c3/6f/bf142541229374287604caf3bb2a4ae17f0a580798fd72d3b009b532db4e/rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92", size = 342138, upload-time = "2025-08-27T12:13:55.791Z" }, - { url = "https://files.pythonhosted.org/packages/1a/77/355b1c041d6be40886c44ff5e798b4e2769e497b790f0f7fd1e78d17e9a8/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2", size = 380247, upload-time = "2025-08-27T12:13:57.683Z" }, - { url = "https://files.pythonhosted.org/packages/d6/a4/d9cef5c3946ea271ce2243c51481971cd6e34f21925af2783dd17b26e815/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723", size = 390699, upload-time = "2025-08-27T12:13:59.137Z" }, - { url = "https://files.pythonhosted.org/packages/3a/06/005106a7b8c6c1a7e91b73169e49870f4af5256119d34a361ae5240a0c1d/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802", size = 521852, upload-time = "2025-08-27T12:14:00.583Z" }, - { url = "https://files.pythonhosted.org/packages/e5/3e/50fb1dac0948e17a02eb05c24510a8fe12d5ce8561c6b7b7d1339ab7ab9c/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f", size = 402582, upload-time = "2025-08-27T12:14:02.034Z" }, - { url = "https://files.pythonhosted.org/packages/cb/b0/f4e224090dc5b0ec15f31a02d746ab24101dd430847c4d99123798661bfc/rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2", size = 384126, upload-time = "2025-08-27T12:14:03.437Z" }, - { url = "https://files.pythonhosted.org/packages/54/77/ac339d5f82b6afff1df8f0fe0d2145cc827992cb5f8eeb90fc9f31ef7a63/rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21", size = 399486, upload-time = "2025-08-27T12:14:05.443Z" }, - { url = "https://files.pythonhosted.org/packages/d6/29/3e1c255eee6ac358c056a57d6d6869baa00a62fa32eea5ee0632039c50a3/rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef", size = 414832, upload-time = "2025-08-27T12:14:06.902Z" }, - { url = "https://files.pythonhosted.org/packages/3f/db/6d498b844342deb3fa1d030598db93937a9964fcf5cb4da4feb5f17be34b/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081", size = 557249, upload-time = "2025-08-27T12:14:08.37Z" }, - { url = "https://files.pythonhosted.org/packages/60/f3/690dd38e2310b6f68858a331399b4d6dbb9132c3e8ef8b4333b96caf403d/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd", size = 587356, upload-time = "2025-08-27T12:14:10.034Z" }, - { url = "https://files.pythonhosted.org/packages/86/e3/84507781cccd0145f35b1dc32c72675200c5ce8d5b30f813e49424ef68fc/rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7", size = 555300, upload-time = "2025-08-27T12:14:11.783Z" }, - { url = "https://files.pythonhosted.org/packages/e5/ee/375469849e6b429b3516206b4580a79e9ef3eb12920ddbd4492b56eaacbe/rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688", size = 216714, upload-time = "2025-08-27T12:14:13.629Z" }, - { url = "https://files.pythonhosted.org/packages/21/87/3fc94e47c9bd0742660e84706c311a860dcae4374cf4a03c477e23ce605a/rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797", size = 228943, upload-time = "2025-08-27T12:14:14.937Z" }, - { url = "https://files.pythonhosted.org/packages/70/36/b6e6066520a07cf029d385de869729a895917b411e777ab1cde878100a1d/rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334", size = 362472, upload-time = "2025-08-27T12:14:16.333Z" }, - { url = "https://files.pythonhosted.org/packages/af/07/b4646032e0dcec0df9c73a3bd52f63bc6c5f9cda992f06bd0e73fe3fbebd/rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33", size = 345676, upload-time = "2025-08-27T12:14:17.764Z" }, - { url = "https://files.pythonhosted.org/packages/b0/16/2f1003ee5d0af4bcb13c0cf894957984c32a6751ed7206db2aee7379a55e/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a", size = 385313, upload-time = "2025-08-27T12:14:19.829Z" }, - { url = "https://files.pythonhosted.org/packages/05/cd/7eb6dd7b232e7f2654d03fa07f1414d7dfc980e82ba71e40a7c46fd95484/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b", size = 399080, upload-time = "2025-08-27T12:14:21.531Z" }, - { url = "https://files.pythonhosted.org/packages/20/51/5829afd5000ec1cb60f304711f02572d619040aa3ec033d8226817d1e571/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7", size = 523868, upload-time = "2025-08-27T12:14:23.485Z" }, - { url = "https://files.pythonhosted.org/packages/05/2c/30eebca20d5db95720ab4d2faec1b5e4c1025c473f703738c371241476a2/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136", size = 408750, upload-time = "2025-08-27T12:14:24.924Z" }, - { url = "https://files.pythonhosted.org/packages/90/1a/cdb5083f043597c4d4276eae4e4c70c55ab5accec078da8611f24575a367/rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff", size = 387688, upload-time = "2025-08-27T12:14:27.537Z" }, - { url = "https://files.pythonhosted.org/packages/7c/92/cf786a15320e173f945d205ab31585cc43969743bb1a48b6888f7a2b0a2d/rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9", size = 407225, upload-time = "2025-08-27T12:14:28.981Z" }, - { url = "https://files.pythonhosted.org/packages/33/5c/85ee16df5b65063ef26017bef33096557a4c83fbe56218ac7cd8c235f16d/rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60", size = 423361, upload-time = "2025-08-27T12:14:30.469Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8e/1c2741307fcabd1a334ecf008e92c4f47bb6f848712cf15c923becfe82bb/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e", size = 562493, upload-time = "2025-08-27T12:14:31.987Z" }, - { url = "https://files.pythonhosted.org/packages/04/03/5159321baae9b2222442a70c1f988cbbd66b9be0675dd3936461269be360/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212", size = 592623, upload-time = "2025-08-27T12:14:33.543Z" }, - { url = "https://files.pythonhosted.org/packages/ff/39/c09fd1ad28b85bc1d4554a8710233c9f4cefd03d7717a1b8fbfd171d1167/rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675", size = 558800, upload-time = "2025-08-27T12:14:35.436Z" }, - { url = "https://files.pythonhosted.org/packages/c5/d6/99228e6bbcf4baa764b18258f519a9035131d91b538d4e0e294313462a98/rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3", size = 221943, upload-time = "2025-08-27T12:14:36.898Z" }, - { url = "https://files.pythonhosted.org/packages/be/07/c802bc6b8e95be83b79bdf23d1aa61d68324cb1006e245d6c58e959e314d/rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456", size = 233739, upload-time = "2025-08-27T12:14:38.386Z" }, - { url = "https://files.pythonhosted.org/packages/c8/89/3e1b1c16d4c2d547c5717377a8df99aee8099ff050f87c45cb4d5fa70891/rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3", size = 223120, upload-time = "2025-08-27T12:14:39.82Z" }, - { url = "https://files.pythonhosted.org/packages/62/7e/dc7931dc2fa4a6e46b2a4fa744a9fe5c548efd70e0ba74f40b39fa4a8c10/rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2", size = 358944, upload-time = "2025-08-27T12:14:41.199Z" }, - { url = "https://files.pythonhosted.org/packages/e6/22/4af76ac4e9f336bfb1a5f240d18a33c6b2fcaadb7472ac7680576512b49a/rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4", size = 342283, upload-time = "2025-08-27T12:14:42.699Z" }, - { url = "https://files.pythonhosted.org/packages/1c/15/2a7c619b3c2272ea9feb9ade67a45c40b3eeb500d503ad4c28c395dc51b4/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e", size = 380320, upload-time = "2025-08-27T12:14:44.157Z" }, - { url = "https://files.pythonhosted.org/packages/a2/7d/4c6d243ba4a3057e994bb5bedd01b5c963c12fe38dde707a52acdb3849e7/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817", size = 391760, upload-time = "2025-08-27T12:14:45.845Z" }, - { url = "https://files.pythonhosted.org/packages/b4/71/b19401a909b83bcd67f90221330bc1ef11bc486fe4e04c24388d28a618ae/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec", size = 522476, upload-time = "2025-08-27T12:14:47.364Z" }, - { url = "https://files.pythonhosted.org/packages/e4/44/1a3b9715c0455d2e2f0f6df5ee6d6f5afdc423d0773a8a682ed2b43c566c/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a", size = 403418, upload-time = "2025-08-27T12:14:49.991Z" }, - { url = "https://files.pythonhosted.org/packages/1c/4b/fb6c4f14984eb56673bc868a66536f53417ddb13ed44b391998100a06a96/rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8", size = 384771, upload-time = "2025-08-27T12:14:52.159Z" }, - { url = "https://files.pythonhosted.org/packages/c0/56/d5265d2d28b7420d7b4d4d85cad8ef891760f5135102e60d5c970b976e41/rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48", size = 400022, upload-time = "2025-08-27T12:14:53.859Z" }, - { url = "https://files.pythonhosted.org/packages/8f/e9/9f5fc70164a569bdd6ed9046486c3568d6926e3a49bdefeeccfb18655875/rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb", size = 416787, upload-time = "2025-08-27T12:14:55.673Z" }, - { url = "https://files.pythonhosted.org/packages/d4/64/56dd03430ba491db943a81dcdef115a985aac5f44f565cd39a00c766d45c/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734", size = 557538, upload-time = "2025-08-27T12:14:57.245Z" }, - { url = "https://files.pythonhosted.org/packages/3f/36/92cc885a3129993b1d963a2a42ecf64e6a8e129d2c7cc980dbeba84e55fb/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb", size = 588512, upload-time = "2025-08-27T12:14:58.728Z" }, - { url = "https://files.pythonhosted.org/packages/dd/10/6b283707780a81919f71625351182b4f98932ac89a09023cb61865136244/rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0", size = 555813, upload-time = "2025-08-27T12:15:00.334Z" }, - { url = "https://files.pythonhosted.org/packages/04/2e/30b5ea18c01379da6272a92825dd7e53dc9d15c88a19e97932d35d430ef7/rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a", size = 217385, upload-time = "2025-08-27T12:15:01.937Z" }, - { url = "https://files.pythonhosted.org/packages/32/7d/97119da51cb1dd3f2f3c0805f155a3aa4a95fa44fe7d78ae15e69edf4f34/rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772", size = 230097, upload-time = "2025-08-27T12:15:03.961Z" }, - { url = "https://files.pythonhosted.org/packages/7f/6c/252e83e1ce7583c81f26d1d884b2074d40a13977e1b6c9c50bbf9a7f1f5a/rpds_py-0.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c918c65ec2e42c2a78d19f18c553d77319119bf43aa9e2edf7fb78d624355527", size = 372140, upload-time = "2025-08-27T12:15:05.441Z" }, - { url = "https://files.pythonhosted.org/packages/9d/71/949c195d927c5aeb0d0629d329a20de43a64c423a6aa53836290609ef7ec/rpds_py-0.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1fea2b1a922c47c51fd07d656324531adc787e415c8b116530a1d29c0516c62d", size = 354086, upload-time = "2025-08-27T12:15:07.404Z" }, - { url = "https://files.pythonhosted.org/packages/9f/02/e43e332ad8ce4f6c4342d151a471a7f2900ed1d76901da62eb3762663a71/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbf94c58e8e0cd6b6f38d8de67acae41b3a515c26169366ab58bdca4a6883bb8", size = 382117, upload-time = "2025-08-27T12:15:09.275Z" }, - { url = "https://files.pythonhosted.org/packages/d0/05/b0fdeb5b577197ad72812bbdfb72f9a08fa1e64539cc3940b1b781cd3596/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2a8fed130ce946d5c585eddc7c8eeef0051f58ac80a8ee43bd17835c144c2cc", size = 394520, upload-time = "2025-08-27T12:15:10.727Z" }, - { url = "https://files.pythonhosted.org/packages/67/1f/4cfef98b2349a7585181e99294fa2a13f0af06902048a5d70f431a66d0b9/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:037a2361db72ee98d829bc2c5b7cc55598ae0a5e0ec1823a56ea99374cfd73c1", size = 522657, upload-time = "2025-08-27T12:15:12.613Z" }, - { url = "https://files.pythonhosted.org/packages/44/55/ccf37ddc4c6dce7437b335088b5ca18da864b334890e2fe9aa6ddc3f79a9/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5281ed1cc1d49882f9997981c88df1a22e140ab41df19071222f7e5fc4e72125", size = 402967, upload-time = "2025-08-27T12:15:14.113Z" }, - { url = "https://files.pythonhosted.org/packages/74/e5/5903f92e41e293b07707d5bf00ef39a0eb2af7190aff4beaf581a6591510/rpds_py-0.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd50659a069c15eef8aa3d64bbef0d69fd27bb4a50c9ab4f17f83a16cbf8905", size = 384372, upload-time = "2025-08-27T12:15:15.842Z" }, - { url = "https://files.pythonhosted.org/packages/8f/e3/fbb409e18aeefc01e49f5922ac63d2d914328430e295c12183ce56ebf76b/rpds_py-0.27.1-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:c4b676c4ae3921649a15d28ed10025548e9b561ded473aa413af749503c6737e", size = 401264, upload-time = "2025-08-27T12:15:17.388Z" }, - { url = "https://files.pythonhosted.org/packages/55/79/529ad07794e05cb0f38e2f965fc5bb20853d523976719400acecc447ec9d/rpds_py-0.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:079bc583a26db831a985c5257797b2b5d3affb0386e7ff886256762f82113b5e", size = 418691, upload-time = "2025-08-27T12:15:19.144Z" }, - { url = "https://files.pythonhosted.org/packages/33/39/6554a7fd6d9906fda2521c6d52f5d723dca123529fb719a5b5e074c15e01/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e44099bd522cba71a2c6b97f68e19f40e7d85399de899d66cdb67b32d7cb786", size = 558989, upload-time = "2025-08-27T12:15:21.087Z" }, - { url = "https://files.pythonhosted.org/packages/19/b2/76fa15173b6f9f445e5ef15120871b945fb8dd9044b6b8c7abe87e938416/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e202e6d4188e53c6661af813b46c37ca2c45e497fc558bacc1a7630ec2695aec", size = 589835, upload-time = "2025-08-27T12:15:22.696Z" }, - { url = "https://files.pythonhosted.org/packages/ee/9e/5560a4b39bab780405bed8a88ee85b30178061d189558a86003548dea045/rpds_py-0.27.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f41f814b8eaa48768d1bb551591f6ba45f87ac76899453e8ccd41dba1289b04b", size = 555227, upload-time = "2025-08-27T12:15:24.278Z" }, - { url = "https://files.pythonhosted.org/packages/52/d7/cd9c36215111aa65724c132bf709c6f35175973e90b32115dedc4ced09cb/rpds_py-0.27.1-cp39-cp39-win32.whl", hash = "sha256:9e71f5a087ead99563c11fdaceee83ee982fd39cf67601f4fd66cb386336ee52", size = 217899, upload-time = "2025-08-27T12:15:25.926Z" }, - { url = "https://files.pythonhosted.org/packages/5b/e0/d75ab7b4dd8ba777f6b365adbdfc7614bbfe7c5f05703031dfa4b61c3d6c/rpds_py-0.27.1-cp39-cp39-win_amd64.whl", hash = "sha256:71108900c9c3c8590697244b9519017a400d9ba26a36c48381b3f64743a44aab", size = 228725, upload-time = "2025-08-27T12:15:27.398Z" }, - { url = "https://files.pythonhosted.org/packages/d5/63/b7cc415c345625d5e62f694ea356c58fb964861409008118f1245f8c3347/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf", size = 371360, upload-time = "2025-08-27T12:15:29.218Z" }, - { url = "https://files.pythonhosted.org/packages/e5/8c/12e1b24b560cf378b8ffbdb9dc73abd529e1adcfcf82727dfd29c4a7b88d/rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3", size = 353933, upload-time = "2025-08-27T12:15:30.837Z" }, - { url = "https://files.pythonhosted.org/packages/9b/85/1bb2210c1f7a1b99e91fea486b9f0f894aa5da3a5ec7097cbad7dec6d40f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636", size = 382962, upload-time = "2025-08-27T12:15:32.348Z" }, - { url = "https://files.pythonhosted.org/packages/cc/c9/a839b9f219cf80ed65f27a7f5ddbb2809c1b85c966020ae2dff490e0b18e/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8", size = 394412, upload-time = "2025-08-27T12:15:33.839Z" }, - { url = "https://files.pythonhosted.org/packages/02/2d/b1d7f928b0b1f4fc2e0133e8051d199b01d7384875adc63b6ddadf3de7e5/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc", size = 523972, upload-time = "2025-08-27T12:15:35.377Z" }, - { url = "https://files.pythonhosted.org/packages/a9/af/2cbf56edd2d07716df1aec8a726b3159deb47cb5c27e1e42b71d705a7c2f/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8", size = 403273, upload-time = "2025-08-27T12:15:37.051Z" }, - { url = "https://files.pythonhosted.org/packages/c0/93/425e32200158d44ff01da5d9612c3b6711fe69f606f06e3895511f17473b/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc", size = 385278, upload-time = "2025-08-27T12:15:38.571Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1a/1a04a915ecd0551bfa9e77b7672d1937b4b72a0fc204a17deef76001cfb2/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71", size = 402084, upload-time = "2025-08-27T12:15:40.529Z" }, - { url = "https://files.pythonhosted.org/packages/51/f7/66585c0fe5714368b62951d2513b684e5215beaceab2c6629549ddb15036/rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad", size = 419041, upload-time = "2025-08-27T12:15:42.191Z" }, - { url = "https://files.pythonhosted.org/packages/8e/7e/83a508f6b8e219bba2d4af077c35ba0e0cdd35a751a3be6a7cba5a55ad71/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab", size = 560084, upload-time = "2025-08-27T12:15:43.839Z" }, - { url = "https://files.pythonhosted.org/packages/66/66/bb945683b958a1b19eb0fe715594630d0f36396ebdef4d9b89c2fa09aa56/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059", size = 590115, upload-time = "2025-08-27T12:15:46.647Z" }, - { url = "https://files.pythonhosted.org/packages/12/00/ccfaafaf7db7e7adace915e5c2f2c2410e16402561801e9c7f96683002d3/rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b", size = 556561, upload-time = "2025-08-27T12:15:48.219Z" }, - { url = "https://files.pythonhosted.org/packages/e1/b7/92b6ed9aad103bfe1c45df98453dfae40969eef2cb6c6239c58d7e96f1b3/rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819", size = 229125, upload-time = "2025-08-27T12:15:49.956Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ed/e1fba02de17f4f76318b834425257c8ea297e415e12c68b4361f63e8ae92/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df", size = 371402, upload-time = "2025-08-27T12:15:51.561Z" }, - { url = "https://files.pythonhosted.org/packages/af/7c/e16b959b316048b55585a697e94add55a4ae0d984434d279ea83442e460d/rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3", size = 354084, upload-time = "2025-08-27T12:15:53.219Z" }, - { url = "https://files.pythonhosted.org/packages/de/c1/ade645f55de76799fdd08682d51ae6724cb46f318573f18be49b1e040428/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9", size = 383090, upload-time = "2025-08-27T12:15:55.158Z" }, - { url = "https://files.pythonhosted.org/packages/1f/27/89070ca9b856e52960da1472efcb6c20ba27cfe902f4f23ed095b9cfc61d/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc", size = 394519, upload-time = "2025-08-27T12:15:57.238Z" }, - { url = "https://files.pythonhosted.org/packages/b3/28/be120586874ef906aa5aeeae95ae8df4184bc757e5b6bd1c729ccff45ed5/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4", size = 523817, upload-time = "2025-08-27T12:15:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/a8/ef/70cc197bc11cfcde02a86f36ac1eed15c56667c2ebddbdb76a47e90306da/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66", size = 403240, upload-time = "2025-08-27T12:16:00.923Z" }, - { url = "https://files.pythonhosted.org/packages/cf/35/46936cca449f7f518f2f4996e0e8344db4b57e2081e752441154089d2a5f/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e", size = 385194, upload-time = "2025-08-27T12:16:02.802Z" }, - { url = "https://files.pythonhosted.org/packages/e1/62/29c0d3e5125c3270b51415af7cbff1ec587379c84f55a5761cc9efa8cd06/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c", size = 402086, upload-time = "2025-08-27T12:16:04.806Z" }, - { url = "https://files.pythonhosted.org/packages/8f/66/03e1087679227785474466fdd04157fb793b3b76e3fcf01cbf4c693c1949/rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf", size = 419272, upload-time = "2025-08-27T12:16:06.471Z" }, - { url = "https://files.pythonhosted.org/packages/6a/24/e3e72d265121e00b063aef3e3501e5b2473cf1b23511d56e529531acf01e/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf", size = 560003, upload-time = "2025-08-27T12:16:08.06Z" }, - { url = "https://files.pythonhosted.org/packages/26/ca/f5a344c534214cc2d41118c0699fffbdc2c1bc7046f2a2b9609765ab9c92/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6", size = 590482, upload-time = "2025-08-27T12:16:10.137Z" }, - { url = "https://files.pythonhosted.org/packages/ce/08/4349bdd5c64d9d193c360aa9db89adeee6f6682ab8825dca0a3f535f434f/rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a", size = 556523, upload-time = "2025-08-27T12:16:12.188Z" }, - { url = "https://files.pythonhosted.org/packages/4e/ea/5463cd5048a7a2fcdae308b6e96432802132c141bfb9420260142632a0f1/rpds_py-0.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa8933159edc50be265ed22b401125c9eebff3171f570258854dbce3ecd55475", size = 371778, upload-time = "2025-08-27T12:16:13.851Z" }, - { url = "https://files.pythonhosted.org/packages/0d/c8/f38c099db07f5114029c1467649d308543906933eebbc226d4527a5f4693/rpds_py-0.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a50431bf02583e21bf273c71b89d710e7a710ad5e39c725b14e685610555926f", size = 354394, upload-time = "2025-08-27T12:16:15.609Z" }, - { url = "https://files.pythonhosted.org/packages/7d/79/b76f97704d9dd8ddbd76fed4c4048153a847c5d6003afe20a6b5c3339065/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78af06ddc7fe5cc0e967085a9115accee665fb912c22a3f54bad70cc65b05fe6", size = 382348, upload-time = "2025-08-27T12:16:17.251Z" }, - { url = "https://files.pythonhosted.org/packages/8a/3f/ef23d3c1be1b837b648a3016d5bbe7cfe711422ad110b4081c0a90ef5a53/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70d0738ef8fee13c003b100c2fbd667ec4f133468109b3472d249231108283a3", size = 394159, upload-time = "2025-08-27T12:16:19.251Z" }, - { url = "https://files.pythonhosted.org/packages/74/8a/9e62693af1a34fd28b1a190d463d12407bd7cf561748cb4745845d9548d3/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2f6fd8a1cea5bbe599b6e78a6e5ee08db434fc8ffea51ff201c8765679698b3", size = 522775, upload-time = "2025-08-27T12:16:20.929Z" }, - { url = "https://files.pythonhosted.org/packages/36/0d/8d5bb122bf7a60976b54c5c99a739a3819f49f02d69df3ea2ca2aff47d5c/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8177002868d1426305bb5de1e138161c2ec9eb2d939be38291d7c431c4712df8", size = 402633, upload-time = "2025-08-27T12:16:22.548Z" }, - { url = "https://files.pythonhosted.org/packages/0f/0e/237948c1f425e23e0cf5a566d702652a6e55c6f8fbd332a1792eb7043daf/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:008b839781d6c9bf3b6a8984d1d8e56f0ec46dc56df61fd669c49b58ae800400", size = 384867, upload-time = "2025-08-27T12:16:24.29Z" }, - { url = "https://files.pythonhosted.org/packages/d6/0a/da0813efcd998d260cbe876d97f55b0f469ada8ba9cbc47490a132554540/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:a55b9132bb1ade6c734ddd2759c8dc132aa63687d259e725221f106b83a0e485", size = 401791, upload-time = "2025-08-27T12:16:25.954Z" }, - { url = "https://files.pythonhosted.org/packages/51/78/c6c9e8a8aaca416a6f0d1b6b4a6ee35b88fe2c5401d02235d0a056eceed2/rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a46fdec0083a26415f11d5f236b79fa1291c32aaa4a17684d82f7017a1f818b1", size = 419525, upload-time = "2025-08-27T12:16:27.659Z" }, - { url = "https://files.pythonhosted.org/packages/a3/69/5af37e1d71487cf6d56dd1420dc7e0c2732c1b6ff612aa7a88374061c0a8/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8a63b640a7845f2bdd232eb0d0a4a2dd939bcdd6c57e6bb134526487f3160ec5", size = 559255, upload-time = "2025-08-27T12:16:29.343Z" }, - { url = "https://files.pythonhosted.org/packages/40/7f/8b7b136069ef7ac3960eda25d832639bdb163018a34c960ed042dd1707c8/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:7e32721e5d4922deaaf963469d795d5bde6093207c52fec719bd22e5d1bedbc4", size = 590384, upload-time = "2025-08-27T12:16:31.005Z" }, - { url = "https://files.pythonhosted.org/packages/d8/06/c316d3f6ff03f43ccb0eba7de61376f8ec4ea850067dddfafe98274ae13c/rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2c426b99a068601b5f4623573df7a7c3d72e87533a2dd2253353a03e7502566c", size = 555959, upload-time = "2025-08-27T12:16:32.73Z" }, - { url = "https://files.pythonhosted.org/packages/60/94/384cf54c430b9dac742bbd2ec26c23feb78ded0d43d6d78563a281aec017/rpds_py-0.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4fc9b7fe29478824361ead6e14e4f5aed570d477e06088826537e202d25fe859", size = 228784, upload-time = "2025-08-27T12:16:34.428Z" }, -] - [[package]] name = "rpds-py" version = "0.30.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, @@ -1693,12 +1291,3 @@ sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd1 wheels = [ { url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", size = 226459, upload-time = "2026-04-02T18:49:12.72Z" }, ] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -] From f13c30452e60378c3af07d52e8a875cc56182070 Mon Sep 17 00:00:00 2001 From: daywalker90 <8257956+daywalker90@users.noreply.github.com> Date: Thu, 23 Apr 2026 00:17:58 +0200 Subject: [PATCH 02/56] CI: add cargo updates to dependabot --- .github/dependabot.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6867e71..083ac61 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,3 +9,29 @@ updates: directory: "/" schedule: interval: "weekly" + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "UTC" + + groups: + all-dependencies: + patterns: + - "*" + + cooldown: + default-days: 7 + + open-pull-requests-limit: 1 + + versioning-strategy: "auto" + + labels: + - "dependencies" + - "rust" + + allow: + - dependency-type: "all" From 3caf173a8667636a6f1903ec349b7075001983c3 Mon Sep 17 00:00:00 2001 From: daywalker90 <8257956+daywalker90@users.noreply.github.com> Date: Thu, 23 Apr 2026 00:20:12 +0200 Subject: [PATCH 03/56] CI: update to CLN v26.04 --- .../{latest_v25.02.yml => latest_v26.04.yml} | 8 +++---- .../{master_v25.02.yml => master_v26.04.yml} | 6 ++--- README.md | 24 +++++++++---------- 3 files changed, 19 insertions(+), 19 deletions(-) rename .github/workflows/{latest_v25.02.yml => latest_v26.04.yml} (51%) rename .github/workflows/{master_v25.02.yml => master_v26.04.yml} (80%) diff --git a/.github/workflows/latest_v25.02.yml b/.github/workflows/latest_v26.04.yml similarity index 51% rename from .github/workflows/latest_v25.02.yml rename to .github/workflows/latest_v26.04.yml index 6d8590c..f545ca4 100644 --- a/.github/workflows/latest_v25.02.yml +++ b/.github/workflows/latest_v26.04.yml @@ -1,4 +1,4 @@ -name: Latest release on CLN v25.02.x +name: Latest release on CLN v26.04.x on: release: @@ -9,6 +9,6 @@ jobs: call-ci: uses: ./.github/workflows/ci.yml with: - cln-version: "v25.02.1" - pyln-version: "25.2.1" - tagged-release: true \ No newline at end of file + cln-version: "v26.04" + pyln-version: "26.4" + tagged-release: true diff --git a/.github/workflows/master_v25.02.yml b/.github/workflows/master_v26.04.yml similarity index 80% rename from .github/workflows/master_v25.02.yml rename to .github/workflows/master_v26.04.yml index e7b060f..5fcd616 100644 --- a/.github/workflows/master_v25.02.yml +++ b/.github/workflows/master_v26.04.yml @@ -1,4 +1,4 @@ -name: master on CLN v25.02.x +name: master on CLN v26.04.x on: push: @@ -19,6 +19,6 @@ jobs: call-ci: uses: ./.github/workflows/ci.yml with: - cln-version: "v25.02.1" - pyln-version: "25.2.1" + cln-version: "v26.04" + pyln-version: "26.4" tagged-release: false diff --git a/README.md b/README.md index 8d8c56b..6d78898 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,4 @@ - - - - + + + +
- - - - - - - -
@@ -47,6 +35,18 @@
+ + + + + + + +
From 80926ba9b7d7cb9f5955a6e97c359822b4880fee Mon Sep 17 00:00:00 2001 From: daywalker90 <8257956+daywalker90@users.noreply.github.com> Date: Thu, 23 Apr 2026 00:20:49 +0200 Subject: [PATCH 04/56] CI: update to min python 3.10 and bitcoind 31.0 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 44922b0..5275ba9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,10 +24,10 @@ jobs: strategy: fail-fast: false matrix: - bitcoind-version: ["30.2"] + bitcoind-version: ["31.0"] experimental: [1] deprecated: [0] - python-version: ["3.9", "3.14"] + python-version: ["3.10", "3.14"] os: ["ubuntu-latest"] runs-on: ${{ matrix.os }} From f26b82bb518a45c95d83ac7654c3b53f6b69bb2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 21:34:24 +0000 Subject: [PATCH 05/56] Bump the cargo group across 1 directory with 2 updates Bumps the cargo group with 2 updates in the / directory: [rand](https://github.com/rust-random/rand) and [rustls-webpki](https://github.com/rustls/webpki). Updates `rand` from 0.8.5 to 0.8.6 - [Release notes](https://github.com/rust-random/rand/releases) - [Changelog](https://github.com/rust-random/rand/blob/0.8.6/CHANGELOG.md) - [Commits](https://github.com/rust-random/rand/compare/0.8.5...0.8.6) Updates `rustls-webpki` from 0.103.10 to 0.103.13 - [Release notes](https://github.com/rustls/webpki/releases) - [Commits](https://github.com/rustls/webpki/compare/v/0.103.10...v/0.103.13) --- updated-dependencies: - dependency-name: rand dependency-version: 0.8.6 dependency-type: indirect dependency-group: cargo - dependency-name: rustls-webpki dependency-version: 0.103.13 dependency-type: indirect dependency-group: cargo ... Signed-off-by: dependabot[bot] --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3da29df..914b225 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1094,9 +1094,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -1228,9 +1228,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.10" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "ring", "rustls-pki-types", @@ -1277,7 +1277,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes", - "rand 0.8.5", + "rand 0.8.6", "secp256k1-sys", "serde", ] From aff911ed90f3c602747c2b77e68039fce7c77990 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 07:16:28 +0000 Subject: [PATCH 06/56] Bump the all-dependencies group with 10 updates Bumps the all-dependencies group with 10 updates: | Package | From | To | | --- | --- | --- | | [tokio](https://github.com/tokio-rs/tokio) | `1.50.0` | `1.52.0` | | [uuid](https://github.com/uuid-rs/uuid) | `1.23.0` | `1.23.1` | | [bitflags](https://github.com/bitflags/bitflags) | `2.11.0` | `2.11.1` | | [cc](https://github.com/rust-lang/cc-rs) | `1.2.58` | `1.2.60` | | [indexmap](https://github.com/indexmap-rs/indexmap) | `2.13.1` | `2.14.0` | | [libc](https://github.com/rust-lang/libc) | `0.2.184` | `0.2.185` | | [lru](https://github.com/jeromefroe/lru-rs) | `0.16.3` | `0.16.4` | | [rustls](https://github.com/rustls/rustls) | `0.23.37` | `0.23.38` | | [semver](https://github.com/dtolnay/semver) | `1.0.27` | `1.0.28` | | [tokio-macros](https://github.com/tokio-rs/tokio) | `2.6.1` | `2.7.0` | Updates `tokio` from 1.50.0 to 1.52.0 - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.50.0...tokio-1.52.0) Updates `uuid` from 1.23.0 to 1.23.1 - [Release notes](https://github.com/uuid-rs/uuid/releases) - [Commits](https://github.com/uuid-rs/uuid/compare/v1.23.0...v1.23.1) Updates `bitflags` from 2.11.0 to 2.11.1 - [Release notes](https://github.com/bitflags/bitflags/releases) - [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md) - [Commits](https://github.com/bitflags/bitflags/compare/2.11.0...2.11.1) Updates `cc` from 1.2.58 to 1.2.60 - [Release notes](https://github.com/rust-lang/cc-rs/releases) - [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.58...cc-v1.2.60) Updates `indexmap` from 2.13.1 to 2.14.0 - [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md) - [Commits](https://github.com/indexmap-rs/indexmap/compare/2.13.1...2.14.0) Updates `libc` from 0.2.184 to 0.2.185 - [Release notes](https://github.com/rust-lang/libc/releases) - [Changelog](https://github.com/rust-lang/libc/blob/0.2.185/CHANGELOG.md) - [Commits](https://github.com/rust-lang/libc/compare/0.2.184...0.2.185) Updates `lru` from 0.16.3 to 0.16.4 - [Changelog](https://github.com/jeromefroe/lru-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/jeromefroe/lru-rs/compare/0.16.3...0.16.4) Updates `rustls` from 0.23.37 to 0.23.38 - [Release notes](https://github.com/rustls/rustls/releases) - [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md) - [Commits](https://github.com/rustls/rustls/compare/v/0.23.37...v/0.23.38) Updates `semver` from 1.0.27 to 1.0.28 - [Release notes](https://github.com/dtolnay/semver/releases) - [Commits](https://github.com/dtolnay/semver/compare/1.0.27...1.0.28) Updates `tokio-macros` from 2.6.1 to 2.7.0 - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-macros-2.6.1...tokio-macros-2.7.0) --- updated-dependencies: - dependency-name: tokio dependency-version: 1.52.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-dependencies - dependency-name: uuid dependency-version: 1.23.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-dependencies - dependency-name: bitflags dependency-version: 2.11.1 dependency-type: indirect update-type: version-update:semver-patch dependency-group: all-dependencies - dependency-name: cc dependency-version: 1.2.60 dependency-type: indirect update-type: version-update:semver-patch dependency-group: all-dependencies - dependency-name: indexmap dependency-version: 2.14.0 dependency-type: indirect update-type: version-update:semver-minor dependency-group: all-dependencies - dependency-name: libc dependency-version: 0.2.185 dependency-type: indirect update-type: version-update:semver-patch dependency-group: all-dependencies - dependency-name: lru dependency-version: 0.16.4 dependency-type: indirect update-type: version-update:semver-patch dependency-group: all-dependencies - dependency-name: rustls dependency-version: 0.23.38 dependency-type: indirect update-type: version-update:semver-patch dependency-group: all-dependencies - dependency-name: semver dependency-version: 1.0.28 dependency-type: indirect update-type: version-update:semver-patch dependency-group: all-dependencies - dependency-name: tokio-macros dependency-version: 2.7.0 dependency-type: indirect update-type: version-update:semver-minor dependency-group: all-dependencies ... Signed-off-by: dependabot[bot] --- Cargo.lock | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 914b225..88dc220 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -176,9 +176,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "block-buffer" @@ -221,9 +221,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.58" +version = "1.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" +checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" dependencies = [ "find-msvc-tools", "shlex", @@ -566,9 +566,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" [[package]] name = "heck" @@ -732,12 +732,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "serde", "serde_core", ] @@ -796,9 +796,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.184" +version = "0.2.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" +checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" [[package]] name = "litemap" @@ -832,9 +832,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.16.3" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" [[package]] name = "matchers" @@ -1205,9 +1205,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.37" +version = "0.23.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +checksum = "7c2c118cb077cca2822033836dfb1b975355dfb784b5e8da48f7b6c5db74e60e" dependencies = [ "once_cell", "ring", @@ -1293,9 +1293,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" @@ -1509,9 +1509,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.50.0" +version = "1.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "a91135f59b1cbf38c91e73cf3386fca9bb77915c45ce2771460c9d92f0f3d776" dependencies = [ "bytes", "libc", @@ -1524,9 +1524,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -1746,9 +1746,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.23.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ "getrandom 0.4.2", "js-sys", From ce0e1fe5853103f48a1e4ae02b40a0e2a5a9623a Mon Sep 17 00:00:00 2001 From: daywalker90 <8257956+daywalker90@users.noreply.github.com> Date: Thu, 23 Apr 2026 09:31:54 +0200 Subject: [PATCH 07/56] Cargo.lock: update tokio to v1.52.1 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88dc220..9a88dd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1509,9 +1509,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.0" +version = "1.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91135f59b1cbf38c91e73cf3386fca9bb77915c45ce2771460c9d92f0f3d776" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" dependencies = [ "bytes", "libc", From 7ec1e68d6cd293dcc7b08d130ccbc755314e4482 Mon Sep 17 00:00:00 2001 From: daywalker90 <8257956+daywalker90@users.noreply.github.com> Date: Thu, 23 Apr 2026 11:00:51 +0200 Subject: [PATCH 08/56] fix budget race condition --- CHANGELOG.md | 5 +++++ Cargo.lock | 2 +- Cargo.toml | 2 +- coffee.yml | 2 +- src/rpc.rs | 8 ++++---- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cb0208..6eb4680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## [0.1.9] 2026-04-23 + +### Fixed +- ``nip47-budget``: race condition where new budget settings would sometimes be ignored + ## [0.1.8] 2026-04-03 ### Changed diff --git a/Cargo.lock b/Cargo.lock index 9a88dd3..99c47f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -272,7 +272,7 @@ dependencies = [ [[package]] name = "cln-nip47" -version = "0.1.8" +version = "0.1.9" dependencies = [ "anyhow", "cln-plugin", diff --git a/Cargo.toml b/Cargo.toml index f846dd5..554256b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cln-nip47" -version = "0.1.8" +version = "0.1.9" edition = "2024" rust-version = "1.85.0" diff --git a/coffee.yml b/coffee.yml index 9c7d20d..980549a 100644 --- a/coffee.yml +++ b/coffee.yml @@ -1,6 +1,6 @@ plugin: name: cln-nip47 - version: 0.1.8 + version: 0.1.9 lang: rust install: | cargo build --release && cp target/release/cln-nip47 . && cargo clean diff --git a/src/rpc.rs b/src/rpc.rs index bbc12db..272d483 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -6,15 +6,15 @@ use cln_rpc::model::requests::{ DeldatastoreRequest, ListdatastoreRequest, }; -use nostr_sdk::{nips::nip47::NostrWalletConnectURI, Keys, SecretKey, Timestamp}; +use nostr_sdk::{Keys, SecretKey, Timestamp, nips::nip47::NostrWalletConnectURI}; use serde_json::json; use crate::{ + PLUGIN_NAME, nwc::{run_nwc, send_nwc_info_event, start_nwc_budget_job, stop_nwc, stop_nwc_budget_job}, parse::parse_time_period, structs::{BudgetIntervalConfig, NwcStore, PluginState}, util::{build_capabilities, is_read_only_nwc, load_nwc_store, update_nwc_store}, - PLUGIN_NAME, }; pub async fn nwc_create( @@ -140,12 +140,12 @@ pub async fn nwc_budget( let is_new_nwc_read_only = is_read_only_nwc(&nwc_store); + update_nwc_store(&mut rpc, &label, nwc_store.clone()).await?; + if nwc_store.interval_config.is_some() { start_nwc_budget_job(&plugin, label.clone()); } - update_nwc_store(&mut rpc, &label, nwc_store.clone()).await?; - if is_old_nwc_read_only != is_new_nwc_read_only { let wallet_keys = Keys::new(SecretKey::from_hex(&nwc_store.walletkey)?); let (method_capabilities, _) = build_capabilities(is_new_nwc_read_only, &plugin); From 07159082d80bfb6ed89cbc7f4dcce4a0db23be59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 06:46:14 +0000 Subject: [PATCH 09/56] Bump the all-dependencies group with 2 updates Bumps the all-dependencies group with 2 updates: [bitcoin-units](https://github.com/rust-bitcoin/rust-bitcoin) and [typenum](https://github.com/paholg/typenum). Updates `bitcoin-units` from 0.1.2 to 0.1.3 - [Release notes](https://github.com/rust-bitcoin/rust-bitcoin/releases) - [Changelog](https://github.com/rust-bitcoin/rust-bitcoin/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-bitcoin/rust-bitcoin/compare/io-0.1.2...bitcoin-units-0.1.3) Updates `typenum` from 1.19.0 to 1.20.0 - [Release notes](https://github.com/paholg/typenum/releases) - [Changelog](https://github.com/paholg/typenum/blob/main/CHANGELOG.md) - [Commits](https://github.com/paholg/typenum/compare/v1.19.0...v1.20.0) --- updated-dependencies: - dependency-name: bitcoin-units dependency-version: 0.1.3 dependency-type: indirect update-type: version-update:semver-patch dependency-group: all-dependencies - dependency-name: typenum dependency-version: 1.20.0 dependency-type: indirect update-type: version-update:semver-minor dependency-group: all-dependencies ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 99c47f9..37c8388 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,9 +155,9 @@ checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" [[package]] name = "bitcoin-units" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" +checksum = "346568ebaab2918487cea76dd55dae13c27bb618cdb737c952e69eb2017c4118" dependencies = [ "bitcoin-internals", "serde", @@ -1678,9 +1678,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "unicode-ident" From 0c2a2a15fb2d260b980352f438f6901cd1e3ec53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 06:56:06 +0000 Subject: [PATCH 10/56] Bump the all-dependencies group with 4 updates Bumps the all-dependencies group with 4 updates: [cc](https://github.com/rust-lang/cc-rs), [data-encoding](https://github.com/ia0/data-encoding), [libc](https://github.com/rust-lang/libc) and [rustls-pki-types](https://github.com/rustls/pki-types). Updates `cc` from 1.2.60 to 1.2.61 - [Release notes](https://github.com/rust-lang/cc-rs/releases) - [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.60...cc-v1.2.61) Updates `data-encoding` from 2.10.0 to 2.11.0 - [Commits](https://github.com/ia0/data-encoding/compare/v2.10.0...v2.11.0) Updates `libc` from 0.2.185 to 0.2.186 - [Release notes](https://github.com/rust-lang/libc/releases) - [Changelog](https://github.com/rust-lang/libc/blob/0.2.186/CHANGELOG.md) - [Commits](https://github.com/rust-lang/libc/compare/0.2.185...0.2.186) Updates `rustls-pki-types` from 1.14.0 to 1.14.1 - [Release notes](https://github.com/rustls/pki-types/releases) - [Commits](https://github.com/rustls/pki-types/compare/v/1.14.0...v/1.14.1) --- updated-dependencies: - dependency-name: cc dependency-version: 1.2.61 dependency-type: indirect update-type: version-update:semver-patch dependency-group: all-dependencies - dependency-name: data-encoding dependency-version: 2.11.0 dependency-type: indirect update-type: version-update:semver-minor dependency-group: all-dependencies - dependency-name: libc dependency-version: 0.2.186 dependency-type: indirect update-type: version-update:semver-patch dependency-group: all-dependencies - dependency-name: rustls-pki-types dependency-version: 1.14.1 dependency-type: indirect update-type: version-update:semver-patch dependency-group: all-dependencies ... Signed-off-by: dependabot[bot] --- Cargo.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37c8388..957e671 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,9 +221,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.60" +version = "1.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" dependencies = [ "find-msvc-tools", "shlex", @@ -348,9 +348,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "digest" @@ -796,9 +796,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.185" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "litemap" @@ -1219,9 +1219,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "zeroize", ] From a2781bee20933244c6006108ef57ab4e665dbc4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 07:30:53 +0000 Subject: [PATCH 11/56] Bump rustls from 0.23.39 to 0.23.40 in the all-dependencies group Bumps the all-dependencies group with 1 update: [rustls](https://github.com/rustls/rustls). Updates `rustls` from 0.23.39 to 0.23.40 - [Release notes](https://github.com/rustls/rustls/releases) - [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md) - [Commits](https://github.com/rustls/rustls/compare/v/0.23.39...v/0.23.40) --- updated-dependencies: - dependency-name: rustls dependency-version: 0.23.40 dependency-type: indirect update-type: version-update:semver-patch dependency-group: all-dependencies ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 957e671..00f6ec3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1205,9 +1205,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.39" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2c118cb077cca2822033836dfb1b975355dfb784b5e8da48f7b6c5db74e60e" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "once_cell", "ring", From 004e403d48f0d428561d8ebb921fe9b7acc05ef7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 09:11:09 +0000 Subject: [PATCH 12/56] Bump the all-dependencies group with 3 updates Bumps the all-dependencies group with 3 updates: [tokio](https://github.com/tokio-rs/tokio), [bitcoin](https://github.com/rust-bitcoin/rust-bitcoin) and [cc](https://github.com/rust-lang/cc-rs). Updates `tokio` from 1.52.1 to 1.52.3 - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.52.1...tokio-1.52.3) Updates `bitcoin` from 0.32.8 to 0.32.9 - [Release notes](https://github.com/rust-bitcoin/rust-bitcoin/releases) - [Changelog](https://github.com/rust-bitcoin/rust-bitcoin/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-bitcoin/rust-bitcoin/compare/bitcoin-0.32.8...bitcoin-0.32.9) Updates `cc` from 1.2.61 to 1.2.62 - [Release notes](https://github.com/rust-lang/cc-rs/releases) - [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.61...cc-v1.2.62) --- updated-dependencies: - dependency-name: tokio dependency-version: 1.52.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-dependencies - dependency-name: bitcoin dependency-version: 0.32.9 dependency-type: indirect update-type: version-update:semver-patch dependency-group: all-dependencies - dependency-name: cc dependency-version: 1.2.62 dependency-type: indirect update-type: version-update:semver-patch dependency-group: all-dependencies ... Signed-off-by: dependabot[bot] --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 00f6ec3..5c81662 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,9 +122,9 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.32.8" +version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e499f9fc0407f50fe98af744ab44fa67d409f76b6772e1689ec8485eb0c0f66" +checksum = "9cf93e61f2dbc3e3c41234ca26a65e2c0b0975c52e0f069ab9893ebbede584d3" dependencies = [ "base58ck", "bech32", @@ -221,9 +221,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.61" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "shlex", @@ -1509,9 +1509,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.1" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", From 140d295eeca2a85e394f635f2a1745e57d54b65e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 May 2026 08:37:45 +0000 Subject: [PATCH 13/56] Bump zerofrom from 0.1.7 to 0.1.8 in the all-dependencies group Bumps the all-dependencies group with 1 update: [zerofrom](https://github.com/unicode-org/icu4x). Updates `zerofrom` from 0.1.7 to 0.1.8 - [Release notes](https://github.com/unicode-org/icu4x/releases) - [Changelog](https://github.com/unicode-org/icu4x/blob/main/CHANGELOG.md) - [Commits](https://github.com/unicode-org/icu4x/commits) --- updated-dependencies: - dependency-name: zerofrom dependency-version: 0.1.8 dependency-type: indirect update-type: version-update:semver-patch dependency-group: all-dependencies ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5c81662..4943b81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2141,9 +2141,9 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] From c956338bcd9ed7d9786c0ec40d757ccbb1145b16 Mon Sep 17 00:00:00 2001 From: daywalker90 <8257956+daywalker90@users.noreply.github.com> Date: Sat, 8 Nov 2025 13:41:11 +0100 Subject: [PATCH 14/56] add support for holdinvoices --- CHANGELOG.md | 6 + Cargo.lock | 476 +++++++++++++++++++++++++++++++-- Cargo.toml | 11 + README.md | 7 + build.rs | 6 + protos/hold.proto | 185 +++++++++++++ src/main.rs | 124 ++++++++- src/nwc.rs | 36 +-- src/nwc_hold.rs | 274 +++++++++++++++---- src/nwc_lookups.rs | 369 +++++++++++++++++++++++--- src/nwc_notifications.rs | 129 ++++++++- src/structs.rs | 5 + src/util.rs | 33 ++- tests/config.toml | 2 +- tests/setup.sh | 14 + tests/test_cln-nip47.py | 557 ++++++++++++++++++++++++++++++++++++++- tests/util.py | 23 +- tests/uv.lock | 142 +++++----- 18 files changed, 2192 insertions(+), 207 deletions(-) create mode 100644 build.rs create mode 100644 protos/hold.proto diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eb4680..91f2c5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Added +- holdinvoice methods: ``make_hold_invoice``, ``cancel_hold_invoice``, ``settle_hold_invoice`` +- holdinvoice notification: ``hold_invoice_accepted`` + ## [0.1.9] 2026-04-23 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 4943b81..55e0cde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,17 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-utility" version = "0.3.1" @@ -81,6 +92,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef49f5882e4b6afaac09ad239a4f8c70a24b8f2b0897edb1f706008efd109cf4" +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "base58ck" version = "0.1.0" @@ -282,10 +299,14 @@ dependencies = [ "log-panics", "nostr-sdk", "parking_lot", + "prost", "regex", "serde", "serde_json", "tokio", + "tonic", + "tonic-prost", + "tonic-prost-build", "uuid", ] @@ -386,12 +407,40 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foldhash" version = "0.1.5" @@ -555,6 +604,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "h2" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -566,9 +634,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -616,12 +684,96 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "libc", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + [[package]] name = "icu_collections" version = "2.1.1" @@ -737,7 +889,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -764,6 +916,15 @@ dependencies = [ "web-sys", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -772,9 +933,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.94" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" dependencies = [ "cfg-if", "futures-util", @@ -800,6 +961,12 @@ version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "litemap" version = "0.8.2" @@ -862,6 +1029,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + [[package]] name = "negentropy" version = "0.5.0" @@ -1017,6 +1190,37 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf0d9e68100b3a7989b4901972f265cd542e560a3a8a724e1e20322f4d06ce9" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a990e22f43e84855daf260dded30524ef4a9021cc7541c26540500a50b624389" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -1071,6 +1275,79 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "pulldown-cmark", + "pulldown-cmark-to-cmark", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" +dependencies = [ + "prost", +] + +[[package]] +name = "pulldown-cmark" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c3a14896dfa883796f1cb410461aef38810ea05f2b2c33c5aded3649095fdad" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] +name = "pulldown-cmark-to-cmark" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50793def1b900256624a709439404384204a5dc3a6ec580281bfaac35e882e90" +dependencies = [ + "pulldown-cmark", +] + [[package]] name = "quote" version = "1.0.45" @@ -1105,9 +1382,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -1203,12 +1480,26 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + [[package]] name = "rustls" version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ + "log", "once_cell", "ring", "rustls-pki-types", @@ -1422,6 +1713,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + [[package]] name = "synstructure" version = "0.13.2" @@ -1433,6 +1730,19 @@ dependencies = [ "syn", ] +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -1595,6 +1905,105 @@ dependencies = [ "tokio", ] +[[package]] +name = "tonic" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec" +dependencies = [ + "async-trait", + "base64", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "socket2", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1882ac3bf5ef12877d7ed57aad87e75154c11931c2ba7e6cde5e22d63522c734" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tonic-prost" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55376a0bbaa4975a3f10d009ad763d8f4108f067c7c2e74f3001fb49778d309" +dependencies = [ + "bytes", + "prost", + "tonic", +] + +[[package]] +name = "tonic-prost-build" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3144df636917574672e93d0f56d7edec49f90305749c668df5101751bb8f95a" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn", + "tempfile", + "tonic-build", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project-lite", + "slab", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.44" @@ -1657,6 +2066,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "tungstenite" version = "0.26.2" @@ -1668,7 +2083,7 @@ dependencies = [ "http", "httparse", "log", - "rand 0.9.2", + "rand 0.9.4", "rustls", "rustls-pki-types", "sha1", @@ -1682,6 +2097,12 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -1767,6 +2188,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1793,9 +2223,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.117" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" dependencies = [ "cfg-if", "once_cell", @@ -1806,9 +2236,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.67" +version = "0.4.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" +checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" dependencies = [ "js-sys", "wasm-bindgen", @@ -1816,9 +2246,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.117" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1826,9 +2256,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.117" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" dependencies = [ "bumpalo", "proc-macro2", @@ -1839,9 +2269,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.117" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" dependencies = [ "unicode-ident", ] @@ -1882,9 +2312,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.94" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a" +checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" dependencies = [ "js-sys", "wasm-bindgen", @@ -1896,14 +2326,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" dependencies = [ "rustls-pki-types", ] diff --git a/Cargo.toml b/Cargo.toml index 554256b..c5b1a1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,17 @@ hex = "0.4" regex = "1" +tonic = { version = "0.14", default-features = false, features = [ + "codegen", + "transport", + "tls-ring", +] } +prost = "0.14" +tonic-prost = "0.14" + +[build-dependencies] +tonic-prost-build = "0.14" + [profile.optimized] inherits = "release" diff --git a/README.md b/README.md index 6d78898..02fbb10 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,9 @@ For a private relay you can for example use [nostr-rs-relay](https://github.com/ * list all NWC configurations or just the one with ``label`` * ***label***: optional. The label the NWC was created with +## Holdinvoice support +For methods or notifications related to holdinvoices you need v0.3.2+ of [hold](https://github.com/BoltzExchange/hold) with enabled grpc (which is the default just make sure the port is free) + ## Supported NWC methods * ``pay_invoice`` * ``multi_pay_invoice`` @@ -137,10 +140,14 @@ For a private relay you can for example use [nostr-rs-relay](https://github.com/ * ``list_transactions`` * ``get_balance`` * ``get_info`` (no ``block_hash``) +* ``make_hold_invoice`` (requires [Holdinvoice support](#holdinvoice_support)) +* ``cancel_hold_invoice`` (requires [Holdinvoice support](#holdinvoice_support)) +* ``settle_hold_invoice`` (requires [Holdinvoice support](#holdinvoice_support)) ## Supported NWC notifications * ``payment_received`` * ``payment_sent`` +* ``hold_invoice_accepted`` (requires [Holdinvoice support](#holdinvoice_support)) ## Supported content encryption: * [NIP-04](https://github.com/nostr-protocol/nips/blob/master/04.md) diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..3e87891 --- /dev/null +++ b/build.rs @@ -0,0 +1,6 @@ +fn main() { + tonic_prost_build::configure() + .protoc_arg("--experimental_allow_proto3_optional") + .compile_protos(&["protos/hold.proto"], &["protos"]) + .unwrap_or_else(|e| panic!("Could not build protos: {e}")); +} diff --git a/protos/hold.proto b/protos/hold.proto new file mode 100644 index 0000000..e7ad240 --- /dev/null +++ b/protos/hold.proto @@ -0,0 +1,185 @@ +syntax = "proto3"; + +package hold; + +service Hold { + rpc GetInfo (GetInfoRequest) returns (GetInfoResponse); + + rpc Invoice (InvoiceRequest) returns (InvoiceResponse) {} + rpc Inject (InjectRequest) returns (InjectResponse) {} + + rpc List (ListRequest) returns (ListResponse) {} + + rpc Settle (SettleRequest) returns (SettleResponse) {} + rpc Cancel (CancelRequest) returns (CancelResponse) {} + + // Cleans cancelled invoices + rpc Clean (CleanRequest) returns (CleanResponse) {} + + rpc Track (TrackRequest) returns (stream TrackResponse) {} + rpc TrackAll (TrackAllRequest) returns (stream TrackAllResponse) {} + + rpc OnionMessages (stream OnionMessageResponse) returns (stream OnionMessage) {} +} + +enum HookAction { + Continue = 0; + Resolve = 1; +} + +message GetInfoRequest {} +message GetInfoResponse { + string version = 1; +} + +message Hop { + bytes public_key = 1; + uint64 short_channel_id = 2; + uint64 base_fee = 3; + uint64 ppm_fee = 4; + uint64 cltv_expiry_delta = 5; +} + +message RoutingHint { + repeated Hop hops = 1; +} + +message InvoiceRequest { + bytes payment_hash = 1; + uint64 amount_msat = 2; + + oneof description { + string memo = 3; + bytes hash = 4; + } + + optional uint64 expiry = 5; + optional uint64 min_final_cltv_expiry = 6; + repeated RoutingHint routing_hints = 7; +} +message InvoiceResponse { + string bolt11 = 1; +} + +message InjectRequest { + string invoice = 1; + optional uint64 min_cltv_expiry = 2; +} +message InjectResponse {} + +message ListRequest { + message Pagination { + // Inclusive + int64 index_start = 1; + uint64 limit = 2; + } + + oneof constraint { + bytes payment_hash = 1; + Pagination pagination = 2; + } +} + +enum InvoiceState { + UNPAID = 0; + ACCEPTED = 1; + PAID = 2; + CANCELLED = 3; +} + +message Htlc { + int64 id = 1; + InvoiceState state = 2; + string scid = 3; + uint64 channel_id = 4; + uint64 msat = 5; + uint64 created_at = 6; + optional uint64 cltv_expiry = 7; +} + +message Invoice { + int64 id = 1; + bytes payment_hash = 2; + optional bytes preimage = 3; + string invoice = 4; + InvoiceState state = 5; + uint64 created_at = 6; + optional uint64 settled_at = 8; + + repeated Htlc htlcs = 7; + + optional uint64 min_cltv_expiry = 9; +} + +message ListResponse { + repeated Invoice invoices = 1; +} + +message SettleRequest { + bytes payment_preimage = 1; +} +message SettleResponse {} + +message CancelRequest { + bytes payment_hash = 1; +} +message CancelResponse {} + +message CleanRequest { + // Clean everything older than age seconds + optional uint64 age = 1; +} +message CleanResponse { + uint64 cleaned = 1; +} + +message TrackRequest { + bytes payment_hash = 1; +} + +message TrackResponse { + InvoiceState state = 1; +} + +message TrackAllRequest { + repeated bytes payment_hashes = 1; +} + +message TrackAllResponse { + bytes payment_hash = 1; + string bolt11 = 2; + InvoiceState state = 3; +} + +message OnionMessage { + message ReplyBlindedPath { + message Hop { + optional bytes blinded_node_id = 1; + optional bytes encrypted_recipient_data = 2; + } + + optional bytes first_node_id = 1; + optional string first_scid = 2; + optional uint64 first_scid_dir = 3; + optional bytes first_path_key = 4; + repeated Hop hops = 5; + } + + message UnknownField { + uint64 number = 1; + bytes value = 2; + } + + uint64 id = 1; + optional bytes pathsecret = 2; + optional ReplyBlindedPath reply_blindedpath = 3; + optional bytes invoice_request = 4; + optional bytes invoice = 5; + optional bytes invoice_error = 6; + repeated UnknownField unknown_fields = 7; +} + +message OnionMessageResponse { + uint64 id = 1; + HookAction action = 2; +} diff --git a/src/main.rs b/src/main.rs index 778d840..957f9c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,8 @@ -use std::{path::Path, time::Duration}; +use std::{ + path::{Path, PathBuf}, + str::FromStr, + time::Duration, +}; use anyhow::anyhow; use cln_plugin::{ @@ -12,10 +16,17 @@ use nwc::run_nwc; use nwc_notifications::{payment_received_handler, payment_sent_handler}; use parse::read_startup_options; use rpc::{nwc_budget, nwc_create, nwc_list, nwc_revoke}; +use serde_json::json; use structs::PluginState; use tokio::time; +use tonic::transport::{Certificate, ClientTlsConfig, Endpoint, Identity}; use util::{load_nwc_store, update_nwc_store}; +use crate::{ + hold::{hold_client::HoldClient, InvoiceState, ListRequest}, + nwc_notifications::holdinvoice_accepted_handler, +}; + mod nwc; mod nwc_balance; mod nwc_hold; @@ -32,6 +43,9 @@ mod tasks; mod util; pub const STARTUP_DELAY: u64 = 1; +pub mod hold { + tonic::include_proto!("hold"); +} const OPT_RELAYS: StringArrayConfigOption = ConfigOption::new_str_arr_no_default( "nip47-relays", @@ -56,10 +70,17 @@ pub const WALLET_PAY_METHODS: [nip47::Method; 4] = [ nip47::Method::PayKeysend, nip47::Method::MultiPayKeysend, ]; +pub const WALLET_HOLD_METHODS: [nip47::Method; 3] = [ + nip47::Method::MakeHoldInvoice, + nip47::Method::CancelHoldInvoice, + nip47::Method::SettleHoldInvoice, +]; pub const WALLET_NOTIFICATIONS: [nip47::NotificationType; 2] = [ nip47::NotificationType::PaymentReceived, nip47::NotificationType::PaymentSent, ]; +pub const WALLET_HOLD_NOTIFICATIONS: [nip47::NotificationType; 1] = + [nip47::NotificationType::HoldInvoiceAccepted]; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { @@ -109,6 +130,16 @@ async fn main() -> Result<(), anyhow::Error> { }; let plugin = confplugin.start(state).await?; + match check_hold_support(plugin.clone()).await { + Ok(()) => { + log::info!("Hold support activated, loading pending invoices..."); + if let Err(e) = load_pending_hold_invoices(plugin.clone()).await { + log::error!("Error loading pending hold invoices: {e}"); + } + } + Err(e) => log::info!("Hold support not activated: {e}"), + } + { let mut rpc = plugin.state().rpc_lock.lock().await; @@ -167,3 +198,94 @@ async fn load_nwcs(plugin: Plugin, rpc: &mut ClnRpc) -> Result<(), } Ok(()) } + +async fn load_pending_hold_invoices(plugin: Plugin) -> Result<(), anyhow::Error> { + let mut hold_client = plugin.state().hold_client.lock().clone().unwrap(); + let invoices_request = ListRequest { constraint: None }; + let invoices = hold_client + .list(invoices_request) + .await? + .into_inner() + .invoices; + + for invoice in invoices { + if invoice.state() == InvoiceState::Accepted || invoice.state() == InvoiceState::Unpaid { + log::debug!( + "Starting holdinvoice accepted handler for {}", + hex::encode(&invoice.payment_hash) + ); + tokio::spawn(holdinvoice_accepted_handler( + plugin.clone(), + invoice.payment_hash, + )); + } + } + + Ok(()) +} + +async fn check_hold_support(plugin: Plugin) -> Result<(), anyhow::Error> { + let mut rpc = plugin.state().rpc_lock.lock().await; + let hold_grpc_host_response: serde_json::Value = rpc + .call_raw("listconfigs", &json!({"config": "hold-grpc-host"})) + .await?; + + let Some(hold_grpc_host_configs) = hold_grpc_host_response.get("configs") else { + return Err(anyhow!("Unsopprted listconfigs response!")); + }; + let Some(hold_grpc_host_config) = hold_grpc_host_configs.get("hold-grpc-host") else { + return Err(anyhow!("hold-grpc-host config not found")); + }; + let Some(hold_grpc_host_value) = hold_grpc_host_config.get("value_str") else { + return Err(anyhow!("hold-grpc-host config not a string")); + }; + let Some(hold_grpc_host) = hold_grpc_host_value.as_str() else { + return Err(anyhow!("hold-grpc-host config not convertable to string")); + }; + + let hold_grpc_port_response: serde_json::Value = rpc + .call_raw("listconfigs", &json!({"config": "hold-grpc-port"})) + .await?; + let Some(hold_grpc_port_configs) = hold_grpc_port_response.get("configs") else { + return Err(anyhow!("Unsopprted listconfigs response!")); + }; + let Some(hold_grpc_port_config) = hold_grpc_port_configs.get("hold-grpc-port") else { + return Err(anyhow!("hold-grpc-port config not found")); + }; + let Some(hold_grpc_port_value) = hold_grpc_port_config.get("value_int") else { + return Err(anyhow!("hold-grpc-port config not a number")); + }; + let hold_grpc_port = if let Some(hgh) = hold_grpc_port_value.as_u64() { + u16::try_from(hgh)? + } else { + return Err(anyhow!("hold-grpc-port config not convertable to integer")); + }; + + let cert_dir = PathBuf::from_str(&plugin.configuration().lightning_dir)?.join("hold"); + + log::debug!( + "Searching {} for hold plugin certs", + cert_dir.to_str().unwrap() + ); + + let ca_cert = tokio::fs::read(cert_dir.join("ca.pem")).await?; + let client_cert = tokio::fs::read(cert_dir.join("client.pem")).await?; + let client_key = tokio::fs::read(cert_dir.join("client-key.pem")).await?; + + let identity = Identity::from_pem(client_cert, client_key); + + let ca = Certificate::from_pem(ca_cert); + + let tls_config = ClientTlsConfig::new() + .ca_certificate(ca) + .identity(identity) + .domain_name("hold"); + + let hold_channel = Endpoint::from_shared(format!("https://{hold_grpc_host}:{hold_grpc_port}"))? + .tls_config(tls_config)? + .keep_alive_while_idle(true) + .connect_lazy(); + *plugin.state().hold_client.lock() = Some(HoldClient::new(hold_channel)); + + Ok(()) +} diff --git a/src/nwc.rs b/src/nwc.rs index 448334b..247aedc 100644 --- a/src/nwc.rs +++ b/src/nwc.rs @@ -55,29 +55,29 @@ pub async fn run_nwc( ); let client_pubkey = Keys::new(nwc_store.uri.secret.clone()).public_key(); - let client = Client::new(wallet_keys.clone()); + let nostr_client = Client::new(wallet_keys.clone()); log::debug!("relay_count:{}", nwc_store.uri.relays.len()); for relay in &nwc_store.uri.relays { log::debug!("Adding relay: {relay}"); - client.add_relay(relay).await?; + nostr_client.add_relay(relay).await?; } if nwc_store.interval_config.is_some() { start_nwc_budget_job(&plugin, label.clone()); } - let client_clone = client.clone(); + let nostr_client_clone = nostr_client.clone(); let plugin_clone = plugin.clone(); let label_clone = label.clone(); tokio::spawn(async move { loop { - client_clone.connect().await; - client_clone + nostr_client_clone.connect().await; + nostr_client_clone .wait_for_connection(Duration::from_secs(30)) .await; - let relays = client_clone.relays().await; + let relays = nostr_client_clone.relays().await; if relays.is_empty() { log::info!("No more relays left, we probably shut down. Exiting..."); break; @@ -98,14 +98,14 @@ pub async fn run_nwc( if let Err(e) = send_nwc_info_event( plugin_clone.clone(), - client_clone.clone(), + nostr_client_clone.clone(), method_capabilities.clone(), wallet_keys.clone(), ) .await { log::warn!("{e}"); - client_clone.disconnect().await; + nostr_client_clone.disconnect().await; time::sleep(Duration::from_secs(5)).await; continue; } @@ -115,15 +115,15 @@ pub async fn run_nwc( .author(client_pubkey) .since(Timestamp::now() - STARTUP_DELAY - 1); - if let Err(e) = client_clone.subscribe(filter, None).await { + if let Err(e) = nostr_client_clone.subscribe(filter, None).await { log::warn!("Could not subscribe to nwc events! {e}"); - client_clone.disconnect().await; + nostr_client_clone.disconnect().await; time::sleep(Duration::from_secs(5)).await; continue; } - let client_clone_handler = client_clone.clone(); - match client_clone + let client_clone_handler = nostr_client_clone.clone(); + match nostr_client_clone .handle_notifications(|notification| { let client_clone_handler = client_clone_handler.clone(); let plugin_clone = plugin_clone.clone(); @@ -152,7 +152,7 @@ pub async fn run_nwc( let mut locked_handles = plugin.state().handles.lock().await; locked_handles.insert( label.clone(), - (client, Keys::new(nwc_store.uri.secret).public_key()), + (nostr_client, Keys::new(nwc_store.uri.secret).public_key()), ); Ok(()) } @@ -227,7 +227,7 @@ pub fn stop_nwc_budget_job(plugin: &Plugin, label: &String) { async fn nwc_request_handler( notification: RelayPoolNotification, - client: client::Client, + nostr_client: client::Client, plugin: Plugin, label: String, wallet_keys: Keys, @@ -281,13 +281,13 @@ async fn nwc_request_handler( nip47::RequestParams::GetBalance => get_balance_response(plugin.clone(), &label).await, nip47::RequestParams::GetInfo => get_info_response(plugin.clone(), &label).await, nip47::RequestParams::MakeHoldInvoice(make_hold_invoice_request) => { - make_hold_invoice_response(plugin.clone(), make_hold_invoice_request, &label).await + make_hold_invoice_response(plugin.clone(), make_hold_invoice_request).await } nip47::RequestParams::CancelHoldInvoice(cancel_hold_invoice_request) => { - cancel_hold_invoice_response(plugin.clone(), cancel_hold_invoice_request, &label).await + cancel_hold_invoice_response(plugin.clone(), cancel_hold_invoice_request).await } nip47::RequestParams::SettleHoldInvoice(settle_hold_invoice_request) => { - settle_hold_invoice_response(plugin.clone(), settle_hold_invoice_request, &label).await + settle_hold_invoice_response(plugin.clone(), settle_hold_invoice_request).await } }; for (response, id) in responses { @@ -309,7 +309,7 @@ async fn nwc_request_handler( } }; - let send_result = match client.send_event(&response_event).await { + let send_result = match nostr_client.send_event(&response_event).await { Ok(o) => o, Err(e) => { log::warn!("Error sending response event! {e}"); diff --git a/src/nwc_hold.rs b/src/nwc_hold.rs index e048d0c..a706bde 100644 --- a/src/nwc_hold.rs +++ b/src/nwc_hold.rs @@ -1,58 +1,242 @@ -use cln_plugin::Plugin; -use nostr_sdk::nips::nip47; +use std::str::FromStr; -use crate::structs::PluginState; +use cln_plugin::Plugin; +use cln_rpc::primitives::Sha256; +use nostr_sdk::{nips::nip47, Timestamp}; + +use crate::{ + hold::{invoice_request::Description, CancelRequest, InvoiceRequest, SettleRequest}, + nwc_notifications::holdinvoice_accepted_handler, + structs::PluginState, +}; pub async fn make_hold_invoice_response( - _plugin: Plugin, - _params: nip47::MakeHoldInvoiceRequest, - _label: &str, + plugin: Plugin, + params: nip47::MakeHoldInvoiceRequest, ) -> Vec<(nip47::Response, Option)> { - vec![( - nip47::Response { - result_type: nip47::Method::MakeHoldInvoice, - error: Some(nip47::NIP47Error { - code: nip47::ErrorCode::NotImplemented, - message: "Not implemented".to_owned(), - }), - result: None, - }, - None, - )] + vec![match make_hold_invoice(plugin, params).await { + Ok(o) => ( + nip47::Response { + result_type: nip47::Method::MakeHoldInvoice, + error: None, + result: Some(nip47::ResponseResult::MakeHoldInvoice(o)), + }, + None, + ), + Err(e) => ( + nip47::Response { + result_type: nip47::Method::MakeHoldInvoice, + error: Some(e), + result: None, + }, + None, + ), + }] +} + +async fn make_hold_invoice( + plugin: Plugin, + params: nip47::MakeHoldInvoiceRequest, +) -> Result { + let Some(mut hold_client) = plugin.state().hold_client.lock().clone() else { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::NotImplemented, + message: "No hold plugin found".to_owned(), + }); + }; + + let description: Option = if let Some(d_hash) = ¶ms.description_hash { + if let Some(description) = ¶ms.description { + let my_description_hash = Sha256::const_hash(description.as_bytes()); + let description_hash = Sha256::from_str(d_hash).map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + if my_description_hash != description_hash { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "description_hash not matching description".to_owned(), + }); + } + } + + let desc_hash_bytes = match hex::decode(d_hash) { + Ok(p) => p, + Err(_e) => { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Could not convert description hash to bytes".to_owned(), + }) + } + }; + Some(Description::Hash(desc_hash_bytes)) + } else { + params + .description + .as_ref() + .map(|desc| Description::Memo(desc.clone())) + }; + + let payment_hash = match hex::decode(¶ms.payment_hash) { + Ok(p) => p, + Err(_e) => { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Invalid payment hash".to_owned(), + }) + } + }; + + let expiry = params.expiry.unwrap_or(60 * 60); + + let holdinvoice_request = InvoiceRequest { + payment_hash: payment_hash.clone(), + amount_msat: params.amount, + expiry: Some(expiry), + min_final_cltv_expiry: params.cltv_expiry_delta.map(u64::from), + routing_hints: Vec::new(), + description, + }; + + let holdinvoice = hold_client + .invoice(holdinvoice_request) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: format!("Error creating hold invoice: {e}"), + })? + .into_inner(); + + let response = nip47::MakeHoldInvoiceResponse { + invoice: Some(holdinvoice.bolt11), + transaction_type: nip47::TransactionType::Incoming, + description: params.description, + description_hash: params.description_hash, + amount: params.amount, + created_at: Timestamp::now(), + expires_at: Timestamp::now() + expiry, + metadata: None, + payment_hash: params.payment_hash, + }; + + tokio::spawn(holdinvoice_accepted_handler(plugin, payment_hash)); + Ok(response) } pub async fn cancel_hold_invoice_response( - _plugin: Plugin, - _params: nip47::CancelHoldInvoiceRequest, - _label: &str, + plugin: Plugin, + params: nip47::CancelHoldInvoiceRequest, ) -> Vec<(nip47::Response, Option)> { - vec![( - nip47::Response { - result_type: nip47::Method::MakeHoldInvoice, - error: Some(nip47::NIP47Error { - code: nip47::ErrorCode::NotImplemented, - message: "Not implemented".to_owned(), - }), - result: None, - }, - None, - )] + vec![match cancel_hold_invoice(plugin, params).await { + Ok(o) => ( + nip47::Response { + result_type: nip47::Method::CancelHoldInvoice, + error: None, + result: Some(nip47::ResponseResult::CancelHoldInvoice(o)), + }, + None, + ), + Err(e) => ( + nip47::Response { + result_type: nip47::Method::CancelHoldInvoice, + error: Some(e), + result: None, + }, + None, + ), + }] +} + +async fn cancel_hold_invoice( + plugin: Plugin, + params: nip47::CancelHoldInvoiceRequest, +) -> Result { + let Some(mut hold_client) = plugin.state().hold_client.lock().clone() else { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::NotImplemented, + message: "No hold plugin found".to_owned(), + }); + }; + + let payment_hash = match hex::decode(¶ms.payment_hash) { + Ok(p) => p, + Err(_e) => { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Invalid payment hash".to_owned(), + }) + } + }; + + let hold_cancel_request = CancelRequest { payment_hash }; + + let hold_cancel_response = hold_client.cancel(hold_cancel_request).await; + + match hold_cancel_response { + Ok(_o) => Ok(nip47::CancelHoldInvoiceResponse {}), + Err(e) => Err(nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + }), + } } pub async fn settle_hold_invoice_response( - _plugin: Plugin, - _params: nip47::SettleHoldInvoiceRequest, - _label: &str, + plugin: Plugin, + params: nip47::SettleHoldInvoiceRequest, ) -> Vec<(nip47::Response, Option)> { - vec![( - nip47::Response { - result_type: nip47::Method::MakeHoldInvoice, - error: Some(nip47::NIP47Error { - code: nip47::ErrorCode::NotImplemented, - message: "Not implemented".to_owned(), - }), - result: None, - }, - None, - )] + vec![match settle_hold_invoice(plugin, params).await { + Ok(o) => ( + nip47::Response { + result_type: nip47::Method::SettleHoldInvoice, + error: None, + result: Some(nip47::ResponseResult::SettleHoldInvoice(o)), + }, + None, + ), + Err(e) => ( + nip47::Response { + result_type: nip47::Method::SettleHoldInvoice, + error: Some(e), + result: None, + }, + None, + ), + }] +} + +async fn settle_hold_invoice( + plugin: Plugin, + params: nip47::SettleHoldInvoiceRequest, +) -> Result { + let Some(mut hold_client) = plugin.state().hold_client.lock().clone() else { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::NotImplemented, + message: "No hold plugin found".to_owned(), + }); + }; + + let preimage = match hex::decode(¶ms.preimage) { + Ok(p) => p, + Err(_e) => { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Invalid preimage".to_owned(), + }) + } + }; + + let hold_settle_request = SettleRequest { + payment_preimage: preimage, + }; + + let hold_settle_response = hold_client.settle(hold_settle_request).await; + + match hold_settle_response { + Ok(_o) => Ok(nip47::SettleHoldInvoiceResponse {}), + Err(e) => Err(nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + }), + } } diff --git a/src/nwc_lookups.rs b/src/nwc_lookups.rs index 33bea1f..b15a54b 100644 --- a/src/nwc_lookups.rs +++ b/src/nwc_lookups.rs @@ -5,6 +5,7 @@ use cln_rpc::{ model::{ requests::{DecodeRequest, ListinvoicesRequest, ListpaysRequest}, responses::{ + DecodeType, ListinvoicesInvoices, ListinvoicesInvoicesStatus, ListpaysPays, @@ -16,7 +17,10 @@ use cln_rpc::{ }; use nostr_sdk::{nips::nip47, Timestamp}; -use crate::structs::{PluginState, NOT_INV_ERR}; +use crate::{ + hold::{self, list_request::Constraint, ListRequest}, + structs::{PluginState, NOT_INV_ERR}, +}; pub async fn lookup_invoice_response( plugin: Plugin, @@ -58,7 +62,7 @@ async fn lookup_invoice( let invoice = if params.payment_hash.is_some() && params.invoice.is_some() { None } else { - params.invoice + params.invoice.clone() }; let invoices = rpc @@ -81,47 +85,234 @@ async fn lookup_invoice( if invoices.len() == 1 { let invoice_response = invoices.into_iter().next().unwrap(); - make_lookup_response_from_listinvoices(&mut rpc, invoice_response).await + return make_lookup_response_from_listinvoices(&mut rpc, invoice_response).await; + } + + let payment_hash_hash = if let Some(hash) = ¶ms.payment_hash { + if let Ok(res) = Sha256::from_str(hash) { + Some(res) + } else { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: "Could not convert payment hash".to_owned(), + }); + } } else { - let payment_hash_hash = if let Some(hash) = params.payment_hash { - if let Ok(res) = Sha256::from_str(&hash) { - Some(res) - } else { + None + }; + + let pays = rpc + .call_typed(&ListpaysRequest { + bolt11: invoice, + index: None, + limit: None, + payment_hash: payment_hash_hash, + start: None, + status: None, + }) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })? + .pays; + + if pays.len() == 1 { + let list_pay = pays.into_iter().next().unwrap(); + + return make_lookup_response_from_listpays(&mut rpc, list_pay).await; + } + + let holdinvoice_support = plugin.state().hold_client.lock().is_some(); + + if holdinvoice_support { + let mut hold_client = plugin.state().hold_client.lock().clone().unwrap(); + + let (hold_invoice, invoice_decoded) = if let Some(ph) = ¶ms.payment_hash { + let payment_hash_hash = match hex::decode(ph) { + Ok(p) => p, + Err(_e) => { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Invalid payment hash".to_owned(), + }) + } + }; + let list_request = ListRequest { + constraint: Some(Constraint::PaymentHash(payment_hash_hash)), + }; + let hold_lookup = hold_client + .list(list_request) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: format!("Could not fetch hold invoice: {e}"), + })? + .into_inner(); + + if hold_lookup.invoices.len() != 1 { return Err(nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: "Could not convert payment hash".to_owned(), + code: nip47::ErrorCode::Other, + message: "Transaction not found".to_owned(), }); } + + let hold_invoice = hold_lookup.invoices.into_iter().next().unwrap(); + let invoice_decoded = rpc + .call_typed(&DecodeRequest { + string: hold_invoice.invoice.clone(), + }) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + + (hold_invoice, invoice_decoded) + } else { + let invoice_decoded = rpc + .call_typed(&DecodeRequest { + string: params.invoice.unwrap(), + }) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + let ph = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded.invoice_payment_hash.unwrap(), + DecodeType::BOLT11_INVOICE => invoice_decoded.payment_hash.unwrap().to_string(), + _ => todo!(), + }; + let payment_hash_hash = match hex::decode(&ph) { + Ok(p) => p, + Err(_e) => { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Invalid payment hash in invoice".to_owned(), + }) + } + }; + + let list_request = ListRequest { + constraint: Some(Constraint::PaymentHash(payment_hash_hash)), + }; + + let hold_lookup = hold_client + .list(list_request) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: format!("Could not fetch hold invoice: {e}"), + })? + .into_inner(); + + if hold_lookup.invoices.len() != 1 { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Transaction not found".to_owned(), + }); + } + + let hold_invoice = hold_lookup.invoices.into_iter().next().unwrap(); + let invoice_decoded = rpc + .call_typed(&DecodeRequest { + string: hold_invoice.invoice.clone(), + }) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + (hold_invoice, invoice_decoded) + }; + let not_invoice_err = Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: NOT_INV_ERR.to_owned(), + }); + + let description = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded.offer_description, + DecodeType::BOLT11_INVOICE => invoice_decoded.description, + _ => return not_invoice_err, + }; + let description_hash = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => None, + DecodeType::BOLT11_INVOICE => invoice_decoded.description_hash.map(|h| h.to_string()), + _ => return not_invoice_err, + }; + + let created_at = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => { + Timestamp::from_secs(invoice_decoded.invoice_created_at.unwrap()) + } + DecodeType::BOLT11_INVOICE => Timestamp::from_secs(invoice_decoded.created_at.unwrap()), + _ => return not_invoice_err, + }; + + let amount = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded.invoice_amount_msat.unwrap().msat(), + DecodeType::BOLT11_INVOICE => { + if let Some(amt) = invoice_decoded.amount_msat { + amt.msat() + } else { + // amount: `any` but have to put a value... + 0 + } + } + _ => return not_invoice_err, + }; + + let expires_at = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded + .invoice_relative_expiry + .map(|e_at| created_at + Timestamp::from_secs(u64::from(e_at))), + DecodeType::BOLT11_INVOICE => invoice_decoded + .expiry + .map(|e_at| created_at + Timestamp::from_secs(e_at)), + _ => return not_invoice_err, + }; + + let state = match hold_invoice.state() { + hold::InvoiceState::Unpaid => nip47::TransactionState::Pending, + hold::InvoiceState::Accepted => nip47::TransactionState::Pending, // TODO ACCEPTED STATE + hold::InvoiceState::Paid => nip47::TransactionState::Settled, + hold::InvoiceState::Cancelled => nip47::TransactionState::Expired, + }; + + let settled_at = if hold_invoice.settled_at() != 0 { + Some(Timestamp::from_secs(hold_invoice.settled_at())) } else { None }; - let pays = rpc - .call_typed(&ListpaysRequest { - bolt11: invoice, - index: None, - limit: None, - payment_hash: payment_hash_hash, - start: None, - status: None, - }) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - })? - .pays; + let preimage = if hold_invoice.state() == hold::InvoiceState::Paid { + Some(hex::encode(hold_invoice.preimage())) + } else { + None + }; - if pays.len() != 1 { - return Err(nip47::NIP47Error { - code: nip47::ErrorCode::NotFound, - message: "Transaction not found".to_owned(), - }); - } - let list_pay = pays.into_iter().next().unwrap(); - - make_lookup_response_from_listpays(&mut rpc, list_pay).await + return Ok(nip47::LookupInvoiceResponse { + transaction_type: Some(nip47::TransactionType::Incoming), + invoice: Some(hold_invoice.invoice.clone()), + description, + description_hash, + preimage, + payment_hash: hex::encode(hold_invoice.payment_hash), + amount, + fees_paid: 0, + created_at, + expires_at, + settled_at, + metadata: None, + state: Some(state), + }); } + + Err(nip47::NIP47Error { + code: nip47::ErrorCode::NotFound, + message: "Transaction not found".to_owned(), + }) } pub async fn list_transactions_response( @@ -193,6 +384,118 @@ async fn list_transactions( Err(_e) => (), } } + + let holdinvoice_support = plugin.state().hold_client.lock().is_some(); + + if holdinvoice_support { + let mut hold_client = plugin.state().hold_client.lock().clone().unwrap(); + + let lookup_request = ListRequest { constraint: None }; + + let hold_lookup = hold_client + .list(lookup_request) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: format!("Could not fetch hold invoices: {e}"), + })? + .into_inner(); + + for hold_invoice in hold_lookup.invoices { + let invoice_decoded = rpc + .call_typed(&DecodeRequest { + string: hold_invoice.invoice.clone(), + }) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + + let description = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded.offer_description, + DecodeType::BOLT11_INVOICE => invoice_decoded.description, + _ => continue, + }; + let description_hash = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => None, + DecodeType::BOLT11_INVOICE => { + invoice_decoded.description_hash.map(|h| h.to_string()) + } + _ => continue, + }; + + let created_at = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => { + Timestamp::from_secs(invoice_decoded.invoice_created_at.unwrap()) + } + DecodeType::BOLT11_INVOICE => { + Timestamp::from_secs(invoice_decoded.created_at.unwrap()) + } + _ => continue, + }; + + let amount = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => { + invoice_decoded.invoice_amount_msat.unwrap().msat() + } + DecodeType::BOLT11_INVOICE => { + if let Some(amt) = invoice_decoded.amount_msat { + amt.msat() + } else { + // amount: `any` but have to put a value... + 0 + } + } + _ => continue, + }; + + let expires_at = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded + .invoice_relative_expiry + .map(|e_at| created_at + Timestamp::from_secs(u64::from(e_at))), + DecodeType::BOLT11_INVOICE => invoice_decoded + .expiry + .map(|e_at| created_at + Timestamp::from_secs(e_at)), + _ => continue, + }; + + let state = match hold_invoice.state() { + hold::InvoiceState::Unpaid => nip47::TransactionState::Pending, + hold::InvoiceState::Accepted => nip47::TransactionState::Pending, // TODO: ACCEPTED STATE + hold::InvoiceState::Paid => nip47::TransactionState::Settled, + hold::InvoiceState::Cancelled => nip47::TransactionState::Expired, + }; + + let settled_at = if hold_invoice.settled_at() != 0 { + Some(Timestamp::from_secs(hold_invoice.settled_at())) + } else { + None + }; + + let preimage = if hold_invoice.state() == hold::InvoiceState::Paid { + Some(hex::encode(hold_invoice.preimage())) + } else { + None + }; + + transactions.push(nip47::LookupInvoiceResponse { + transaction_type: Some(nip47::TransactionType::Incoming), + invoice: Some(hold_invoice.invoice.clone()), + description, + description_hash, + preimage, + payment_hash: hex::encode(hold_invoice.payment_hash), + amount, + fees_paid: 0, + created_at, + expires_at, + settled_at, + metadata: None, + state: Some(state), + }); + } + } } if query_payments { diff --git a/src/nwc_notifications.rs b/src/nwc_notifications.rs index 8f02b6a..e86afaf 100644 --- a/src/nwc_notifications.rs +++ b/src/nwc_notifications.rs @@ -4,7 +4,7 @@ use anyhow::anyhow; use cln_plugin::Plugin; use cln_rpc::{ model::{ - requests::{DecodeRequest, ListinvoicesRequest, ListpaysRequest}, + requests::{DecodeRequest, ListinvoicesRequest, ListpaysRequest, ListpeerchannelsRequest}, responses::{ DecodeResponse, ListinvoicesInvoices, @@ -24,6 +24,7 @@ use nostr_sdk::{ }; use crate::{ + hold::{list_request::Constraint, InvoiceState, ListRequest, TrackRequest}, structs::{PluginState, NOT_INV_ERR}, OPT_NOTIFICATIONS, }; @@ -380,3 +381,129 @@ async fn send_notification( Ok(()) } + +pub async fn holdinvoice_accepted_handler( + plugin: Plugin, + payment_hash: Vec, +) -> Result<(), anyhow::Error> { + let mut hold_client = plugin.state().hold_client.lock().clone().unwrap(); + + let track_request = TrackRequest { + payment_hash: payment_hash.clone(), + }; + let mut track_stream = hold_client.track(track_request).await?.into_inner(); + + while let Some(response) = track_stream.message().await? { + log::debug!("Invoice status: {}", response.state().as_str_name()); + if response.state() == InvoiceState::Accepted { + break; + } + } + + let list_request = ListRequest { + constraint: Some(Constraint::PaymentHash(payment_hash.clone())), + }; + + let hold_lookup = hold_client.list(list_request).await?.into_inner(); + + if hold_lookup.invoices.len() != 1 { + return Err(anyhow!("hold plugin did not return exactly one invoice")); + } + + let hold_invoice = hold_lookup.invoices.first().unwrap(); + + let mut rpc = plugin.state().rpc_lock.lock().await; + + let invoice_decoded = rpc + .call_typed(&DecodeRequest { + string: hold_invoice.invoice.clone(), + }) + .await?; + + let amount = match invoice_decoded.item_type { + cln_rpc::model::responses::DecodeType::BOLT12_INVOICE => { + invoice_decoded.invoice_amount_msat.unwrap().msat() + } + cln_rpc::model::responses::DecodeType::BOLT11_INVOICE => { + if let Some(amt) = invoice_decoded.amount_msat { + amt.msat() + } else { + // amount: `any` but have to put a value... + 0 + } + } + _ => return Err(anyhow!("hold plugin did not return an invoice string")), + }; + + let created_at = match invoice_decoded.item_type { + cln_rpc::model::responses::DecodeType::BOLT12_INVOICE => { + Timestamp::from_secs(invoice_decoded.invoice_created_at.unwrap()) + } + cln_rpc::model::responses::DecodeType::BOLT11_INVOICE => { + Timestamp::from_secs(invoice_decoded.created_at.unwrap()) + } + _ => return Err(anyhow!("hold plugin did not return an invoice string")), + }; + + let expires_at = match invoice_decoded.item_type { + cln_rpc::model::responses::DecodeType::BOLT12_INVOICE => { + created_at + + Timestamp::from_secs(u64::from(invoice_decoded.invoice_relative_expiry.unwrap())) + } + cln_rpc::model::responses::DecodeType::BOLT11_INVOICE => { + created_at + Timestamp::from_secs(invoice_decoded.expiry.unwrap()) + } + _ => return Err(anyhow!("hold plugin did not return an invoice string")), + }; + + let list_peer_channels = rpc + .call_typed(&ListpeerchannelsRequest { + id: None, + short_channel_id: None, + }) + .await? + .channels; + + let payment_hash_hash = Sha256::from_str(&hex::encode(payment_hash))?; + + let mut lowest_htlc_expiry = 0; + + for peer in list_peer_channels { + if let Some(htlcs) = peer.htlcs { + for htlc in htlcs { + if htlc.payment_hash != payment_hash_hash { + continue; + } + if htlc.expiry < lowest_htlc_expiry { + lowest_htlc_expiry = htlc.expiry; + } + } + } + } + + let clients = plugin.state().handles.lock().await; + + let content = nip47::Notification { + notification_type: nip47::NotificationType::HoldInvoiceAccepted, + notification: nip47::NotificationResult::HoldInvoiceAccepted( + nip47::HoldInvoiceAcceptedNotification { + transaction_type: nip47::TransactionType::Incoming, + invoice: hold_invoice.invoice.clone(), + description: None, + description_hash: None, + payment_hash: hex::encode(&hold_invoice.payment_hash), + amount, + created_at, + expires_at, + settle_deadline: lowest_htlc_expiry, + metadata: None, + }, + ), + }; + let notification = serde_json::to_string(&content).unwrap(); + + for (client, client_pubkey) in clients.values() { + send_notification(¬ification, client, client_pubkey).await?; + } + Ok(()) +} diff --git a/src/structs.rs b/src/structs.rs index c96db5a..8964bfd 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -5,6 +5,9 @@ use nostr_sdk::{client, nips::nip47, nostr}; use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use tokio::sync::oneshot; +use tonic::transport::Channel; + +use crate::hold::hold_client::HoldClient; pub const NOT_INV_ERR: &str = "Not an invoice or invalid invoice"; @@ -14,6 +17,7 @@ pub struct PluginState { pub handles: Arc>>, pub rpc_lock: Arc>, pub budget_jobs: Arc>>>, + pub hold_client: Arc>>>, } impl PluginState { pub async fn new(path: PathBuf) -> Result { @@ -22,6 +26,7 @@ impl PluginState { handles: Arc::new(tokio::sync::Mutex::new(HashMap::new())), rpc_lock: Arc::new(tokio::sync::Mutex::new(ClnRpc::new(path).await?)), budget_jobs: Arc::new(Mutex::new(HashMap::new())), + hold_client: Arc::new(Mutex::new(None)), }) } } diff --git a/src/util.rs b/src/util.rs index 36cc484..6956d9e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -10,6 +10,8 @@ use crate::{ structs::{NwcStore, PluginState}, OPT_NOTIFICATIONS, PLUGIN_NAME, + WALLET_HOLD_METHODS, + WALLET_HOLD_NOTIFICATIONS, WALLET_NOTIFICATIONS, WALLET_PAY_METHODS, WALLET_READ_METHODS, @@ -124,11 +126,22 @@ pub fn at_or_above_version(my_version: &str, min_version: &str) -> Result) -> (String, String) { + let holdinvoice_support = plugin.state().hold_client.lock().is_some(); + let mut methods = WALLET_READ_METHODS.map(|m| m.to_string()).join(" "); if !is_read_only { methods.push(' '); methods.push_str(WALLET_PAY_METHODS.map(|m| m.to_string()).join(" ").as_str()); } + if holdinvoice_support { + methods.push(' '); + methods.push_str( + WALLET_HOLD_METHODS + .map(|m| m.to_string()) + .join(" ") + .as_str(), + ); + } let mut notifications = String::new(); if plugin.option(&OPT_NOTIFICATIONS).unwrap() { @@ -138,23 +151,41 @@ pub fn build_capabilities(is_read_only: bool, plugin: &Plugin) -> ( .join(" ") .as_str(), ); + if holdinvoice_support { + notifications.push(' '); + notifications.push_str( + WALLET_HOLD_NOTIFICATIONS + .map(|m| m.to_string()) + .join(" ") + .as_str(), + ); + } } (methods, notifications) } -pub fn build_methods_vec(is_read_only: bool, _plugin: &Plugin) -> Vec { +pub fn build_methods_vec(is_read_only: bool, plugin: &Plugin) -> Vec { + let holdinvoice_support = plugin.state().hold_client.lock().is_some(); let mut methods = WALLET_READ_METHODS.to_vec(); if !is_read_only { methods.extend_from_slice(&WALLET_PAY_METHODS); } + if holdinvoice_support { + methods.extend_from_slice(&WALLET_HOLD_METHODS); + } methods } pub fn build_notifications_vec(plugin: &Plugin) -> Vec { + let holdinvoice_support = plugin.state().hold_client.lock().is_some(); + let mut notifications = Vec::new(); if plugin.option(&OPT_NOTIFICATIONS).unwrap() { notifications.extend_from_slice(&WALLET_NOTIFICATIONS.map(|m| m.to_string())); + if holdinvoice_support { + notifications.extend_from_slice(&WALLET_HOLD_NOTIFICATIONS.map(|m| m.to_string())); + } } notifications } diff --git a/tests/config.toml b/tests/config.toml index 062124b..3afabc9 100644 --- a/tests/config.toml +++ b/tests/config.toml @@ -9,4 +9,4 @@ reject_future_seconds = 1800 [limits] limit_scrapers = false [network] -address = "0.0.0.0" +address = "127.0.0.1" diff --git a/tests/setup.sh b/tests/setup.sh index 0fe9440..829a336 100755 --- a/tests/setup.sh +++ b/tests/setup.sh @@ -81,3 +81,17 @@ if [ -d "$proto_path" ]; then exit 1 fi fi + +hold_url="https://github.com/BoltzExchange/hold/releases/download/v0.3.3/hold-linux-amd64.tar.gz" + +if ! curl -L "$hold_url" -o "$script_dir/hold-linux-amd64.tar.gz"; then + echo "Error downloading the file from $hold_url" >&2 + exit 1 +fi + +if ! tar -xzvf "$script_dir/hold-linux-amd64.tar.gz" -C "$script_dir"; then + echo "Error extracting the contents of hold-linux-amd64.tar.gz" >&2 + exit 1 +fi + +mv "$script_dir/build/hold-linux-amd64" "$script_dir/hold" diff --git a/tests/test_cln-nip47.py b/tests/test_cln-nip47.py index daf256c..3642c96 100755 --- a/tests/test_cln-nip47.py +++ b/tests/test_cln-nip47.py @@ -4,13 +4,15 @@ import json import logging import time import asyncio +import secrets +import uuid from datetime import datetime, timedelta from typing import Any, Awaitable, Callable, Union import pytest from pyln.testing.fixtures import * # noqa: F403 from pyln.testing.utils import RpcError, wait_for, TIMEOUT -from util import generate_random_label, get_plugin # noqa: F401 +from util import generate_random_label, get_plugin, get_hold # noqa: F401 from nostr_sdk import ( Alphabet, @@ -75,7 +77,10 @@ async def fetch_event_responses( ) -> tuple[list[Event], Any]: events = [] response_filter = Filter().kind(Kind(event_kind)).pubkey(client_pubkey) - await client.subscribe(response_filter) + + id = uuid.uuid4().hex + LOGGER.info(f"Subscribing with id {id} to {response_filter}") + await client.subscribe_with_id(id, response_filter) handler = NotificationHandler(events, stop_after) task = asyncio.create_task(client.handle_notifications(handler)) @@ -1454,3 +1459,551 @@ async def test_budget_command(nostr_relay, node_factory, get_plugin): # noqa: F info_event.tags().find(TagKind.UNKNOWN("notifications")).content() == "payment_received payment_sent" ) + + +@pytest.mark.asyncio +async def test_hold_invoice( + node_factory, + executor, + get_plugin, # noqa: F811 + get_hold, # noqa: F811 + nostr_relay, +): + url = nostr_relay + l1, l2 = node_factory.line_graph( + 2, + wait_for_announce=True, + opts=[ + { + "log-level": "debug", + "may_reconnect": True, + }, + { + "log-level": "debug", + "plugin": get_plugin, + "important-plugin": get_hold, + "nip47-relays": url, + "may_reconnect": True, + }, + ], + ) + uri_res = l2.rpc.call("nip47-create", ["test1", 3010]) + uri_str = uri_res["uri"] + client_pubkey = PublicKey.parse(uri_res["clientkey_public"]) + LOGGER.info(uri_str) + uri = NostrWalletConnectUri.parse(uri_str) + + nwc = Nwc(uri) + + preimage = secrets.token_hex(32) + payment_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() + LOGGER.info(f"preimage: {preimage}") + LOGGER.info(f"payment_hash: {payment_hash}") + content = { + "method": "make_hold_invoice", + "params": { + "amount": 5000, + "payment_hash": payment_hash, + }, + } + content = json.dumps(content) + signer = NostrSigner.keys(Keys(uri.secret())) + encrypted_content = await signer.nip04_encrypt(uri.public_key(), content) + event = ( + await EventBuilder(Kind(23194), encrypted_content) + .tags([Tag.public_key(uri.public_key())]) + .sign(signer) + ) + client = Client(signer) + relay_url = RelayUrl.parse(url) + await client.add_relay(relay_url) + await client.connect() + + await fetch_info_event(client, uri) + + (responses1, _res) = await fetch_event_responses( + client, client_pubkey, 23195, client.send_event(event), 1 + ) + error_events = [] + success_events = [] + for event in responses1: + LOGGER.info(event) + content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = json.loads(content) + LOGGER.info(content) + if "result" in content and content["result"] is not None: + success_events.append(content) + if "error" in content and content["error"] is not None: + error_events.append(content) + + assert len(success_events) == 1 + assert len(error_events) == 0 + + assert success_events[0]["result_type"] == "make_hold_invoice" + assert success_events[0]["result"]["payment_hash"] == payment_hash + assert success_events[0]["result"]["type"] == "incoming" + assert success_events[0]["result"]["invoice"] is not None + invoice1 = success_events[0]["result"]["invoice"] + assert "description" not in success_events[0]["result"] + assert "description_hash" not in success_events[0]["result"] + assert success_events[0]["result"]["amount"] == 5000 + invoice1_created_at = pytest.approx(int(time.time()), abs=1) + assert success_events[0]["result"]["created_at"] == invoice1_created_at + invoice1_expires_at = pytest.approx(int(time.time()) + 3600, abs=1) + assert success_events[0]["result"]["expires_at"] == invoice1_expires_at + assert "metadata" not in success_events[0]["result"] + + lookup_hold = await nwc.lookup_invoice( + LookupInvoiceRequest( + payment_hash=payment_hash, + invoice=None, + ) + ) + assert lookup_hold.invoice == success_events[0]["result"]["invoice"] + assert lookup_hold.amount == 5000 + assert lookup_hold.description is None + assert lookup_hold.created_at.as_secs() == success_events[0]["result"]["created_at"] + assert lookup_hold.description_hash is None + assert lookup_hold.expires_at.as_secs() == success_events[0]["result"]["expires_at"] + assert lookup_hold.fees_paid == 0 + assert lookup_hold.metadata is None + assert lookup_hold.preimage is None + assert lookup_hold.payment_hash == payment_hash + assert lookup_hold.transaction_type.name == "INCOMING" + assert lookup_hold.state.name == "PENDING" + assert lookup_hold.settled_at is None + + (responses2, _res) = await fetch_event_responses( + client, + client_pubkey, + 23196, + lambda: executor.submit( + l1.rpc.call, "xpay", [success_events[0]["result"]["invoice"]] + ), + 1, + ) + hold_events = [] + for event in responses2: + LOGGER.info(event) + content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = json.loads(content) + LOGGER.info(content) + if content["notification_type"] == "hold_invoice_accepted": + hold_events.append(content) + assert content["notification"]["payment_hash"] == payment_hash + + lookup_hold = await nwc.lookup_invoice( + LookupInvoiceRequest( + payment_hash=None, + invoice=invoice1, + ) + ) + assert lookup_hold.invoice == invoice1 + assert lookup_hold.amount == 5000 + assert lookup_hold.description is None + assert lookup_hold.created_at.as_secs() == invoice1_created_at + assert lookup_hold.description_hash is None + assert lookup_hold.expires_at.as_secs() == invoice1_expires_at + assert lookup_hold.fees_paid == 0 + assert lookup_hold.metadata is None + assert lookup_hold.preimage is None + assert lookup_hold.payment_hash == payment_hash + assert lookup_hold.transaction_type.name == "INCOMING" + assert lookup_hold.state.name == "PENDING" # TODO ACCEPTED STATE + assert lookup_hold.settled_at is None + + content = { + "method": "settle_hold_invoice", + "params": { + "preimage": preimage, + }, + } + content = json.dumps(content) + encrypted_content = await signer.nip04_encrypt(uri.public_key(), content) + event = ( + await EventBuilder(Kind(23194), encrypted_content) + .tags([Tag.public_key(uri.public_key())]) + .sign(signer) + ) + + (responses3, _res) = await fetch_event_responses( + client, client_pubkey, 23195, client.send_event(event), 1 + ) + error_events = [] + success_events = [] + for event in responses3: + LOGGER.info(event) + content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = json.loads(content) + LOGGER.info(content) + if ( + "result" in content + and content["result"] is not None + and content["result_type"] == "settle_hold_invoice" + ): + success_events.append(content) + if "error" in content and content["error"] is not None: + error_events.append(content) + + assert len(success_events) == 1 + assert len(error_events) == 0 + for content in success_events: + assert content["result_type"] == "settle_hold_invoice" + lookup_hold = await nwc.lookup_invoice( + LookupInvoiceRequest( + payment_hash=None, + invoice=invoice1, + ) + ) + assert lookup_hold.invoice == invoice1 + assert lookup_hold.amount == 5000 + assert lookup_hold.description is None + assert lookup_hold.created_at.as_secs() == invoice1_created_at + assert lookup_hold.description_hash is None + assert lookup_hold.expires_at.as_secs() == invoice1_expires_at + assert lookup_hold.fees_paid == 0 + assert lookup_hold.metadata is None + assert lookup_hold.preimage == preimage + assert lookup_hold.payment_hash == payment_hash + assert lookup_hold.transaction_type.name == "INCOMING" + assert lookup_hold.state.name == "SETTLED" + assert lookup_hold.settled_at.as_secs() == pytest.approx( + int(time.time()), abs=1 + ) + + wait_for( + lambda: l1.rpc.call("listpays", {"payment_hash": payment_hash})["pays"][0][ + "status" + ] + == "complete" + ) + + preimage = secrets.token_hex(32) + payment_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() + content = { + "method": "make_hold_invoice", + "params": { + "amount": 5000, + "payment_hash": payment_hash, + "description": "cancel_hold", + "expiry": 1000, + "cltv_expiry_delta": 200, + }, + } + content = json.dumps(content) + signer = NostrSigner.keys(Keys(uri.secret())) + encrypted_content = await signer.nip04_encrypt(uri.public_key(), content) + event = ( + await EventBuilder(Kind(23194), encrypted_content) + .tags([Tag.public_key(uri.public_key())]) + .sign(signer) + ) + + (responses4, _res) = await fetch_event_responses( + client, client_pubkey, 23195, client.send_event(event), 1 + ) + error_events = [] + success_events = [] + for event in responses4: + LOGGER.info(event) + content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = json.loads(content) + LOGGER.info(content) + if ( + "result" in content + and content["result"] is not None + and content["result_type"] == "make_hold_invoice" + and content["result"]["payment_hash"] == payment_hash + ): + success_events.append(content) + if "error" in content and content["error"] is not None: + error_events.append(content) + + assert len(success_events) == 1 + assert len(error_events) == 0 + for content in success_events: + assert content["result_type"] == "make_hold_invoice" + assert content["result"]["payment_hash"] == payment_hash + invoice2 = content["result"]["invoice"] + invoice2_created_at = pytest.approx(int(time.time()), abs=1) + invoice2_expires_at = pytest.approx(int(time.time()) + 1000, abs=1) + + (responses5, _res) = await fetch_event_responses( + client, + client_pubkey, + 23196, + lambda: executor.submit( + l1.rpc.call, "xpay", [success_events[0]["result"]["invoice"]] + ), + 1, + ) + hold_events = [] + for event in responses5: + LOGGER.info(event) + content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = json.loads(content) + LOGGER.info(content) + if ( + content["notification_type"] == "hold_invoice_accepted" + and content["notification"]["payment_hash"] == payment_hash + ): + hold_events.append(content) + assert len(hold_events) == 1 + + content = { + "method": "cancel_hold_invoice", + "params": { + "payment_hash": payment_hash, + }, + } + content = json.dumps(content) + encrypted_content = await signer.nip04_encrypt(uri.public_key(), content) + event = ( + await EventBuilder(Kind(23194), encrypted_content) + .tags([Tag.public_key(uri.public_key())]) + .sign(signer) + ) + + (responses6, _res) = await fetch_event_responses( + client, + client_pubkey, + 23195, + client.send_event(event), + 1, + ) + error_events = [] + success_events = [] + for event in responses6: + LOGGER.info(event) + content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = json.loads(content) + LOGGER.info(content) + if ( + "result" in content + and content["result"] is not None + and content["result_type"] == "cancel_hold_invoice" + ): + success_events.append(content) + if "error" in content and content["error"] is not None: + error_events.append(content) + + assert len(success_events) == 1 + assert len(error_events) == 0 + for content in success_events: + assert content["result_type"] == "cancel_hold_invoice" + lookup_hold = await nwc.lookup_invoice( + LookupInvoiceRequest( + payment_hash=None, + invoice=invoice2, + ) + ) + assert lookup_hold.invoice == invoice2 + assert lookup_hold.amount == 5000 + assert lookup_hold.description == "cancel_hold" + assert lookup_hold.created_at.as_secs() == invoice2_created_at + assert lookup_hold.description_hash is None + assert lookup_hold.expires_at.as_secs() == invoice2_expires_at + assert lookup_hold.fees_paid == 0 + assert lookup_hold.metadata is None + assert lookup_hold.preimage is None + assert lookup_hold.payment_hash == payment_hash + assert lookup_hold.transaction_type.name == "INCOMING" + assert lookup_hold.state.name == "EXPIRED" + assert lookup_hold.settled_at is None + + invoice2_decoded = l1.rpc.call("decode", [invoice2]) + assert invoice2_decoded["min_final_cltv_expiry"] == 200 + + wait_for( + lambda: l1.rpc.call("listpays", {"payment_hash": payment_hash})["pays"][0][ + "status" + ] + == "failed" + ) + + nwc = Nwc(uri) + + invoice_lookup1 = await nwc.lookup_invoice( + LookupInvoiceRequest( + payment_hash=payment_hash, + invoice=None, + ) + ) + invoice_lookup2 = await nwc.lookup_invoice( + LookupInvoiceRequest( + payment_hash=None, + invoice=invoice2, + ) + ) + invoice_lookup3 = await nwc.lookup_invoice( + LookupInvoiceRequest( + payment_hash=payment_hash, + invoice=invoice2, + ) + ) + assert invoice_lookup1 == invoice_lookup2 + assert invoice_lookup1 == invoice_lookup3 + + invoice_lookup4 = await nwc.lookup_invoice( + LookupInvoiceRequest( + payment_hash=None, + invoice=invoice1, + ) + ) + + result = await nwc.list_transactions( + ListTransactionsRequest( + _from=None, + until=None, + limit=None, + offset=None, + unpaid=True, + transaction_type=None, + ) + ) + assert len(result) == 2 + assert result == [invoice_lookup1, invoice_lookup4] or result == [ + invoice_lookup4, + invoice_lookup1, + ] + + description3 = "test3" + description_hash3 = hashlib.sha256(description3.encode()).hexdigest() + preimage = secrets.token_hex(32) + payment_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() + content = { + "method": "make_hold_invoice", + "params": { + "amount": 5001, + "payment_hash": payment_hash, + "description": description3, + "description_hash": description_hash3, + }, + } + content = json.dumps(content) + encrypted_content = await signer.nip04_encrypt(uri.public_key(), content) + event = ( + await EventBuilder(Kind(23194), encrypted_content) + .tags([Tag.public_key(uri.public_key())]) + .sign(signer) + ) + await client.send_event(event) + + start_time = datetime.now() + while (datetime.now() - start_time) < timedelta(seconds=10): + time.sleep(1) + try: + await nwc.lookup_invoice( + LookupInvoiceRequest( + payment_hash=payment_hash, + invoice=None, + ) + ) + break + except Exception: + continue + + lookup_hold = await nwc.lookup_invoice( + LookupInvoiceRequest( + payment_hash=payment_hash, + invoice=None, + ) + ) + assert lookup_hold.amount == 5001 + assert lookup_hold.description is None + assert lookup_hold.description_hash == description_hash3 + assert lookup_hold.metadata is None + assert lookup_hold.preimage is None + assert lookup_hold.payment_hash == payment_hash + assert lookup_hold.transaction_type.name == "INCOMING" + assert lookup_hold.state.name == "PENDING" + assert lookup_hold.settled_at is None + + description4 = "test4" + description_hash4 = hashlib.sha256(description4.encode()).hexdigest() + preimage = secrets.token_hex(32) + payment_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() + content = { + "method": "make_hold_invoice", + "params": { + "amount": 5002, + "payment_hash": payment_hash, + "description_hash": description_hash4, + }, + } + content = json.dumps(content) + encrypted_content = await signer.nip04_encrypt(uri.public_key(), content) + event = ( + await EventBuilder(Kind(23194), encrypted_content) + .tags([Tag.public_key(uri.public_key())]) + .sign(signer) + ) + await client.send_event(event) + + start_time = datetime.now() + while (datetime.now() - start_time) < timedelta(seconds=10): + time.sleep(1) + try: + await nwc.lookup_invoice( + LookupInvoiceRequest( + payment_hash=payment_hash, + invoice=None, + ) + ) + break + except Exception: + continue + + lookup_hold = await nwc.lookup_invoice( + LookupInvoiceRequest( + payment_hash=payment_hash, + invoice=None, + ) + ) + assert lookup_hold.amount == 5002 + assert lookup_hold.description is None + assert lookup_hold.description_hash == description_hash4 + assert lookup_hold.metadata is None + assert lookup_hold.preimage is None + assert lookup_hold.payment_hash == payment_hash + assert lookup_hold.transaction_type.name == "INCOMING" + assert lookup_hold.state.name == "PENDING" + assert lookup_hold.settled_at is None + + info_event = await fetch_info_event(client, uri) + assert ( + info_event.content() + == "make_invoice lookup_invoice list_transactions get_balance get_info pay_invoice multi_pay_invoice pay_keysend multi_pay_keysend make_hold_invoice cancel_hold_invoice settle_hold_invoice notifications" + ) + assert ( + info_event.tags().find(TagKind.UNKNOWN("encryption")).content() + == "nip44_v2 nip04" + ) + assert ( + info_event.tags().find(TagKind.UNKNOWN("notifications")).content() + == "payment_received payment_sent hold_invoice_accepted" + ) + + l2.restart() + + l1.rpc.connect(l2.info["id"], "localhost", l2.port) + + (responses7, _res) = await fetch_event_responses( + client, + client_pubkey, + 23196, + lambda: executor.submit(l1.rpc.call, "xpay", [lookup_hold.invoice]), + 1, + ) + hold_events = [] + for event in responses7: + LOGGER.info(event) + content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = json.loads(content) + LOGGER.info(content) + if ( + content["notification_type"] == "hold_invoice_accepted" + and content["notification"]["payment_hash"] == payment_hash + ): + hold_events.append(content) + assert len(hold_events) == 1 diff --git a/tests/util.py b/tests/util.py index a3f69aa..393a970 100644 --- a/tests/util.py +++ b/tests/util.py @@ -1,8 +1,8 @@ -import logging import os import random import string from pathlib import Path +from hashlib import sha256 import pytest @@ -11,9 +11,11 @@ plugin_dir = Path(__file__).parent.parent.resolve() COMPILED_PATH = plugin_dir / "target" / RUST_PROFILE / "cln-nip47" DOWNLOAD_PATH = plugin_dir / "tests" / "cln-nip47" +DOWNLOAD_HOLD_PATH = plugin_dir / "tests" / "hold" + @pytest.fixture -def get_plugin(directory): +def get_plugin(): if COMPILED_PATH.is_file(): return COMPILED_PATH elif DOWNLOAD_PATH.is_file(): @@ -22,6 +24,14 @@ def get_plugin(directory): raise ValueError("No files were found.") +@pytest.fixture +def get_hold(): + if DOWNLOAD_HOLD_PATH.is_file(): + return DOWNLOAD_HOLD_PATH + else: + raise ValueError("No files were found.") + + def generate_random_label(): label_length = 8 random_label = "".join( @@ -34,15 +44,6 @@ def generate_random_number(): return random.randint(1, 20_000_000_000_000_00_000) -def pay_with_thread(rpc, bolt11): - LOGGER = logging.getLogger(__name__) - try: - rpc.dev_pay(bolt11, dev_use_shadow=False) - except Exception as e: - LOGGER.info(f"holdinvoice: Error paying payment hash:{e}") - pass - - def update_config_file_option(lightning_dir, option_name, option_value): with open(lightning_dir + "/config", "r") as file: lines = file.readlines() diff --git a/tests/uv.lock b/tests/uv.lock index 6c3395a..7baa4f7 100644 --- a/tests/uv.lock +++ b/tests/uv.lock @@ -451,62 +451,62 @@ wheels = [ [[package]] name = "cryptography" -version = "46.0.7" +version = "48.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/47/93/ac8f3d5ff04d54bc814e961a43ae5b0b146154c89c61b47bb07557679b18/cryptography-46.0.7.tar.gz", hash = "sha256:e4cfd68c5f3e0bfdad0d38e023239b96a2fe84146481852dffbcca442c245aa5", size = 750652, upload-time = "2026-04-08T01:57:54.692Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/5d/4a8f770695d73be252331e60e526291e3df0c9b27556a90a6b47bccca4c2/cryptography-46.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:ea42cbe97209df307fdc3b155f1b6fa2577c0defa8f1f7d3be7d31d189108ad4", size = 7179869, upload-time = "2026-04-08T01:56:17.157Z" }, - { url = "https://files.pythonhosted.org/packages/5f/45/6d80dc379b0bbc1f9d1e429f42e4cb9e1d319c7a8201beffd967c516ea01/cryptography-46.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b36a4695e29fe69215d75960b22577197aca3f7a25b9cf9d165dcfe9d80bc325", size = 4275492, upload-time = "2026-04-08T01:56:19.36Z" }, - { url = "https://files.pythonhosted.org/packages/4a/9a/1765afe9f572e239c3469f2cb429f3ba7b31878c893b246b4b2994ffe2fe/cryptography-46.0.7-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ad9ef796328c5e3c4ceed237a183f5d41d21150f972455a9d926593a1dcb308", size = 4426670, upload-time = "2026-04-08T01:56:21.415Z" }, - { url = "https://files.pythonhosted.org/packages/8f/3e/af9246aaf23cd4ee060699adab1e47ced3f5f7e7a8ffdd339f817b446462/cryptography-46.0.7-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:73510b83623e080a2c35c62c15298096e2a5dc8d51c3b4e1740211839d0dea77", size = 4280275, upload-time = "2026-04-08T01:56:23.539Z" }, - { url = "https://files.pythonhosted.org/packages/0f/54/6bbbfc5efe86f9d71041827b793c24811a017c6ac0fd12883e4caa86b8ed/cryptography-46.0.7-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cbd5fb06b62bd0721e1170273d3f4d5a277044c47ca27ee257025146c34cbdd1", size = 4928402, upload-time = "2026-04-08T01:56:25.624Z" }, - { url = "https://files.pythonhosted.org/packages/2d/cf/054b9d8220f81509939599c8bdbc0c408dbd2bdd41688616a20731371fe0/cryptography-46.0.7-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:420b1e4109cc95f0e5700eed79908cef9268265c773d3a66f7af1eef53d409ef", size = 4459985, upload-time = "2026-04-08T01:56:27.309Z" }, - { url = "https://files.pythonhosted.org/packages/f9/46/4e4e9c6040fb01c7467d47217d2f882daddeb8828f7df800cb806d8a2288/cryptography-46.0.7-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:24402210aa54baae71d99441d15bb5a1919c195398a87b563df84468160a65de", size = 3990652, upload-time = "2026-04-08T01:56:29.095Z" }, - { url = "https://files.pythonhosted.org/packages/36/5f/313586c3be5a2fbe87e4c9a254207b860155a8e1f3cca99f9910008e7d08/cryptography-46.0.7-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:8a469028a86f12eb7d2fe97162d0634026d92a21f3ae0ac87ed1c4a447886c83", size = 4279805, upload-time = "2026-04-08T01:56:30.928Z" }, - { url = "https://files.pythonhosted.org/packages/69/33/60dfc4595f334a2082749673386a4d05e4f0cf4df8248e63b2c3437585f2/cryptography-46.0.7-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9694078c5d44c157ef3162e3bf3946510b857df5a3955458381d1c7cfc143ddb", size = 4892883, upload-time = "2026-04-08T01:56:32.614Z" }, - { url = "https://files.pythonhosted.org/packages/c7/0b/333ddab4270c4f5b972f980adef4faa66951a4aaf646ca067af597f15563/cryptography-46.0.7-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:42a1e5f98abb6391717978baf9f90dc28a743b7d9be7f0751a6f56a75d14065b", size = 4459756, upload-time = "2026-04-08T01:56:34.306Z" }, - { url = "https://files.pythonhosted.org/packages/d2/14/633913398b43b75f1234834170947957c6b623d1701ffc7a9600da907e89/cryptography-46.0.7-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91bbcb08347344f810cbe49065914fe048949648f6bd5c2519f34619142bbe85", size = 4410244, upload-time = "2026-04-08T01:56:35.977Z" }, - { url = "https://files.pythonhosted.org/packages/10/f2/19ceb3b3dc14009373432af0c13f46aa08e3ce334ec6eff13492e1812ccd/cryptography-46.0.7-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5d1c02a14ceb9148cc7816249f64f623fbfee39e8c03b3650d842ad3f34d637e", size = 4674868, upload-time = "2026-04-08T01:56:38.034Z" }, - { url = "https://files.pythonhosted.org/packages/1a/bb/a5c213c19ee94b15dfccc48f363738633a493812687f5567addbcbba9f6f/cryptography-46.0.7-cp311-abi3-win32.whl", hash = "sha256:d23c8ca48e44ee015cd0a54aeccdf9f09004eba9fc96f38c911011d9ff1bd457", size = 3026504, upload-time = "2026-04-08T01:56:39.666Z" }, - { url = "https://files.pythonhosted.org/packages/2b/02/7788f9fefa1d060ca68717c3901ae7fffa21ee087a90b7f23c7a603c32ae/cryptography-46.0.7-cp311-abi3-win_amd64.whl", hash = "sha256:397655da831414d165029da9bc483bed2fe0e75dde6a1523ec2fe63f3c46046b", size = 3488363, upload-time = "2026-04-08T01:56:41.893Z" }, - { url = "https://files.pythonhosted.org/packages/7b/56/15619b210e689c5403bb0540e4cb7dbf11a6bf42e483b7644e471a2812b3/cryptography-46.0.7-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:d151173275e1728cf7839aaa80c34fe550c04ddb27b34f48c232193df8db5842", size = 7119671, upload-time = "2026-04-08T01:56:44Z" }, - { url = "https://files.pythonhosted.org/packages/74/66/e3ce040721b0b5599e175ba91ab08884c75928fbeb74597dd10ef13505d2/cryptography-46.0.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:db0f493b9181c7820c8134437eb8b0b4792085d37dbb24da050476ccb664e59c", size = 4268551, upload-time = "2026-04-08T01:56:46.071Z" }, - { url = "https://files.pythonhosted.org/packages/03/11/5e395f961d6868269835dee1bafec6a1ac176505a167f68b7d8818431068/cryptography-46.0.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ebd6daf519b9f189f85c479427bbd6e9c9037862cf8fe89ee35503bd209ed902", size = 4408887, upload-time = "2026-04-08T01:56:47.718Z" }, - { url = "https://files.pythonhosted.org/packages/40/53/8ed1cf4c3b9c8e611e7122fb56f1c32d09e1fff0f1d77e78d9ff7c82653e/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:b7b412817be92117ec5ed95f880defe9cf18a832e8cafacf0a22337dc1981b4d", size = 4271354, upload-time = "2026-04-08T01:56:49.312Z" }, - { url = "https://files.pythonhosted.org/packages/50/46/cf71e26025c2e767c5609162c866a78e8a2915bbcfa408b7ca495c6140c4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:fbfd0e5f273877695cb93baf14b185f4878128b250cc9f8e617ea0c025dfb022", size = 4905845, upload-time = "2026-04-08T01:56:50.916Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ea/01276740375bac6249d0a971ebdf6b4dc9ead0ee0a34ef3b5a88c1a9b0d4/cryptography-46.0.7-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ffca7aa1d00cf7d6469b988c581598f2259e46215e0140af408966a24cf086ce", size = 4444641, upload-time = "2026-04-08T01:56:52.882Z" }, - { url = "https://files.pythonhosted.org/packages/3d/4c/7d258f169ae71230f25d9f3d06caabcff8c3baf0978e2b7d65e0acac3827/cryptography-46.0.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:60627cf07e0d9274338521205899337c5d18249db56865f943cbe753aa96f40f", size = 3967749, upload-time = "2026-04-08T01:56:54.597Z" }, - { url = "https://files.pythonhosted.org/packages/b5/2a/2ea0767cad19e71b3530e4cad9605d0b5e338b6a1e72c37c9c1ceb86c333/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:80406c3065e2c55d7f49a9550fe0c49b3f12e5bfff5dedb727e319e1afb9bf99", size = 4270942, upload-time = "2026-04-08T01:56:56.416Z" }, - { url = "https://files.pythonhosted.org/packages/41/3d/fe14df95a83319af25717677e956567a105bb6ab25641acaa093db79975d/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:c5b1ccd1239f48b7151a65bc6dd54bcfcc15e028c8ac126d3fada09db0e07ef1", size = 4871079, upload-time = "2026-04-08T01:56:58.31Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/4a479e0f36f8f378d397f4eab4c850b4ffb79a2f0d58704b8fa0703ddc11/cryptography-46.0.7-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d5f7520159cd9c2154eb61eb67548ca05c5774d39e9c2c4339fd793fe7d097b2", size = 4443999, upload-time = "2026-04-08T01:57:00.508Z" }, - { url = "https://files.pythonhosted.org/packages/28/17/b59a741645822ec6d04732b43c5d35e4ef58be7bfa84a81e5ae6f05a1d33/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fcd8eac50d9138c1d7fc53a653ba60a2bee81a505f9f8850b6b2888555a45d0e", size = 4399191, upload-time = "2026-04-08T01:57:02.654Z" }, - { url = "https://files.pythonhosted.org/packages/59/6a/bb2e166d6d0e0955f1e9ff70f10ec4b2824c9cfcdb4da772c7dd69cc7d80/cryptography-46.0.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:65814c60f8cc400c63131584e3e1fad01235edba2614b61fbfbfa954082db0ee", size = 4655782, upload-time = "2026-04-08T01:57:04.592Z" }, - { url = "https://files.pythonhosted.org/packages/95/b6/3da51d48415bcb63b00dc17c2eff3a651b7c4fed484308d0f19b30e8cb2c/cryptography-46.0.7-cp314-cp314t-win32.whl", hash = "sha256:fdd1736fed309b4300346f88f74cd120c27c56852c3838cab416e7a166f67298", size = 3002227, upload-time = "2026-04-08T01:57:06.91Z" }, - { url = "https://files.pythonhosted.org/packages/32/a8/9f0e4ed57ec9cebe506e58db11ae472972ecb0c659e4d52bbaee80ca340a/cryptography-46.0.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e06acf3c99be55aa3b516397fe42f5855597f430add9c17fa46bf2e0fb34c9bb", size = 3475332, upload-time = "2026-04-08T01:57:08.807Z" }, - { url = "https://files.pythonhosted.org/packages/a7/7f/cd42fc3614386bc0c12f0cb3c4ae1fc2bbca5c9662dfed031514911d513d/cryptography-46.0.7-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:462ad5cb1c148a22b2e3bcc5ad52504dff325d17daf5df8d88c17dda1f75f2a4", size = 7165618, upload-time = "2026-04-08T01:57:10.645Z" }, - { url = "https://files.pythonhosted.org/packages/a5/d0/36a49f0262d2319139d2829f773f1b97ef8aef7f97e6e5bd21455e5a8fb5/cryptography-46.0.7-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:84d4cced91f0f159a7ddacad249cc077e63195c36aac40b4150e7a57e84fffe7", size = 4270628, upload-time = "2026-04-08T01:57:12.885Z" }, - { url = "https://files.pythonhosted.org/packages/8a/6c/1a42450f464dda6ffbe578a911f773e54dd48c10f9895a23a7e88b3e7db5/cryptography-46.0.7-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:128c5edfe5e5938b86b03941e94fac9ee793a94452ad1365c9fc3f4f62216832", size = 4415405, upload-time = "2026-04-08T01:57:14.923Z" }, - { url = "https://files.pythonhosted.org/packages/9a/92/4ed714dbe93a066dc1f4b4581a464d2d7dbec9046f7c8b7016f5286329e2/cryptography-46.0.7-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5e51be372b26ef4ba3de3c167cd3d1022934bc838ae9eaad7e644986d2a3d163", size = 4272715, upload-time = "2026-04-08T01:57:16.638Z" }, - { url = "https://files.pythonhosted.org/packages/b7/e6/a26b84096eddd51494bba19111f8fffe976f6a09f132706f8f1bf03f51f7/cryptography-46.0.7-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:cdf1a610ef82abb396451862739e3fc93b071c844399e15b90726ef7470eeaf2", size = 4918400, upload-time = "2026-04-08T01:57:19.021Z" }, - { url = "https://files.pythonhosted.org/packages/c7/08/ffd537b605568a148543ac3c2b239708ae0bd635064bab41359252ef88ed/cryptography-46.0.7-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1d25aee46d0c6f1a501adcddb2d2fee4b979381346a78558ed13e50aa8a59067", size = 4450634, upload-time = "2026-04-08T01:57:21.185Z" }, - { url = "https://files.pythonhosted.org/packages/16/01/0cd51dd86ab5b9befe0d031e276510491976c3a80e9f6e31810cce46c4ad/cryptography-46.0.7-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:cdfbe22376065ffcf8be74dc9a909f032df19bc58a699456a21712d6e5eabfd0", size = 3985233, upload-time = "2026-04-08T01:57:22.862Z" }, - { url = "https://files.pythonhosted.org/packages/92/49/819d6ed3a7d9349c2939f81b500a738cb733ab62fbecdbc1e38e83d45e12/cryptography-46.0.7-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:abad9dac36cbf55de6eb49badd4016806b3165d396f64925bf2999bcb67837ba", size = 4271955, upload-time = "2026-04-08T01:57:24.814Z" }, - { url = "https://files.pythonhosted.org/packages/80/07/ad9b3c56ebb95ed2473d46df0847357e01583f4c52a85754d1a55e29e4d0/cryptography-46.0.7-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:935ce7e3cfdb53e3536119a542b839bb94ec1ad081013e9ab9b7cfd478b05006", size = 4879888, upload-time = "2026-04-08T01:57:26.88Z" }, - { url = "https://files.pythonhosted.org/packages/b8/c7/201d3d58f30c4c2bdbe9b03844c291feb77c20511cc3586daf7edc12a47b/cryptography-46.0.7-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:35719dc79d4730d30f1c2b6474bd6acda36ae2dfae1e3c16f2051f215df33ce0", size = 4449961, upload-time = "2026-04-08T01:57:29.068Z" }, - { url = "https://files.pythonhosted.org/packages/a5/ef/649750cbf96f3033c3c976e112265c33906f8e462291a33d77f90356548c/cryptography-46.0.7-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7bbc6ccf49d05ac8f7d7b5e2e2c33830d4fe2061def88210a126d130d7f71a85", size = 4401696, upload-time = "2026-04-08T01:57:31.029Z" }, - { url = "https://files.pythonhosted.org/packages/41/52/a8908dcb1a389a459a29008c29966c1d552588d4ae6d43f3a1a4512e0ebe/cryptography-46.0.7-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a1529d614f44b863a7b480c6d000fe93b59acee9c82ffa027cfadc77521a9f5e", size = 4664256, upload-time = "2026-04-08T01:57:33.144Z" }, - { url = "https://files.pythonhosted.org/packages/4b/fa/f0ab06238e899cc3fb332623f337a7364f36f4bb3f2534c2bb95a35b132c/cryptography-46.0.7-cp38-abi3-win32.whl", hash = "sha256:f247c8c1a1fb45e12586afbb436ef21ff1e80670b2861a90353d9b025583d246", size = 3013001, upload-time = "2026-04-08T01:57:34.933Z" }, - { url = "https://files.pythonhosted.org/packages/d2/f1/00ce3bde3ca542d1acd8f8cfa38e446840945aa6363f9b74746394b14127/cryptography-46.0.7-cp38-abi3-win_amd64.whl", hash = "sha256:506c4ff91eff4f82bdac7633318a526b1d1309fc07ca76a3ad182cb5b686d6d3", size = 3472985, upload-time = "2026-04-08T01:57:36.714Z" }, - { url = "https://files.pythonhosted.org/packages/63/0c/dca8abb64e7ca4f6b2978769f6fea5ad06686a190cec381f0a796fdcaaba/cryptography-46.0.7-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc9ab8856ae6cf7c9358430e49b368f3108f050031442eaeb6b9d87e4dcf4e4f", size = 3476879, upload-time = "2026-04-08T01:57:38.664Z" }, - { url = "https://files.pythonhosted.org/packages/3a/ea/075aac6a84b7c271578d81a2f9968acb6e273002408729f2ddff517fed4a/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d3b99c535a9de0adced13d159c5a9cf65c325601aa30f4be08afd680643e9c15", size = 4219700, upload-time = "2026-04-08T01:57:40.625Z" }, - { url = "https://files.pythonhosted.org/packages/6c/7b/1c55db7242b5e5612b29fc7a630e91ee7a6e3c8e7bf5406d22e206875fbd/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d02c738dacda7dc2a74d1b2b3177042009d5cab7c7079db74afc19e56ca1b455", size = 4385982, upload-time = "2026-04-08T01:57:42.725Z" }, - { url = "https://files.pythonhosted.org/packages/cb/da/9870eec4b69c63ef5925bf7d8342b7e13bc2ee3d47791461c4e49ca212f4/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:04959522f938493042d595a736e7dbdff6eb6cc2339c11465b3ff89343b65f65", size = 4219115, upload-time = "2026-04-08T01:57:44.939Z" }, - { url = "https://files.pythonhosted.org/packages/f4/72/05aa5832b82dd341969e9a734d1812a6aadb088d9eb6f0430fc337cc5a8f/cryptography-46.0.7-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:3986ac1dee6def53797289999eabe84798ad7817f3e97779b5061a95b0ee4968", size = 4385479, upload-time = "2026-04-08T01:57:46.86Z" }, - { url = "https://files.pythonhosted.org/packages/20/2a/1b016902351a523aa2bd446b50a5bc1175d7a7d1cf90fe2ef904f9b84ebc/cryptography-46.0.7-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:258514877e15963bd43b558917bc9f54cf7cf866c38aa576ebf47a77ddbc43a4", size = 3412829, upload-time = "2026-04-08T01:57:48.874Z" }, + { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" }, + { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" }, + { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" }, + { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" }, + { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" }, + { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" }, + { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" }, + { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" }, + { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" }, + { url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" }, + { url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" }, + { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" }, + { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" }, + { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" }, + { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" }, + { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" }, + { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" }, + { url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" }, + { url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" }, + { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" }, + { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" }, + { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" }, + { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" }, + { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" }, + { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" }, + { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" }, + { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" }, + { url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" }, + { url = "https://files.pythonhosted.org/packages/be/d2/024b5e06be9d44cb021fb0e1a03d34d63989cf56a0fe62f3dfbab695b9b4/cryptography-48.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:84cf79f0dc8b36ac5da873481716e87aef31fcfa0444f9e1d8b4b2cece142855", size = 3950391, upload-time = "2026-05-04T22:59:17.415Z" }, + { url = "https://files.pythonhosted.org/packages/bc/17/3861e17c56fa0fd37491a14a8673fdb77c57fc5693cafe745ea8b06dba75/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b", size = 4637126, upload-time = "2026-05-04T22:59:20.197Z" }, + { url = "https://files.pythonhosted.org/packages/f0/0a/7e226dbff530f21480727eb764973a7bff2b912f8e15cd4f129e71b56d1d/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0890f502ddf7d9c6426129c3f49f5c0a39278ed7cd6322c8755ffca6ee675a13", size = 4667270, upload-time = "2026-05-04T22:59:22.647Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f2/5a72274ca9f1b2a8b44a662ee0bf1b435909deb473d6f97bcd035bcdbc71/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb", size = 4636797, upload-time = "2026-05-04T22:59:24.912Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e1/48cedb2fe63626e91ded1edad159e2a4fb8b6906c4425eb7749673077ce7/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:4defde8685ae324a9eb9d818717e93b4638ef67070ac9bc15b8ca85f63048355", size = 4666800, upload-time = "2026-05-04T22:59:27.474Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ca/7e8365deec19afb2b2c7be7c1c0aa8f99633b54e90c570999acda93260fc/cryptography-48.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:db63bf618e5dea46c07de12e900fe1cdd2541e6dc9dbae772a70b7d4d4765f6a", size = 3739536, upload-time = "2026-05-04T22:59:29.61Z" }, ] [[package]] @@ -558,11 +558,11 @@ wheels = [ [[package]] name = "idna" -version = "3.13" +version = "3.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ce/cc/762dfb036166873f0059f3b7de4565e1b5bc3d6f28a414c13da27e442f99/idna-3.13.tar.gz", hash = "sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242", size = 194210, upload-time = "2026-04-22T16:42:42.314Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/b1/efac073e0c297ecf2fb33c346989a529d4e19164f1759102dee5953ee17e/idna-3.14.tar.gz", hash = "sha256:466d810d7a2cc1022bea9b037c39728d51ae7dad40d480fc9b7d7ecf98ba8ee3", size = 198272, upload-time = "2026-05-10T20:32:15.935Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/13/ad7d7ca3808a898b4612b6fe93cde56b53f3034dcde235acb1f0e1df24c6/idna-3.13-py3-none-any.whl", hash = "sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3", size = 68629, upload-time = "2026-04-22T16:42:40.909Z" }, + { url = "https://files.pythonhosted.org/packages/6c/3c/3f62dee257eb3d6b2c1ef2a09d36d9793c7111156a73b5654d2c2305e5ce/idna-3.14-py3-none-any.whl", hash = "sha256:e677eaf072e290f7b725f9acf0b3a2bd55f9fd6f7c70abe5f0e34823d0accf69", size = 72184, upload-time = "2026-05-10T20:32:14.295Z" }, ] [[package]] @@ -759,11 +759,11 @@ wheels = [ [[package]] name = "packaging" -version = "26.1" +version = "26.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" }, + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] [[package]] @@ -895,20 +895,20 @@ wheels = [ [[package]] name = "pyln-client" -version = "26.4" +version = "26.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyln-bolt7" }, { name = "pyln-proto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/14/0faf6b8b04f368629897a010357d3b8d21c8a800b972b433a358caebe32b/pyln_client-26.4.tar.gz", hash = "sha256:e696076fcf1329ee22198097b8139a096f653cbbfc6a03e471455ca6f5d68a46", size = 92160, upload-time = "2026-04-20T04:58:12.666Z" } +sdist = { url = "https://files.pythonhosted.org/packages/28/fe/e1e19c59dd7520dec2de992f0a3da6292db24162c9830be279616a7b8810/pyln_client-26.4.1.tar.gz", hash = "sha256:26dfb21ac39c61b7ab7a40db18798b002f3f19a179ff2ec40374a9419d032050", size = 92170, upload-time = "2026-04-24T04:53:08.46Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/41/f67df5064b4b9d258fb30e96e1d3ca8f0395c7c0098b24091dde6238fb8e/pyln_client-26.4-py3-none-any.whl", hash = "sha256:d5470e1ecccc1b6e46e39658c1019c62868c5aed4a0203635052803ddedcce2e", size = 37655, upload-time = "2026-04-20T04:58:11.429Z" }, + { url = "https://files.pythonhosted.org/packages/65/90/d3b5e11f85cf076b1011a1ea6031ced3745abf451e47a1697216e1ea7c29/pyln_client-26.4.1-py3-none-any.whl", hash = "sha256:a9afba9b117818023bf4567a94c34633e9f2e89f0e1ca11e433329a2c678a685", size = 37673, upload-time = "2026-04-24T04:53:07.187Z" }, ] [[package]] name = "pyln-proto" -version = "26.4" +version = "26.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "base58" }, @@ -917,14 +917,14 @@ dependencies = [ { name = "cryptography" }, { name = "pysocks" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0d/35/5d30332fcaa8d77be87290c5534d45c48822310611ae6ea6d3e48c242ded/pyln_proto-26.4.tar.gz", hash = "sha256:dde9e243c7bf6fbcf7904e0dbd18c4f05dc17e668b074bfab090a7376e3de6cf", size = 44861, upload-time = "2026-04-20T04:58:09.361Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/4b/c8d676ad84d6066553b203668c0e36f45cfae0a4d7140eb88aed6d65b38b/pyln_proto-26.4.1.tar.gz", hash = "sha256:d73f261bacc06a6a613dcece05fc429699b740a4673f24dffb7c41bd3542d589", size = 44864, upload-time = "2026-04-24T04:53:10.196Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/c9/8a34960e1e9550f87697d1f05208d9cc916c14419f18f2d6013801079fc1/pyln_proto-26.4-py3-none-any.whl", hash = "sha256:97772d285aa0d0c8a1545577aeef8821e08d59676f63ae760d270e1b3015bc9d", size = 31811, upload-time = "2026-04-20T04:58:08.369Z" }, + { url = "https://files.pythonhosted.org/packages/a3/f1/6541b6cd8a017d3505fcb49885d3f01b8e6bb09e40db18e686edb9edeb99/pyln_proto-26.4.1-py3-none-any.whl", hash = "sha256:b5d10d5e69cf3debd9b289e2ffa49713fca6c349a71cb0eaf9e733024f15b3da", size = 31833, upload-time = "2026-04-24T04:53:08.686Z" }, ] [[package]] name = "pyln-testing" -version = "26.4" +version = "26.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cheroot" }, @@ -939,9 +939,9 @@ dependencies = [ { name = "python-bitcoinlib" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/17/7e/275f0416e30faa3093ad31703f3d8702dc06569164a185813b23672ba6d9/pyln_testing-26.4.tar.gz", hash = "sha256:cca1f4fec65f12bfda809674ea7c231285a96ef0b8bc2c69ef4919f1b465d2ae", size = 52609, upload-time = "2026-04-20T04:58:07.746Z" } +sdist = { url = "https://files.pythonhosted.org/packages/64/73/d969847564591fe16633b11bf8be9f8184ccd885e367acee171e9fd6d2d8/pyln_testing-26.4.1.tar.gz", hash = "sha256:0a40d66ec2f0840df95bdbc4845c214aede01e9d7a89a422f5b6b00a89e63690", size = 52617, upload-time = "2026-04-24T04:53:10.904Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/c4/3cb55bf86a2499510a24b310897903efafce847869eb1ec1c7b67b7fcfa4/pyln_testing-26.4-py3-none-any.whl", hash = "sha256:ccb7641bbbb1b6b650f1b8238aacf04d7ebeea944ebd2855a901e25d18846804", size = 54365, upload-time = "2026-04-20T04:58:06.793Z" }, + { url = "https://files.pythonhosted.org/packages/c6/ad/603ff35f7b60bcc6cf364a101c58a73887132063f0773767108946361ed2/pyln_testing-26.4.1-py3-none-any.whl", hash = "sha256:dd0e9d8a865593ddc9a14ef43e9aaec0fe0d348ce9ec2fcd23807a35c98c9e8c", size = 54381, upload-time = "2026-04-24T04:53:09.599Z" }, ] [[package]] @@ -1035,7 +1035,7 @@ wheels = [ [[package]] name = "requests" -version = "2.33.1" +version = "2.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -1043,9 +1043,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/b8/7a707d60fea4c49094e40262cc0e2ca6c768cca21587e34d3f705afec47e/requests-2.34.0.tar.gz", hash = "sha256:7d62fe92f50eb82c529b0916bb445afa1531a566fc8f35ffdc64446e771b856a", size = 142436, upload-time = "2026-05-11T19:29:51.717Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e6/e300fce5fe83c30520607a015dabd985df3251e188d234bfe9492e17a389/requests-2.34.0-py3-none-any.whl", hash = "sha256:917520a21b767485ce7c588f4ebb917c436b24a31231b44228715eaeb5a52c60", size = 73021, upload-time = "2026-05-11T19:29:49.923Z" }, ] [[package]] @@ -1273,11 +1273,11 @@ wheels = [ [[package]] name = "urllib3" -version = "2.6.3" +version = "2.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, ] [[package]] From 5c5f49de294a2b8b0c91840a0771a225b49cac20 Mon Sep 17 00:00:00 2001 From: daywalker90 <8257956+daywalker90@users.noreply.github.com> Date: Mon, 5 Jan 2026 14:49:33 +0100 Subject: [PATCH 15/56] wait for hold certs to be generated --- src/main.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main.rs b/src/main.rs index 957f9c8..59e5eed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -268,6 +268,14 @@ async fn check_hold_support(plugin: Plugin) -> Result<(), anyhow::E cert_dir.to_str().unwrap() ); + let max_retries = 10; + let mut retries = 0; + while retries < max_retries && !do_certificates_exist(&cert_dir) { + log::debug!("Hold certificates incomplete. Waiting..."); + time::sleep(Duration::from_millis(500)).await; + retries += 1; + } + let ca_cert = tokio::fs::read(cert_dir.join("ca.pem")).await?; let client_cert = tokio::fs::read(cert_dir.join("client.pem")).await?; let client_key = tokio::fs::read(cert_dir.join("client-key.pem")).await?; @@ -289,3 +297,12 @@ async fn check_hold_support(plugin: Plugin) -> Result<(), anyhow::E Ok(()) } + +fn do_certificates_exist(cert_dir: &Path) -> bool { + let required_files = ["client.pem", "client-key.pem", "ca.pem"]; + + required_files.iter().all(|file| { + let path = cert_dir.join(file); + path.exists() && path.metadata().map(|m| m.len() > 0).unwrap_or(false) + }) +} From 78ac7cac0536b16e8cab130e5a763beed42bd11b Mon Sep 17 00:00:00 2001 From: daywalker90 <8257956+daywalker90@users.noreply.github.com> Date: Tue, 3 Feb 2026 22:33:00 +0100 Subject: [PATCH 16/56] prepare for nostr 0.45 --- Cargo.lock | 90 +++++++++----------------------- Cargo.toml | 11 +++- src/main.rs | 4 +- src/nwc.rs | 109 +++++++++++++++++++++------------------ src/nwc_balance.rs | 2 +- src/nwc_hold.rs | 4 +- src/nwc_info.rs | 2 +- src/nwc_invoice.rs | 6 +-- src/nwc_keysend.rs | 2 +- src/nwc_lookups.rs | 2 +- src/nwc_notifications.rs | 18 +++---- src/nwc_pay.rs | 2 +- src/parse.rs | 3 +- src/rpc.rs | 4 +- src/structs.rs | 7 +-- src/tasks.rs | 2 +- src/util.rs | 2 +- tests/test_cln-nip47.py | 19 +++---- 18 files changed, 132 insertions(+), 157 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 55e0cde..696565d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,12 +86,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "atomic-destructor" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef49f5882e4b6afaac09ad239a4f8c70a24b8f2b0897edb1f706008efd109cf4" - [[package]] name = "atomic-waker" version = "1.1.2" @@ -215,6 +209,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "btreecap" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6160c957d8aa33d0a8ba1dbab98e3cb57023ad9374c501441e88559f99e6c4c9" + [[package]] name = "bumpalo" version = "3.20.2" @@ -294,9 +294,11 @@ dependencies = [ "anyhow", "cln-plugin", "cln-rpc", + "futures", "hex", "log", "log-panics", + "nostr", "nostr-sdk", "parking_lot", "prost", @@ -363,7 +365,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", - "rand_core 0.6.4", "typenum", ] @@ -561,10 +562,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] [[package]] @@ -911,9 +910,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", ] [[package]] @@ -1043,9 +1039,8 @@ checksum = "f0efe882e02d206d8d279c20eb40e03baf7cb5136a1476dc084a324fbc3ec42d" [[package]] name = "nostr" -version = "0.44.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aa5e3b6a278ed061835fe1ee293b71641e6bf8b401cfe4e1834bbf4ef0a34e1" +version = "0.44.1" +source = "git+https://github.com/rust-nostr/nostr.git?rev=bf3028eb11b4e324c0c660b4e4c83348cdcc00c2#bf3028eb11b4e324c0c660b4e4c83348cdcc00c2" dependencies = [ "aes", "base64", @@ -1055,9 +1050,10 @@ dependencies = [ "cbc", "chacha20", "chacha20poly1305", - "getrandom 0.2.17", "hex", "instant", + "once_cell", + "rand", "scrypt", "secp256k1", "serde", @@ -1069,9 +1065,9 @@ dependencies = [ [[package]] name = "nostr-database" version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7462c9d8ae5ef6a28d66a192d399ad2530f1f2130b13186296dbb11bdef5b3d1" +source = "git+https://github.com/rust-nostr/nostr.git?rev=bf3028eb11b4e324c0c660b4e4c83348cdcc00c2#bf3028eb11b4e324c0c660b4e4c83348cdcc00c2" dependencies = [ + "btreecap", "lru", "nostr", "tokio", @@ -1080,42 +1076,27 @@ dependencies = [ [[package]] name = "nostr-gossip" version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade30de16869618919c6b5efc8258f47b654a98b51541eb77f85e8ec5e3c83a6" +source = "git+https://github.com/rust-nostr/nostr.git?rev=bf3028eb11b4e324c0c660b4e4c83348cdcc00c2#bf3028eb11b4e324c0c660b4e4c83348cdcc00c2" dependencies = [ "nostr", ] -[[package]] -name = "nostr-relay-pool" -version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b1073ccfbaea5549fb914a9d52c68dab2aecda61535e5143dd73e95445a804b" -dependencies = [ - "async-utility", - "async-wsocket", - "atomic-destructor", - "hex", - "lru", - "negentropy", - "nostr", - "nostr-database", - "tokio", - "tracing", -] - [[package]] name = "nostr-sdk" version = "0.44.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471732576710e779b64f04c55e3f8b5292f865fea228436daf19694f0bf70393" +source = "git+https://github.com/rust-nostr/nostr.git?rev=bf3028eb11b4e324c0c660b4e4c83348cdcc00c2#bf3028eb11b4e324c0c660b4e4c83348cdcc00c2" dependencies = [ "async-utility", + "async-wsocket", + "futures", + "hex", + "lru", + "negentropy", "nostr", "nostr-database", "nostr-gossip", - "nostr-relay-pool", "tokio", + "tokio-stream", "tracing", ] @@ -1369,37 +1350,16 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" -[[package]] -name = "rand" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - [[package]] name = "rand" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ - "rand_chacha 0.9.0", + "rand_chacha", "rand_core 0.9.5", ] -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - [[package]] name = "rand_chacha" version = "0.9.0" @@ -1568,7 +1528,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes", - "rand 0.8.6", "secp256k1-sys", "serde", ] @@ -1874,6 +1833,7 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", + "tokio-util", ] [[package]] @@ -2083,7 +2043,7 @@ dependencies = [ "http", "httparse", "log", - "rand 0.9.4", + "rand", "rustls", "rustls-pki-types", "sha1", diff --git a/Cargo.toml b/Cargo.toml index c5b1a1c..d7be3be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,15 @@ cln-plugin = "0.6" # cln-plugin = { path = "../lightning/plugins/", version = "^0.6" } parking_lot = "0.12" -# nostr-sdk = { git = "https://github.com/rust-nostr/nostr.git", rev = "f7122f5", features = ["nip47", "nip04", "nip44"]} -nostr-sdk = { version = "0.44", features = ["nip47", "nip04", "nip44"] } +nostr-sdk = { git = "https://github.com/rust-nostr/nostr.git", rev = "bf3028eb11b4e324c0c660b4e4c83348cdcc00c2" } +nostr = { git = "https://github.com/rust-nostr/nostr.git", rev = "bf3028eb11b4e324c0c660b4e4c83348cdcc00c2", features = [ + "nip47", + "nip04", + "nip44", +] } +# nostr-sdk = { version = "0.44", features = ["nip47", "nip04", "nip44"] } + +futures = "0.3" uuid = { version = "1", features = ["v4"] } diff --git a/src/main.rs b/src/main.rs index 59e5eed..bb172bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use cln_plugin::{ options::{ConfigOption, DefaultBooleanConfigOption, StringArrayConfigOption}, }; use cln_rpc::{ClnRpc, model::requests::ListdatastoreRequest}; -use nostr_sdk::nips::nip47; +use nostr::nips::nip47; use nwc::run_nwc; use nwc_notifications::{payment_received_handler, payment_sent_handler}; use parse::read_startup_options; @@ -23,7 +23,7 @@ use tonic::transport::{Certificate, ClientTlsConfig, Endpoint, Identity}; use util::{load_nwc_store, update_nwc_store}; use crate::{ - hold::{hold_client::HoldClient, InvoiceState, ListRequest}, + hold::{InvoiceState, ListRequest, hold_client::HoldClient}, nwc_notifications::holdinvoice_accepted_handler, }; diff --git a/src/nwc.rs b/src/nwc.rs index 247aedc..423e0cf 100644 --- a/src/nwc.rs +++ b/src/nwc.rs @@ -2,24 +2,27 @@ use std::{borrow::Cow, time::Duration}; use anyhow::anyhow; use cln_plugin::Plugin; -use nostr_sdk::{ +use futures::StreamExt; +use nostr::{ Alphabet, - Client, Event, EventBuilder, EventId, + Filter, Keys, + Kind, PublicKey, - RelayPoolNotification, - RelayStatus, SecretKey, SignerError, SingleLetterTag, + Tag, TagKind, Timestamp, - client, nips::{nip04, nip44, nip47}, - nostr::{Filter, Kind, Tag}, +}; +use nostr_sdk::{ + client::{self, Client, ClientNotification, Error}, + relay::RelayStatus, }; use tokio::{sync::oneshot, time}; @@ -46,16 +49,16 @@ pub async fn run_nwc( plugin: Plugin, label: String, nwc_store: NwcStore, -) -> Result<(), client::Error> { +) -> Result<(), Error> { let (method_capabilities, _) = build_capabilities(is_read_only_nwc(&nwc_store), &plugin); let wallet_keys = Keys::new( SecretKey::from_hex(&nwc_store.walletkey) - .map_err(|e| client::Error::Signer(SignerError::backend(e)))?, + .map_err(|e| Error::Signer(SignerError::backend(e)))?, ); let client_pubkey = Keys::new(nwc_store.uri.secret.clone()).public_key(); - let nostr_client = Client::new(wallet_keys.clone()); + let nostr_client = Client::builder().signer(wallet_keys.clone()).build(); log::debug!("relay_count:{}", nwc_store.uri.relays.len()); @@ -73,9 +76,9 @@ pub async fn run_nwc( let label_clone = label.clone(); tokio::spawn(async move { loop { - nostr_client_clone.connect().await; nostr_client_clone - .wait_for_connection(Duration::from_secs(30)) + .connect() + .and_wait(Duration::from_secs(30)) .await; let relays = nostr_client_clone.relays().await; if relays.is_empty() { @@ -115,37 +118,41 @@ pub async fn run_nwc( .author(client_pubkey) .since(Timestamp::now() - STARTUP_DELAY - 1); - if let Err(e) = nostr_client_clone.subscribe(filter, None).await { - log::warn!("Could not subscribe to nwc events! {e}"); - nostr_client_clone.disconnect().await; - time::sleep(Duration::from_secs(5)).await; - continue; + let mut notifications = nostr_client_clone.notifications(); + + match nostr_client_clone.subscribe(filter).await { + Ok(o) => { + if o.success.is_empty() { + log::warn!("Could not subscribe to any relay!"); + nostr_client_clone.disconnect().await; + time::sleep(Duration::from_secs(5)).await; + continue; + } + } + Err(e) => { + log::warn!("Error subscribing to relays: {e}"); + nostr_client_clone.disconnect().await; + time::sleep(Duration::from_secs(5)).await; + continue; + } } - let client_clone_handler = nostr_client_clone.clone(); - match nostr_client_clone - .handle_notifications(|notification| { - let client_clone_handler = client_clone_handler.clone(); - let plugin_clone = plugin_clone.clone(); - let label_clone = label_clone.clone(); - let wallet_keys_clone = wallet_keys.clone(); - nwc_request_handler( - notification, - client_clone_handler, - plugin_clone, - label_clone, - wallet_keys_clone, - client_pubkey, - ) - }) + while let Some(notification) = notifications.next().await { + if let Err(e) = nwc_request_handler( + notification, + &nostr_client_clone, + &plugin_clone, + &label_clone, + &wallet_keys, + client_pubkey, + ) .await - { - Ok(()) => { - log::info!("NWC handler for `{label_clone}` stopped"); - break; - } - Err(e) => log::warn!("NWC handler for `{label_clone}` had an error: {e}"), + { + log::warn!("NWC handler for `{label_clone}` had an error: {e}"); + }; } + + {}; } }); @@ -226,29 +233,29 @@ pub fn stop_nwc_budget_job(plugin: &Plugin, label: &String) { } async fn nwc_request_handler( - notification: RelayPoolNotification, - nostr_client: client::Client, - plugin: Plugin, - label: String, - wallet_keys: Keys, + notification: ClientNotification, + nostr_client: &client::Client, + plugin: &Plugin, + label: &String, + wallet_keys: &Keys, client_pubkey: PublicKey, -) -> Result> { +) -> Result<(), Box> { let (relay_url, subscription_id, event) = match notification { - RelayPoolNotification::Event { + ClientNotification::Event { relay_url, subscription_id, event, } => (relay_url, subscription_id, event), - RelayPoolNotification::Message { + ClientNotification::Message { relay_url: _, message: _, - } => return Ok(false), - RelayPoolNotification::Shutdown => return Ok(true), + } => return Ok(()), + ClientNotification::Shutdown => return Ok(()), }; if let Some(expi) = event.tags.expiration() { if *expi < Timestamp::now() { - return Ok(false); + return Ok(()); } } log::debug!("relay_url:{relay_url} subscription_id:{subscription_id} {event:?}"); @@ -264,7 +271,7 @@ async fn nwc_request_handler( multi_pay_invoice(plugin.clone(), multi_pay_invoice_request, &label).await } nip47::RequestParams::PayKeysend(pay_keysend_request) => { - pay_keysend_response(plugin, pay_keysend_request, &label).await + pay_keysend_response(plugin.clone(), pay_keysend_request, &label).await } nip47::RequestParams::MultiPayKeysend(multi_pay_keysend_request) => { multi_pay_keysend(plugin.clone(), multi_pay_keysend_request, &label).await @@ -330,7 +337,7 @@ async fn nwc_request_handler( log::debug!("SENT RESPONSE {response_event:?}"); } - Ok(false) + Ok(()) } fn check_nip44_support(event: &Event) -> bool { diff --git a/src/nwc_balance.rs b/src/nwc_balance.rs index c29b343..1c3c1ea 100644 --- a/src/nwc_balance.rs +++ b/src/nwc_balance.rs @@ -1,6 +1,6 @@ use cln_plugin::Plugin; use cln_rpc::{model::requests::ListpeerchannelsRequest, primitives::ChannelState}; -use nostr_sdk::nips::nip47; +use nostr::nips::nip47; use crate::{structs::PluginState, util::load_nwc_store}; diff --git a/src/nwc_hold.rs b/src/nwc_hold.rs index a706bde..d3b0d8f 100644 --- a/src/nwc_hold.rs +++ b/src/nwc_hold.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use cln_plugin::Plugin; use cln_rpc::primitives::Sha256; -use nostr_sdk::{nips::nip47, Timestamp}; +use nostr::{nips::nip47, Timestamp}; use crate::{ hold::{invoice_request::Description, CancelRequest, InvoiceRequest, SettleRequest}, @@ -93,7 +93,7 @@ async fn make_hold_invoice( payment_hash: payment_hash.clone(), amount_msat: params.amount, expiry: Some(expiry), - min_final_cltv_expiry: params.cltv_expiry_delta.map(u64::from), + min_final_cltv_expiry: params.min_cltv_expiry_delta.map(u64::from), routing_hints: Vec::new(), description, }; diff --git a/src/nwc_info.rs b/src/nwc_info.rs index d3930e2..27e196a 100644 --- a/src/nwc_info.rs +++ b/src/nwc_info.rs @@ -1,6 +1,6 @@ use cln_plugin::Plugin; use cln_rpc::model::requests::GetinfoRequest; -use nostr_sdk::nips::nip47; +use nostr::nips::nip47; use crate::{ structs::PluginState, diff --git a/src/nwc_invoice.rs b/src/nwc_invoice.rs index 654a0b0..f521673 100644 --- a/src/nwc_invoice.rs +++ b/src/nwc_invoice.rs @@ -5,7 +5,7 @@ use cln_rpc::{ model::requests::InvoiceRequest, primitives::{Amount, AmountOrAny, Sha256}, }; -use nostr_sdk::nips::nip47; +use nostr::{nips::nip47, types::Timestamp}; use uuid::Uuid; use crate::structs::PluginState; @@ -94,8 +94,8 @@ async fn make_invoice( description_hash: params.description_hash, preimage: None, amount: Some(params.amount), - created_at: Some(nostr_sdk::nostr::Timestamp::now()), - expires_at: Some(nostr_sdk::nostr::Timestamp::from_secs(o.expires_at)), + created_at: Some(Timestamp::now()), + expires_at: Some(Timestamp::from_secs(o.expires_at)), }), Err(e) => Err(nip47::NIP47Error { code: nip47::ErrorCode::Internal, diff --git a/src/nwc_keysend.rs b/src/nwc_keysend.rs index 477db88..fbbe1a0 100644 --- a/src/nwc_keysend.rs +++ b/src/nwc_keysend.rs @@ -5,7 +5,7 @@ use cln_rpc::{ model::requests::KeysendRequest, primitives::{Amount, PublicKey, TlvEntry, TlvStream}, }; -use nostr_sdk::nips::nip47; +use nostr::nips::nip47; use tokio::time; use crate::{ diff --git a/src/nwc_lookups.rs b/src/nwc_lookups.rs index b15a54b..cef844c 100644 --- a/src/nwc_lookups.rs +++ b/src/nwc_lookups.rs @@ -15,7 +15,7 @@ use cln_rpc::{ primitives::Sha256, ClnRpc, }; -use nostr_sdk::{nips::nip47, Timestamp}; +use nostr::{nips::nip47, Timestamp}; use crate::{ hold::{self, list_request::Constraint, ListRequest}, diff --git a/src/nwc_notifications.rs b/src/nwc_notifications.rs index e86afaf..8f77a99 100644 --- a/src/nwc_notifications.rs +++ b/src/nwc_notifications.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use anyhow::anyhow; use cln_plugin::Plugin; use cln_rpc::{ + ClnRpc, model::{ requests::{DecodeRequest, ListinvoicesRequest, ListpaysRequest, ListpeerchannelsRequest}, responses::{ @@ -14,19 +15,14 @@ use cln_rpc::{ }, }, primitives::Sha256, - ClnRpc, -}; -use nostr_sdk::{ - nips::nip47, - nostr::{key::PublicKey, EventBuilder, Kind, Tag}, - Client, - Timestamp, }; +use nostr::{EventBuilder, Kind, Tag, Timestamp, key::PublicKey, nips::nip47}; +use nostr_sdk::client::Client; use crate::{ - hold::{list_request::Constraint, InvoiceState, ListRequest, TrackRequest}, - structs::{PluginState, NOT_INV_ERR}, OPT_NOTIFICATIONS, + hold::{InvoiceState, ListRequest, TrackRequest, list_request::Constraint}, + structs::{NOT_INV_ERR, PluginState}, }; pub async fn payment_received_handler( @@ -341,7 +337,7 @@ async fn send_notification( client: &Client, client_pubkey: &PublicKey, ) -> Result<(), anyhow::Error> { - let signer = client.signer().await?; + let signer = client.signer().unwrap().clone(); log::debug!("NOTIFICATION: {notification}"); let content_encrypted_nip04 = signer.nip04_encrypt(client_pubkey, notification).await?; let event_nip04 = EventBuilder::new(Kind::from_u16(23196), content_encrypted_nip04) @@ -460,6 +456,7 @@ pub async fn holdinvoice_accepted_handler( .call_typed(&ListpeerchannelsRequest { id: None, short_channel_id: None, + channel_id: None, }) .await? .channels; @@ -497,6 +494,7 @@ pub async fn holdinvoice_accepted_handler( expires_at, settle_deadline: lowest_htlc_expiry, metadata: None, + state: Some(nip47::TransactionState::Accepted), }, ), }; diff --git a/src/nwc_pay.rs b/src/nwc_pay.rs index 73b64a0..87b2733 100644 --- a/src/nwc_pay.rs +++ b/src/nwc_pay.rs @@ -10,7 +10,7 @@ use cln_rpc::{ ClnRpc, RpcError, }; -use nostr_sdk::nips::nip47; +use nostr::nips::nip47; use tokio::time; use crate::{ diff --git a/src/parse.rs b/src/parse.rs index 7c32966..b1dffed 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -3,6 +3,7 @@ use std::path::Path; use anyhow::anyhow; use cln_plugin::ConfiguredPlugin; use cln_rpc::{model::requests::GetinfoRequest, ClnRpc}; +use nostr::types::RelayUrl; use crate::{ structs::{PluginState, TimeUnit}, @@ -37,7 +38,7 @@ pub async fn read_startup_options( config.my_cln_version = version; for relay in relays_str { log::debug!("RELAY:{relay}"); - config.relays.push(nostr_sdk::RelayUrl::parse(&relay)?); + config.relays.push(RelayUrl::parse(&relay)?); } Ok(()) } diff --git a/src/rpc.rs b/src/rpc.rs index 272d483..06387aa 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -6,7 +6,7 @@ use cln_rpc::model::requests::{ DeldatastoreRequest, ListdatastoreRequest, }; -use nostr_sdk::{Keys, SecretKey, Timestamp, nips::nip47::NostrWalletConnectURI}; +use nostr::{Keys, SecretKey, Timestamp, nips::nip47::NostrWalletConnectUri}; use serde_json::json; use crate::{ @@ -29,7 +29,7 @@ pub async fn nwc_create( let wallet_keys = Keys::generate(); let client_keys = Keys::generate(); - let uri = NostrWalletConnectURI::new( + let uri = NostrWalletConnectUri::new( wallet_keys.public_key(), config.relays.clone(), client_keys.secret_key().clone(), diff --git a/src/structs.rs b/src/structs.rs index 8964bfd..e7eea09 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,7 +1,8 @@ use std::{collections::HashMap, path::PathBuf, str::FromStr, sync::Arc}; use cln_rpc::ClnRpc; -use nostr_sdk::{client, nips::nip47, nostr}; +use nostr::{nips::nip47::NostrWalletConnectUri, types::RelayUrl}; +use nostr_sdk::client; use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use tokio::sync::oneshot; @@ -33,7 +34,7 @@ impl PluginState { #[derive(Clone, Debug)] pub struct Config { - pub relays: Vec, + pub relays: Vec, pub my_cln_version: String, } impl Config { @@ -77,7 +78,7 @@ pub struct BudgetIntervalConfig { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NwcStore { - pub uri: nip47::NostrWalletConnectURI, + pub uri: NostrWalletConnectUri, pub walletkey: String, #[serde(skip_serializing_if = "Option::is_none")] pub budget_msat: Option, diff --git a/src/tasks.rs b/src/tasks.rs index 2437e4a..e46ed40 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -3,7 +3,7 @@ use std::{path::Path, time::Duration}; use anyhow::anyhow; use cln_plugin::Plugin; use cln_rpc::ClnRpc; -use nostr_sdk::Timestamp; +use nostr::types::Timestamp; use tokio::{sync::oneshot, time}; use crate::{ diff --git a/src/util.rs b/src/util.rs index 6956d9e..6d769ad 100644 --- a/src/util.rs +++ b/src/util.rs @@ -4,7 +4,7 @@ use cln_rpc::{ model::requests::{DatastoreMode, DatastoreRequest, ListdatastoreRequest}, ClnRpc, }; -use nostr_sdk::nips::nip47; +use nostr::nips::nip47; use crate::{ structs::{NwcStore, PluginState}, diff --git a/tests/test_cln-nip47.py b/tests/test_cln-nip47.py index 3642c96..6caba46 100755 --- a/tests/test_cln-nip47.py +++ b/tests/test_cln-nip47.py @@ -1484,6 +1484,7 @@ async def test_hold_invoice( "important-plugin": get_hold, "nip47-relays": url, "may_reconnect": True, + "broken_log": r"Relay receiver exited with error", }, ], ) @@ -1672,10 +1673,10 @@ async def test_hold_invoice( ) wait_for( - lambda: l1.rpc.call("listpays", {"payment_hash": payment_hash})["pays"][0][ - "status" - ] - == "complete" + lambda: ( + l1.rpc.call("listpays", {"payment_hash": payment_hash})["pays"][0]["status"] + == "complete" + ) ) preimage = secrets.token_hex(32) @@ -1687,7 +1688,7 @@ async def test_hold_invoice( "payment_hash": payment_hash, "description": "cancel_hold", "expiry": 1000, - "cltv_expiry_delta": 200, + "min_cltv_expiry_delta": 200, }, } content = json.dumps(content) @@ -1815,10 +1816,10 @@ async def test_hold_invoice( assert invoice2_decoded["min_final_cltv_expiry"] == 200 wait_for( - lambda: l1.rpc.call("listpays", {"payment_hash": payment_hash})["pays"][0][ - "status" - ] - == "failed" + lambda: ( + l1.rpc.call("listpays", {"payment_hash": payment_hash})["pays"][0]["status"] + == "failed" + ) ) nwc = Nwc(uri) From 6690c9d49d120b3368de7d51d8f293a383d5a533 Mon Sep 17 00:00:00 2001 From: daywalker90 <8257956+daywalker90@users.noreply.github.com> Date: Tue, 24 Feb 2026 16:18:46 +0100 Subject: [PATCH 17/56] removed multi_* methods --- CHANGELOG.md | 3 + README.md | 2 - src/main.rs | 8 +- src/nwc.rs | 30 +++---- src/nwc_keysend.rs | 36 +------- src/nwc_lookups.rs | 4 +- src/nwc_pay.rs | 35 -------- tests/test_cln-nip47.py | 192 +--------------------------------------- 8 files changed, 23 insertions(+), 287 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91f2c5e..f0fd46e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ - holdinvoice methods: ``make_hold_invoice``, ``cancel_hold_invoice``, ``settle_hold_invoice`` - holdinvoice notification: ``hold_invoice_accepted`` +### Removed +- ``multi_pay_invoice`` and ``multi_pay_keysend``, they were removed from the spec + ## [0.1.9] 2026-04-23 ### Fixed diff --git a/README.md b/README.md index 02fbb10..eb2425b 100644 --- a/README.md +++ b/README.md @@ -132,9 +132,7 @@ For methods or notifications related to holdinvoices you need v0.3.2+ of [hold]( ## Supported NWC methods * ``pay_invoice`` -* ``multi_pay_invoice`` * ``pay_keysend`` (no ``preimage`` in request allowed since CLN only supports generating it itself) -* ``multi_pay_keysend`` (no ``preimage`` in request allowed since CLN only supports generating it itself) * ``make_invoice`` * ``lookup_invoice`` * ``list_transactions`` diff --git a/src/main.rs b/src/main.rs index bb172bf..dd70aad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -64,12 +64,8 @@ pub const WALLET_READ_METHODS: [nip47::Method; 5] = [ nip47::Method::GetBalance, nip47::Method::GetInfo, ]; -pub const WALLET_PAY_METHODS: [nip47::Method; 4] = [ - nip47::Method::PayInvoice, - nip47::Method::MultiPayInvoice, - nip47::Method::PayKeysend, - nip47::Method::MultiPayKeysend, -]; +pub const WALLET_PAY_METHODS: [nip47::Method; 2] = + [nip47::Method::PayInvoice, nip47::Method::PayKeysend]; pub const WALLET_HOLD_METHODS: [nip47::Method; 3] = [ nip47::Method::MakeHoldInvoice, nip47::Method::CancelHoldInvoice, diff --git a/src/nwc.rs b/src/nwc.rs index 423e0cf..16c00c8 100644 --- a/src/nwc.rs +++ b/src/nwc.rs @@ -37,9 +37,9 @@ use crate::{ }, nwc_info::get_info_response, nwc_invoice::make_invoice_response, - nwc_keysend::{multi_pay_keysend, pay_keysend_response}, + nwc_keysend::pay_keysend_response, nwc_lookups::{list_transactions_response, lookup_invoice_response}, - nwc_pay::{multi_pay_invoice, pay_invoice_response}, + nwc_pay::pay_invoice_response, structs::{NwcStore, PluginState}, tasks::budget_task, util::{build_capabilities, build_notifications_vec, is_read_only_nwc}, @@ -236,7 +236,7 @@ async fn nwc_request_handler( notification: ClientNotification, nostr_client: &client::Client, plugin: &Plugin, - label: &String, + label: &str, wallet_keys: &Keys, client_pubkey: PublicKey, ) -> Result<(), Box> { @@ -249,8 +249,8 @@ async fn nwc_request_handler( ClientNotification::Message { relay_url: _, message: _, - } => return Ok(()), - ClientNotification::Shutdown => return Ok(()), + } + | ClientNotification::Shutdown => return Ok(()), }; if let Some(expi) = event.tags.expiration() { @@ -261,20 +261,14 @@ async fn nwc_request_handler( log::debug!("relay_url:{relay_url} subscription_id:{subscription_id} {event:?}"); let mut use_nip44 = check_nip44_support(&event); - let request = decrypt_request(&event.content, &wallet_keys, &client_pubkey, &mut use_nip44)?; + let request = decrypt_request(&event.content, wallet_keys, &client_pubkey, &mut use_nip44)?; let responses = match request.params { nip47::RequestParams::PayInvoice(pay_invoice_request) => { - pay_invoice_response(plugin.clone(), pay_invoice_request, &label).await - } - nip47::RequestParams::MultiPayInvoice(multi_pay_invoice_request) => { - multi_pay_invoice(plugin.clone(), multi_pay_invoice_request, &label).await + pay_invoice_response(plugin.clone(), pay_invoice_request, label).await } nip47::RequestParams::PayKeysend(pay_keysend_request) => { - pay_keysend_response(plugin.clone(), pay_keysend_request, &label).await - } - nip47::RequestParams::MultiPayKeysend(multi_pay_keysend_request) => { - multi_pay_keysend(plugin.clone(), multi_pay_keysend_request, &label).await + pay_keysend_response(plugin.clone(), pay_keysend_request, label).await } nip47::RequestParams::MakeInvoice(make_invoice_request) => { make_invoice_response(plugin.clone(), make_invoice_request).await @@ -285,8 +279,8 @@ async fn nwc_request_handler( nip47::RequestParams::ListTransactions(list_transactions_request) => { list_transactions_response(plugin.clone(), list_transactions_request).await } - nip47::RequestParams::GetBalance => get_balance_response(plugin.clone(), &label).await, - nip47::RequestParams::GetInfo => get_info_response(plugin.clone(), &label).await, + nip47::RequestParams::GetBalance => get_balance_response(plugin.clone(), label).await, + nip47::RequestParams::GetInfo => get_info_response(plugin.clone(), label).await, nip47::RequestParams::MakeHoldInvoice(make_hold_invoice_request) => { make_hold_invoice_response(plugin.clone(), make_hold_invoice_request).await } @@ -299,7 +293,7 @@ async fn nwc_request_handler( }; for (response, id) in responses { let content = - match encrypt_response_content(&response, &wallet_keys, &client_pubkey, use_nip44) { + match encrypt_response_content(&response, wallet_keys, &client_pubkey, use_nip44) { Ok(o) => o, Err(e) => { log::warn!("{e}"); @@ -308,7 +302,7 @@ async fn nwc_request_handler( }; let response_event = - match build_response_event(event.id, content, &wallet_keys, client_pubkey, id) { + match build_response_event(event.id, content, wallet_keys, client_pubkey, id) { Ok(o) => o, Err(e) => { log::warn!("Error signing reponse event! {e}"); diff --git a/src/nwc_keysend.rs b/src/nwc_keysend.rs index fbbe1a0..bc49ec2 100644 --- a/src/nwc_keysend.rs +++ b/src/nwc_keysend.rs @@ -1,4 +1,4 @@ -use std::{str::FromStr, time::Duration}; +use std::str::FromStr; use cln_plugin::Plugin; use cln_rpc::{ @@ -6,7 +6,6 @@ use cln_rpc::{ primitives::{Amount, PublicKey, TlvEntry, TlvStream}, }; use nostr::nips::nip47; -use tokio::time; use crate::{ structs::PluginState, @@ -149,36 +148,3 @@ async fn pay_keysend( }, } } - -pub async fn multi_pay_keysend( - plugin: Plugin, - params: nip47::MultiPayKeysendRequest, - label: &str, -) -> Vec<(nip47::Response, Option)> { - let mut responses = Vec::new(); - for pay in params.keysends { - let result = pay_keysend(plugin.clone(), pay.clone(), label).await; - let id = if let Some(i) = pay.id { i } else { pay.pubkey }; - let response_res = match result { - Ok(resp) => ( - nip47::Response { - result_type: nip47::Method::MultiPayKeysend, - error: None, - result: Some(nip47::ResponseResult::MultiPayKeysend(resp)), - }, - Some(id), - ), - Err(e) => ( - nip47::Response { - result_type: nip47::Method::MultiPayKeysend, - error: Some(e), - result: None, - }, - Some(id), - ), - }; - responses.push(response_res); - time::sleep(Duration::from_millis(100)).await; - } - responses -} diff --git a/src/nwc_lookups.rs b/src/nwc_lookups.rs index cef844c..5b5090c 100644 --- a/src/nwc_lookups.rs +++ b/src/nwc_lookups.rs @@ -275,7 +275,7 @@ async fn lookup_invoice( let state = match hold_invoice.state() { hold::InvoiceState::Unpaid => nip47::TransactionState::Pending, - hold::InvoiceState::Accepted => nip47::TransactionState::Pending, // TODO ACCEPTED STATE + hold::InvoiceState::Accepted => nip47::TransactionState::Accepted, hold::InvoiceState::Paid => nip47::TransactionState::Settled, hold::InvoiceState::Cancelled => nip47::TransactionState::Expired, }; @@ -462,7 +462,7 @@ async fn list_transactions( let state = match hold_invoice.state() { hold::InvoiceState::Unpaid => nip47::TransactionState::Pending, - hold::InvoiceState::Accepted => nip47::TransactionState::Pending, // TODO: ACCEPTED STATE + hold::InvoiceState::Accepted => nip47::TransactionState::Accepted, hold::InvoiceState::Paid => nip47::TransactionState::Settled, hold::InvoiceState::Cancelled => nip47::TransactionState::Expired, }; diff --git a/src/nwc_pay.rs b/src/nwc_pay.rs index 87b2733..a313720 100644 --- a/src/nwc_pay.rs +++ b/src/nwc_pay.rs @@ -1,5 +1,3 @@ -use std::time::Duration; - use cln_plugin::Plugin; use cln_rpc::{ model::{ @@ -11,7 +9,6 @@ use cln_rpc::{ RpcError, }; use nostr::nips::nip47; -use tokio::time; use crate::{ structs::{NwcStore, PluginState, NOT_INV_ERR}, @@ -380,35 +377,3 @@ async fn pay_with_legacy_full( ) .await } - -pub async fn multi_pay_invoice( - plugin: Plugin, - params: nip47::MultiPayInvoiceRequest, - label: &str, -) -> Vec<(nip47::Response, Option)> { - let mut responses = Vec::new(); - for pay in params.invoices { - let result = pay_invoice(plugin.clone(), pay, label).await; - let response_res = match result { - Ok((resp, id)) => ( - nip47::Response { - result_type: nip47::Method::MultiPayInvoice, - error: None, - result: Some(nip47::ResponseResult::MultiPayInvoice(resp)), - }, - id, - ), - Err((e, id)) => ( - nip47::Response { - result_type: nip47::Method::MultiPayInvoice, - error: Some(e), - result: None, - }, - id, - ), - }; - responses.push(response_res); - time::sleep(Duration::from_millis(100)).await; - } - responses -} diff --git a/tests/test_cln-nip47.py b/tests/test_cln-nip47.py index 6caba46..03210a9 100755 --- a/tests/test_cln-nip47.py +++ b/tests/test_cln-nip47.py @@ -15,7 +15,6 @@ from pyln.testing.utils import RpcError, wait_for, TIMEOUT from util import generate_random_label, get_plugin, get_hold # noqa: F401 from nostr_sdk import ( - Alphabet, Client, RelayUrl, EventBuilder, @@ -34,7 +33,6 @@ from nostr_sdk import ( Nwc, PayInvoiceRequest, PayKeysendRequest, - SingleLetterTag, Tag, TagKind, Method, @@ -223,9 +221,7 @@ async def test_get_info(nostr_relay, node_factory, get_plugin): # noqa: F811 Method.GET_BALANCE, Method.GET_INFO, Method.PAY_INVOICE, - Method.MULTI_PAY_INVOICE, Method.PAY_KEYSEND, - Method.MULTI_PAY_KEYSEND, ] assert get_info.network == "regtest" assert get_info.notifications == ["payment_received", "payment_sent"] @@ -255,9 +251,7 @@ async def test_get_info(nostr_relay, node_factory, get_plugin): # noqa: F811 Method.GET_BALANCE, Method.GET_INFO, Method.PAY_INVOICE, - Method.MULTI_PAY_INVOICE, Method.PAY_KEYSEND, - Method.MULTI_PAY_KEYSEND, ] assert get_info.network == "regtest" assert get_info.notifications == [] @@ -265,7 +259,7 @@ async def test_get_info(nostr_relay, node_factory, get_plugin): # noqa: F811 assert ( info_event.content() - == "make_invoice lookup_invoice list_transactions get_balance get_info pay_invoice multi_pay_invoice pay_keysend multi_pay_keysend" + == "make_invoice lookup_invoice list_transactions get_balance get_info pay_invoice pay_keysend" ) assert ( info_event.tags().find(TagKind.UNKNOWN("encryption")).content() @@ -450,99 +444,6 @@ async def test_pay_keysend(nostr_relay, node_factory, get_plugin): # noqa: F811 ) -@pytest.mark.asyncio -async def test_multi_keysend(nostr_relay, node_factory, get_plugin): # noqa: F811 - url = nostr_relay - l1, l2, l3 = node_factory.line_graph( - 3, - wait_for_announce=True, - opts=[ - { - "log-level": "debug", - "plugin": get_plugin, - "nip47-relays": url, - "broken_log": r"Relay receiver exited with error|Connection failed", - }, - {"log-level": "debug"}, - {"log-level": "debug"}, - ], - ) - uri_res = l1.rpc.call("nip47-create", ["test1", 3010]) - uri_str = uri_res["uri"] - client_pubkey = PublicKey.parse(uri_res["clientkey_public"]) - LOGGER.info(uri_str) - uri = NostrWalletConnectUri.parse(uri_str) - content = { - "method": "multi_pay_keysend", - "params": { - "keysends": [ - {"id": "4da52c32a1", "pubkey": l2.info["id"], "amount": 1000}, - {"id": "3da52c32a1", "pubkey": l3.info["id"], "amount": 2000}, - ], - }, - } - content = json.dumps(content) - signer = NostrSigner.keys(Keys(uri.secret())) - encrypted_content = await signer.nip04_encrypt(uri.public_key(), content) - event = ( - await EventBuilder(Kind(23194), encrypted_content) - .tags([Tag.public_key(uri.public_key())]) - .sign(signer) - ) - client = Client(signer) - await client.add_relay(RelayUrl.parse(url)) - await client.connect() - (responses1, _res) = await fetch_event_responses( - client, client_pubkey, 23195, client.send_event(event), 2 - ) - - content = { - "method": "multi_pay_keysend", - "params": { - "keysends": [ - {"id": "5da52c32a1", "pubkey": l2.info["id"], "amount": 5}, - {"id": "2da52c32a1", "pubkey": l3.info["id"], "amount": 5}, - ], - }, - } - content = json.dumps(content) - encrypted_content = await signer.nip04_encrypt(uri.public_key(), content) - event = ( - await EventBuilder(Kind(23194), encrypted_content) - .tags([Tag.public_key(uri.public_key())]) - .sign(signer) - ) - (responses2, _res) = await fetch_event_responses( - client, client_pubkey, 23195, client.send_event(event), 2 - ) - - reponses = responses1 + responses2 - - error_events = [] - success_events = [] - for event in reponses: - LOGGER.info(event) - assert event.tags().find( - TagKind.SINGLE_LETTER(SingleLetterTag.lowercase(Alphabet.D)) - ) - content = await signer.nip04_decrypt(uri.public_key(), event.content()) - content = json.loads(content) - if "result" in content and content["result"] is not None: - success_events.append(content) - if "error" in content and content["error"] is not None: - error_events.append(content) - - assert len(success_events) == 3 - assert len(error_events) == 1 - for content in success_events: - assert content["result_type"] == "multi_pay_keysend" - assert content["result"]["preimage"] is not None - for content in error_events: - assert content["result_type"] == "multi_pay_keysend" - assert content["error"]["message"] == "Payment exceeds budget!" - assert content["error"]["code"] == "QUOTA_EXCEEDED" - - @pytest.mark.asyncio async def test_lookup_invoice(nostr_relay, node_factory, get_plugin): # noqa: F811 url = nostr_relay @@ -1104,91 +1005,6 @@ async def test_pay_invoice(nostr_relay, node_factory, get_plugin): # noqa: F811 ) -@pytest.mark.asyncio -async def test_multi_pay(nostr_relay, node_factory, get_plugin): # noqa: F811 - url = nostr_relay - l1, l2 = node_factory.line_graph( - 2, - wait_for_announce=True, - opts=[ - { - "log-level": "debug", - "plugin": get_plugin, - "nip47-relays": url, - "broken_log": r"Relay receiver exited with error|Connection failed", - }, - {"log-level": "debug"}, - ], - ) - uri_res = l1.rpc.call("nip47-create", ["test1", 30000]) - uri_str = uri_res["uri"] - client_pubkey = PublicKey.parse(uri_res["clientkey_public"]) - LOGGER.info(uri_str) - uri = NostrWalletConnectUri.parse(uri_str) - invoice1 = l2.rpc.call( - "invoice", - {"label": generate_random_label(), "description": "test1", "amount_msat": 3000}, - ) - invoice2 = l2.rpc.call( - "invoice", - {"label": generate_random_label(), "description": "test2", "amount_msat": 4000}, - ) - invoice3 = l2.rpc.call( - "invoice", - { - "label": generate_random_label(), - "description": "test3", - "amount_msat": 23001, - }, - ) - content = { - "method": "multi_pay_invoice", - "params": { - "invoices": [ - {"id": "4da52c32a1", "invoice": invoice1["bolt11"]}, - {"id": "3da52c32a1", "invoice": invoice2["bolt11"]}, - {"id": "af3g2k2o11", "invoice": invoice3["bolt11"]}, - ], - }, - } - content = json.dumps(content) - signer = NostrSigner.keys(Keys(uri.secret())) - encrypted_content = await signer.nip44_encrypt(uri.public_key(), content) - request_event = ( - await EventBuilder(Kind(23194), encrypted_content) - .tags([Tag.public_key(uri.public_key())]) - .sign(signer) - ) - client = Client(signer) - await client.add_relay(RelayUrl.parse(url)) - await client.connect() - - (responses, _res) = await fetch_event_responses( - client, client_pubkey, 23195, client.send_event(request_event), 3 - ) - - success_pays = [] - error_pays = [] - for response in responses: - d_tag = response.tags().find( - TagKind.SINGLE_LETTER(SingleLetterTag.lowercase(Alphabet.D)) - ) - content = await signer.nip44_decrypt(uri.public_key(), response.content()) - content = json.loads(content) - assert content["result_type"] == "multi_pay_invoice" - if "result" in content and content["result"] is not None: - assert d_tag is not None - assert content["result"]["preimage"] is not None - success_pays.append(content) - if "error" in content and content["error"] is not None: - assert d_tag.content() == "af3g2k2o11" - assert content["error"]["code"] == "QUOTA_EXCEEDED" - assert content["error"]["message"] == "Payment exceeds budget!" - error_pays.append(content) - assert len(success_pays) == 2 - assert len(error_pays) == 1 - - @pytest.mark.asyncio async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 url = nostr_relay @@ -1408,16 +1224,14 @@ async def test_budget_command(nostr_relay, node_factory, get_plugin): # noqa: F Method.GET_BALANCE, Method.GET_INFO, Method.PAY_INVOICE, - Method.MULTI_PAY_INVOICE, Method.PAY_KEYSEND, - Method.MULTI_PAY_KEYSEND, ] info_event = await fetch_info_event(client, uri) assert ( info_event.content() - == "make_invoice lookup_invoice list_transactions get_balance get_info pay_invoice multi_pay_invoice pay_keysend multi_pay_keysend notifications" + == "make_invoice lookup_invoice list_transactions get_balance get_info pay_invoice pay_keysend notifications" ) assert ( info_event.tags().find(TagKind.UNKNOWN("encryption")).content() @@ -1974,7 +1788,7 @@ async def test_hold_invoice( info_event = await fetch_info_event(client, uri) assert ( info_event.content() - == "make_invoice lookup_invoice list_transactions get_balance get_info pay_invoice multi_pay_invoice pay_keysend multi_pay_keysend make_hold_invoice cancel_hold_invoice settle_hold_invoice notifications" + == "make_invoice lookup_invoice list_transactions get_balance get_info pay_invoice pay_keysend make_hold_invoice cancel_hold_invoice settle_hold_invoice notifications" ) assert ( info_event.tags().find(TagKind.UNKNOWN("encryption")).content() From b16db826931185890c5bd870b0b19bd951612501 Mon Sep 17 00:00:00 2001 From: daywalker90 <8257956+daywalker90@users.noreply.github.com> Date: Tue, 12 May 2026 20:17:57 +0200 Subject: [PATCH 18/56] more nostr v0.45 prep --- Cargo.lock | 83 +++++++++++++++++++++++++++------------- Cargo.toml | 4 +- src/nwc.rs | 32 ++++++---------- src/nwc_notifications.rs | 4 +- 4 files changed, 71 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 696565d..81fd1eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,11 +69,9 @@ dependencies = [ [[package]] name = "async-wsocket" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c92385c7c8b3eb2de1b78aeca225212e4c9a69a78b802832759b108681a5069" +version = "0.13.1" +source = "git+https://github.com/shadowylab/async-wsocket?rev=0fed6c9c6aec7393ee0e9cf3933d76914ab427d3#0fed6c9c6aec7393ee0e9cf3933d76914ab427d3" dependencies = [ - "async-utility", "futures", "futures-util", "js-sys", @@ -83,6 +81,7 @@ dependencies = [ "tokio-tungstenite", "url", "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", ] @@ -221,6 +220,12 @@ version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.11.1" @@ -418,6 +423,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "faster-hex" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73" +dependencies = [ + "heapless", + "serde", +] + [[package]] name = "fastrand" version = "2.4.1" @@ -622,6 +637,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -637,6 +661,16 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -903,15 +937,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "itertools" version = "0.14.0" @@ -1040,7 +1065,7 @@ checksum = "f0efe882e02d206d8d279c20eb40e03baf7cb5136a1476dc084a324fbc3ec42d" [[package]] name = "nostr" version = "0.44.1" -source = "git+https://github.com/rust-nostr/nostr.git?rev=bf3028eb11b4e324c0c660b4e4c83348cdcc00c2#bf3028eb11b4e324c0c660b4e4c83348cdcc00c2" +source = "git+https://github.com/rust-nostr/nostr.git?rev=6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6#6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6" dependencies = [ "aes", "base64", @@ -1050,33 +1075,30 @@ dependencies = [ "cbc", "chacha20", "chacha20poly1305", - "hex", - "instant", - "once_cell", + "faster-hex", "rand", "scrypt", "secp256k1", "serde", "serde_json", "unicode-normalization", + "universal-time", "url", ] [[package]] name = "nostr-database" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr.git?rev=bf3028eb11b4e324c0c660b4e4c83348cdcc00c2#bf3028eb11b4e324c0c660b4e4c83348cdcc00c2" +source = "git+https://github.com/rust-nostr/nostr.git?rev=6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6#6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6" dependencies = [ "btreecap", - "lru", "nostr", - "tokio", ] [[package]] name = "nostr-gossip" version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr.git?rev=bf3028eb11b4e324c0c660b4e4c83348cdcc00c2#bf3028eb11b4e324c0c660b4e4c83348cdcc00c2" +source = "git+https://github.com/rust-nostr/nostr.git?rev=6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6#6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6" dependencies = [ "nostr", ] @@ -1084,12 +1106,12 @@ dependencies = [ [[package]] name = "nostr-sdk" version = "0.44.1" -source = "git+https://github.com/rust-nostr/nostr.git?rev=bf3028eb11b4e324c0c660b4e4c83348cdcc00c2#bf3028eb11b4e324c0c660b4e4c83348cdcc00c2" +source = "git+https://github.com/rust-nostr/nostr.git?rev=6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6#6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6" dependencies = [ "async-utility", "async-wsocket", + "faster-hex", "futures", - "hex", "lru", "negentropy", "nostr", @@ -1098,6 +1120,7 @@ dependencies = [ "tokio", "tokio-stream", "tracing", + "universal-time", ] [[package]] @@ -1838,9 +1861,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", @@ -2034,9 +2057,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", @@ -2094,6 +2117,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "universal-time" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f942cacf37a673350e8f252dff4691fb8e525a5fff6e1f2c4b6c2f3b8349248d" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index d7be3be..a0bb620 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,8 @@ cln-plugin = "0.6" # cln-plugin = { path = "../lightning/plugins/", version = "^0.6" } parking_lot = "0.12" -nostr-sdk = { git = "https://github.com/rust-nostr/nostr.git", rev = "bf3028eb11b4e324c0c660b4e4c83348cdcc00c2" } -nostr = { git = "https://github.com/rust-nostr/nostr.git", rev = "bf3028eb11b4e324c0c660b4e4c83348cdcc00c2", features = [ +nostr-sdk = { git = "https://github.com/rust-nostr/nostr.git", rev = "6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6" } +nostr = { git = "https://github.com/rust-nostr/nostr.git", rev = "6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6", features = [ "nip47", "nip04", "nip44", diff --git a/src/nwc.rs b/src/nwc.rs index 16c00c8..b319853 100644 --- a/src/nwc.rs +++ b/src/nwc.rs @@ -1,10 +1,9 @@ -use std::{borrow::Cow, time::Duration}; +use std::time::Duration; use anyhow::anyhow; use cln_plugin::Plugin; use futures::StreamExt; use nostr::{ - Alphabet, Event, EventBuilder, EventId, @@ -13,12 +12,10 @@ use nostr::{ Kind, PublicKey, SecretKey, - SignerError, - SingleLetterTag, Tag, - TagKind, Timestamp, nips::{nip04, nip44, nip47}, + signer::SignerError, }; use nostr_sdk::{ client::{self, Client, ClientNotification, Error}, @@ -149,7 +146,7 @@ pub async fn run_nwc( .await { log::warn!("NWC handler for `{label_clone}` had an error: {e}"); - }; + } } {}; @@ -254,7 +251,7 @@ async fn nwc_request_handler( }; if let Some(expi) = event.tags.expiration() { - if *expi < Timestamp::now() { + if expi < Timestamp::now() { return Ok(()); } } @@ -335,13 +332,12 @@ async fn nwc_request_handler( } fn check_nip44_support(event: &Event) -> bool { - if let Some(encryption_tag) = event - .tags - .find(TagKind::Custom(Cow::Borrowed("encryption"))) - { - if let Some(enc_tag_content) = encryption_tag.content() { - if enc_tag_content.contains("nip44_v2") { - return true; + for tag in event.tags.iter() { + if tag.kind() == "encryption" { + if let Some(enc_tag_content) = tag.content() { + if enc_tag_content.contains("nip44_v2") { + return true; + } } } } @@ -442,13 +438,7 @@ fn build_response_event( .tag(Tag::event(event_id)) .tag(Tag::public_key(client_pubkey)); if let Some(i) = id { - response_builder = response_builder.tag(Tag::custom( - TagKind::SingleLetter(SingleLetterTag { - character: Alphabet::D, - uppercase: false, - }), - vec![i], - )); + response_builder = response_builder.tag(Tag::identifier(i)); } match response_builder.sign_with_keys(wallet_keys) { Ok(o) => Ok(o), diff --git a/src/nwc_notifications.rs b/src/nwc_notifications.rs index 8f77a99..4527a5c 100644 --- a/src/nwc_notifications.rs +++ b/src/nwc_notifications.rs @@ -342,7 +342,7 @@ async fn send_notification( let content_encrypted_nip04 = signer.nip04_encrypt(client_pubkey, notification).await?; let event_nip04 = EventBuilder::new(Kind::from_u16(23196), content_encrypted_nip04) .tag(Tag::public_key(*client_pubkey)) - .sign(&signer) + .sign_async(&signer) .await?; let nip04_result = client.send_event(&event_nip04).await?; if nip04_result.success.is_empty() { @@ -360,7 +360,7 @@ async fn send_notification( let content_encrypted_nip44 = signer.nip44_encrypt(client_pubkey, notification).await?; let event_nip44 = EventBuilder::new(Kind::from_u16(23197), content_encrypted_nip44) .tag(Tag::public_key(*client_pubkey)) - .sign(&signer) + .sign_async(&signer) .await?; let nip44_result = client.send_event(&event_nip44).await?; if nip44_result.success.is_empty() { From af306edd3a8a0b1b080a115e48100241f3278b20 Mon Sep 17 00:00:00 2001 From: daywalker90 <8257956+daywalker90@users.noreply.github.com> Date: Sun, 17 May 2026 19:46:22 +0200 Subject: [PATCH 19/56] use xkeysend on CLN v26.06+ --- CHANGELOG.md | 3 + src/nwc_keysend.rs | 204 ++++++++++++++++++++++++++++++--------------- src/nwc_pay.rs | 9 +- 3 files changed, 147 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0fd46e..08b1dbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ - holdinvoice methods: ``make_hold_invoice``, ``cancel_hold_invoice``, ``settle_hold_invoice`` - holdinvoice notification: ``hold_invoice_accepted`` +### Changed +- `pay_keysend` will use the new CLN `xkeysend` command using CLN v26.06+ + ### Removed - ``multi_pay_invoice`` and ``multi_pay_keysend``, they were removed from the spec diff --git a/src/nwc_keysend.rs b/src/nwc_keysend.rs index bc49ec2..c5c5673 100644 --- a/src/nwc_keysend.rs +++ b/src/nwc_keysend.rs @@ -1,15 +1,15 @@ -use std::str::FromStr; +use std::{collections::HashMap, str::FromStr}; use cln_plugin::Plugin; use cln_rpc::{ - model::requests::KeysendRequest, + model::requests::{KeysendRequest, XkeysendRequest}, primitives::{Amount, PublicKey, TlvEntry, TlvStream}, }; use nostr::nips::nip47; use crate::{ structs::PluginState, - util::{budget_amount_check, load_nwc_store, update_nwc_store}, + util::{at_or_above_version, budget_amount_check, load_nwc_store, update_nwc_store}, }; pub async fn pay_keysend_response( @@ -76,75 +76,147 @@ async fn pay_keysend( message: e.to_string(), })?; - let mut extratlvs = TlvStream { - entries: Vec::new(), - }; - for tlv in params.tlv_records { - extratlvs.entries.push(TlvEntry { - typ: tlv.tlv_type, - value: tlv.value.as_bytes().to_owned(), - }); - } - let extratlvs = if extratlvs.entries.is_empty() { - None - } else { - Some(extratlvs) - }; + let my_cln_version = plugin.state().config.lock().my_cln_version.clone(); - match rpc - .call_typed(&KeysendRequest { - exemptfee: None, - extratlvs, - label: None, - maxdelay: None, - maxfee: None, - maxfeepercent: None, - retry_for: None, - routehints: None, - amount_msat: Amount::from_msat(params.amount), - destination: pubkey, - }) - .await - { - Ok(o) => { - if let Some(ref mut bdg) = nwc_store.budget_msat { - *bdg = bdg.saturating_sub(o.amount_sent_msat.msat()); - update_nwc_store(&mut rpc, label, nwc_store) - .await - .map_err(|e| nip47::NIP47Error { + if at_or_above_version(&my_cln_version, "26.06").map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: e.to_string(), + })? { + let mut extratlvs = HashMap::with_capacity(params.tlv_records.len()); + for tlv in params.tlv_records { + extratlvs.insert(tlv.tlv_type.to_string(), tlv.value); + } + let extratlvs = if extratlvs.is_empty() { + None + } else { + Some(extratlvs) + }; + + match rpc + .call_typed(&XkeysendRequest { + extratlvs, + label: None, + maxdelay: None, + maxfee: None, + retry_for: None, + layers: None, + amount_msat: Amount::from_msat(params.amount), + destination: pubkey, + }) + .await + { + Ok(o) => { + if let Some(ref mut bdg) = nwc_store.budget_msat { + *bdg = bdg.saturating_sub(o.amount_sent_msat.msat()); + update_nwc_store(&mut rpc, label, nwc_store) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + } + + let preimage = hex::encode(o.payment_preimage.to_vec()); + + let fees_paid = o.amount_sent_msat.msat() - o.amount_msat.msat(); + + Ok(nip47::PayKeysendResponse { + preimage, + fees_paid: Some(fees_paid), + }) + } + Err(e) => match e.code { + Some(c) => match c { + 203 | 205 | 207 | 219 => Err(nip47::NIP47Error { + code: nip47::ErrorCode::PaymentFailed, + message: e.to_string(), + }), + 209 => Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: e.to_string(), + }), + _ => Err(nip47::NIP47Error { code: nip47::ErrorCode::Internal, message: e.to_string(), - })?; - } - - let preimage = hex::encode(o.payment_preimage.to_vec()); - - let fees_paid = o.amount_sent_msat.msat() - o.amount_msat.msat(); - - Ok(nip47::PayKeysendResponse { - preimage, - fees_paid: Some(fees_paid), - }) - } - Err(e) => match e.code { - Some(c) => match c { - 203 | 205 | 210 => Err(nip47::NIP47Error { - code: nip47::ErrorCode::PaymentFailed, - message: e.to_string(), - }), - 206 => Err(nip47::NIP47Error { - code: nip47::ErrorCode::InsufficientBalance, - message: e.to_string(), - }), - _ => Err(nip47::NIP47Error { + }), + }, + None => Err(nip47::NIP47Error { code: nip47::ErrorCode::Internal, message: e.to_string(), }), }, - None => Err(nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - }), - }, + } + } else { + let mut extratlvs = TlvStream { + entries: Vec::new(), + }; + for tlv in params.tlv_records { + extratlvs.entries.push(TlvEntry { + typ: tlv.tlv_type, + value: tlv.value.as_bytes().to_owned(), + }); + } + let extratlvs = if extratlvs.entries.is_empty() { + None + } else { + Some(extratlvs) + }; + + match rpc + .call_typed(&KeysendRequest { + exemptfee: None, + extratlvs, + label: None, + maxdelay: None, + maxfee: None, + maxfeepercent: None, + retry_for: None, + routehints: None, + amount_msat: Amount::from_msat(params.amount), + destination: pubkey, + }) + .await + { + Ok(o) => { + if let Some(ref mut bdg) = nwc_store.budget_msat { + *bdg = bdg.saturating_sub(o.amount_sent_msat.msat()); + update_nwc_store(&mut rpc, label, nwc_store) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + } + + let preimage = hex::encode(o.payment_preimage.to_vec()); + + let fees_paid = o.amount_sent_msat.msat() - o.amount_msat.msat(); + + Ok(nip47::PayKeysendResponse { + preimage, + fees_paid: Some(fees_paid), + }) + } + Err(e) => match e.code { + Some(c) => match c { + 203 | 205 | 210 => Err(nip47::NIP47Error { + code: nip47::ErrorCode::PaymentFailed, + message: e.to_string(), + }), + 206 => Err(nip47::NIP47Error { + code: nip47::ErrorCode::InsufficientBalance, + message: e.to_string(), + }), + _ => Err(nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + }), + }, + None => Err(nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + }), + }, + } } } diff --git a/src/nwc_pay.rs b/src/nwc_pay.rs index a313720..4f4e859 100644 --- a/src/nwc_pay.rs +++ b/src/nwc_pay.rs @@ -1,17 +1,17 @@ use cln_plugin::Plugin; use cln_rpc::{ + ClnRpc, + RpcError, model::{ requests::{DecodeRequest, PayRequest, XpayRequest}, responses::DecodeResponse, }, primitives::{Amount, Secret}, - ClnRpc, - RpcError, }; use nostr::nips::nip47; use crate::{ - structs::{NwcStore, PluginState, NOT_INV_ERR}, + structs::{NOT_INV_ERR, NwcStore, PluginState}, util::{at_or_above_version, budget_amount_check, load_nwc_store, update_nwc_store}, }; @@ -316,6 +316,9 @@ async fn pay_with_xpay_full( layers: None, invstring: params.invoice, payer_note: None, + dev_use_shadow: None, + label: None, + localinvreqid: None, }) .await .map_err(|e| map_cln_error_to_nip47(&e, id, true))?; From fb972c5ed1133d1af953401581b7b8cba56bcf44 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 26 Jul 2026 13:14:09 +0200 Subject: [PATCH 20/56] CI: use specific python versions per CLN version --- .github/workflows/ci.yml | 24 ++++++++++--------- .github/workflows/latest_v25.05.yml | 14 ----------- .github/workflows/latest_v25.09.yml | 6 +++-- .github/workflows/latest_v25.12.yml | 8 ++++--- .github/workflows/latest_v26.04.yml | 2 ++ .github/workflows/latest_v26.06.yml | 16 +++++++++++++ .github/workflows/master_v25.09.yml | 6 +++-- .github/workflows/master_v25.12.yml | 6 +++-- .github/workflows/master_v26.04.yml | 2 ++ .../{master_v25.05.yml => master_v26.06.yml} | 8 ++++--- .github/workflows/release.yml | 12 ---------- 11 files changed, 55 insertions(+), 49 deletions(-) delete mode 100644 .github/workflows/latest_v25.05.yml create mode 100644 .github/workflows/latest_v26.06.yml rename .github/workflows/{master_v25.05.yml => master_v26.06.yml} (70%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5275ba9..b15911b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,12 @@ on: tagged-release: required: true type: boolean + python-version-1: + required: true + type: string + python-version-2: + required: true + type: string jobs: build: @@ -27,7 +33,7 @@ jobs: bitcoind-version: ["31.0"] experimental: [1] deprecated: [0] - python-version: ["3.10", "3.14"] + python-version: [ "${{ inputs.python-version-1 }}", "${{ inputs.python-version-2 }}"] os: ["ubuntu-latest"] runs-on: ${{ matrix.os }} @@ -119,25 +125,21 @@ jobs: key: ${{ steps.cache-cln.outputs.cache-primary-key }} - name: Set up Rust - if: ${{ inputs.tagged-release == false}} + if: ${{ inputs.tagged-release == false }} uses: dtolnay/rust-toolchain@stable - - name: Set up protoc - if: ${{ inputs.tagged-release == false}} - uses: arduino/setup-protoc@v3 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install cargo-binstall - if: ${{ inputs.tagged-release == false}} + if: ${{ inputs.tagged-release == false }} uses: taiki-e/install-action@cargo-binstall - name: Install cargo-msrv - if: ${{ inputs.tagged-release == false}} + if: ${{ inputs.tagged-release == false }} + env: + GITHUB_TOKEN: ${{ github.token }} run: cargo binstall --no-confirm cargo-msrv - name: Verify MSRV - if: ${{ inputs.tagged-release == false}} + if: ${{ inputs.tagged-release == false }} run: cargo msrv verify - name: Get plugin binary diff --git a/.github/workflows/latest_v25.05.yml b/.github/workflows/latest_v25.05.yml deleted file mode 100644 index b5502c9..0000000 --- a/.github/workflows/latest_v25.05.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: Latest release on CLN v25.05.x - -on: - release: - types: [published, edited] - workflow_dispatch: - -jobs: - call-ci: - uses: ./.github/workflows/ci.yml - with: - cln-version: "v25.05" - pyln-version: "25.5" - tagged-release: true \ No newline at end of file diff --git a/.github/workflows/latest_v25.09.yml b/.github/workflows/latest_v25.09.yml index 2926b68..d96d48d 100644 --- a/.github/workflows/latest_v25.09.yml +++ b/.github/workflows/latest_v25.09.yml @@ -9,6 +9,8 @@ jobs: call-ci: uses: ./.github/workflows/ci.yml with: - cln-version: "v25.09.2" - pyln-version: "25.9.2" + cln-version: "v25.09.3" + pyln-version: "25.9.3" tagged-release: true + python-version-1: "3.10" + python-version-2: "3.12" diff --git a/.github/workflows/latest_v25.12.yml b/.github/workflows/latest_v25.12.yml index 268f5f6..3a86b18 100644 --- a/.github/workflows/latest_v25.12.yml +++ b/.github/workflows/latest_v25.12.yml @@ -9,6 +9,8 @@ jobs: call-ci: uses: ./.github/workflows/ci.yml with: - cln-version: "v25.12" - pyln-version: "25.12" - tagged-release: true \ No newline at end of file + cln-version: "v25.12.1" + pyln-version: "25.12.1" + tagged-release: true + python-version-1: "3.10" + python-version-2: "3.12" diff --git a/.github/workflows/latest_v26.04.yml b/.github/workflows/latest_v26.04.yml index f545ca4..a0dc3a0 100644 --- a/.github/workflows/latest_v26.04.yml +++ b/.github/workflows/latest_v26.04.yml @@ -12,3 +12,5 @@ jobs: cln-version: "v26.04" pyln-version: "26.4" tagged-release: true + python-version-1: "3.10" + python-version-2: "3.12" diff --git a/.github/workflows/latest_v26.06.yml b/.github/workflows/latest_v26.06.yml new file mode 100644 index 0000000..27edc2d --- /dev/null +++ b/.github/workflows/latest_v26.06.yml @@ -0,0 +1,16 @@ +name: Latest release on CLN v26.06.x + +on: + release: + types: [published, edited] + workflow_dispatch: + +jobs: + call-ci: + uses: ./.github/workflows/ci.yml + with: + cln-version: "v26.06.6" + pyln-version: "26.6.6" + tagged-release: true + python-version-1: "3.10" + python-version-2: "3.14" diff --git a/.github/workflows/master_v25.09.yml b/.github/workflows/master_v25.09.yml index 7b62c39..1a73ecd 100644 --- a/.github/workflows/master_v25.09.yml +++ b/.github/workflows/master_v25.09.yml @@ -19,6 +19,8 @@ jobs: call-ci: uses: ./.github/workflows/ci.yml with: - cln-version: "v25.09.2" - pyln-version: "25.9.2" + cln-version: "v25.09.3" + pyln-version: "25.9.3" tagged-release: false + python-version-1: "3.10" + python-version-2: "3.12" diff --git a/.github/workflows/master_v25.12.yml b/.github/workflows/master_v25.12.yml index 5df85d3..7e0ae62 100644 --- a/.github/workflows/master_v25.12.yml +++ b/.github/workflows/master_v25.12.yml @@ -19,6 +19,8 @@ jobs: call-ci: uses: ./.github/workflows/ci.yml with: - cln-version: "v25.12" - pyln-version: "25.12" + cln-version: "v25.12.1" + pyln-version: "25.12.1" tagged-release: false + python-version-1: "3.10" + python-version-2: "3.12" diff --git a/.github/workflows/master_v26.04.yml b/.github/workflows/master_v26.04.yml index 5fcd616..82715be 100644 --- a/.github/workflows/master_v26.04.yml +++ b/.github/workflows/master_v26.04.yml @@ -22,3 +22,5 @@ jobs: cln-version: "v26.04" pyln-version: "26.4" tagged-release: false + python-version-1: "3.10" + python-version-2: "3.12" diff --git a/.github/workflows/master_v25.05.yml b/.github/workflows/master_v26.06.yml similarity index 70% rename from .github/workflows/master_v25.05.yml rename to .github/workflows/master_v26.06.yml index dfd753c..25fe3a8 100644 --- a/.github/workflows/master_v25.05.yml +++ b/.github/workflows/master_v26.06.yml @@ -1,4 +1,4 @@ -name: master on CLN v25.05.x +name: master on CLN v26.06.x on: push: @@ -19,6 +19,8 @@ jobs: call-ci: uses: ./.github/workflows/ci.yml with: - cln-version: "v25.05" - pyln-version: "25.5" + cln-version: "v26.06.6" + pyln-version: "26.6.6" tagged-release: false + python-version-1: "3.10" + python-version-2: "3.14" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 56b76c8..6fef945 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,12 +25,6 @@ jobs: sudo apt update sudo apt install -y gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf - - name: Install Protoc - uses: arduino/setup-protoc@v3 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - version: "21.12" - - name: Build Linux binaries (release) run: | cargo build --profile optimized --locked --target x86_64-unknown-linux-gnu @@ -70,12 +64,6 @@ jobs: with: targets: x86_64-apple-darwin, aarch64-apple-darwin - - name: Install Protoc - uses: arduino/setup-protoc@v3 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - version: "21.12" - - name: Build macOS universal (release) id: build env: From f85b2efcb39c3213f2a369f703c051eb7c2ac5cb Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 26 Jul 2026 13:15:02 +0200 Subject: [PATCH 21/56] update dependencies, upgrade nostr and cln dependencies --- Cargo.lock | 592 +++++++++++++++------------------------ Cargo.toml | 20 +- build.rs | 2 + src/main.rs | 2 +- src/nwc.rs | 36 +-- src/nwc_notifications.rs | 44 ++- src/structs.rs | 3 +- 7 files changed, 287 insertions(+), 412 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 81fd1eb..b9dd79f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" [[package]] name = "async-trait" @@ -69,13 +69,15 @@ dependencies = [ [[package]] name = "async-wsocket" -version = "0.13.1" -source = "git+https://github.com/shadowylab/async-wsocket?rev=0fed6c9c6aec7393ee0e9cf3933d76914ab427d3#0fed6c9c6aec7393ee0e9cf3933d76914ab427d3" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c69f24cf7a798027c6b0801de5bfd05d01fe343e87a8329f7c8b46a6d3bc4a79" dependencies = [ "futures", "futures-util", "js-sys", "tokio", + "tokio-happy-eyeballs", "tokio-rustls", "tokio-socks", "tokio-tungstenite", @@ -93,11 +95,10 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "base58ck" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +checksum = "8f1cba749a07c1efb1f4d87518f39cea4aec25d0991fb97e80459c057238f0d2" dependencies = [ - "bitcoin-internals", "bitcoin_hashes", ] @@ -107,12 +108,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - [[package]] name = "bech32" version = "0.11.1" @@ -132,13 +127,12 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.32.9" +version = "0.32.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf93e61f2dbc3e3c41234ca26a65e2c0b0975c52e0f069ab9893ebbede584d3" +checksum = "a1a121ccf1177a09084bd6ce97c52119919aac08ea3649967958eae41beceebf" dependencies = [ "base58ck", "bech32", - "bitcoin-internals", "bitcoin-io", "bitcoin-units", "bitcoin_hashes", @@ -148,15 +142,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitcoin-internals" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" -dependencies = [ - "serde", -] - [[package]] name = "bitcoin-io" version = "0.1.4" @@ -165,19 +150,18 @@ checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" [[package]] name = "bitcoin-units" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346568ebaab2918487cea76dd55dae13c27bb618cdb737c952e69eb2017c4118" +checksum = "c202276cab20ab02cf6cc9d4df0d4284f7c784031619d3842236de8bc2bd5c6c" dependencies = [ - "bitcoin-internals", "serde", ] [[package]] name = "bitcoin_hashes" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" +checksum = "4ed83caece3afc59919481b33b472e1432d1abc4641ed9100be142ef5110b406" dependencies = [ "bitcoin-io", "hex-conservative", @@ -186,9 +170,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.11.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "block-buffer" @@ -216,9 +200,9 @@ checksum = "6160c957d8aa33d0a8ba1dbab98e3cb57023ad9374c501441e88559f99e6c4c9" [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "byteorder" @@ -228,9 +212,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" [[package]] name = "cbc" @@ -243,9 +227,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.62" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", "shlex", @@ -307,6 +291,7 @@ dependencies = [ "nostr-sdk", "parking_lot", "prost", + "protoc-bin-vendored", "regex", "serde", "serde_json", @@ -319,9 +304,9 @@ dependencies = [ [[package]] name = "cln-plugin" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c340e01d74b553f761dd5636b8238cb92cacdf7d9d27a8820dad42da76ce0cb5" +checksum = "b9a4bb9c7335040d49c51900305b91155303539e9f54839bb194e915277bf882" dependencies = [ "anyhow", "bytes", @@ -338,9 +323,9 @@ dependencies = [ [[package]] name = "cln-rpc" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8c63eb175d58f0212e35e427237cf3e4f856f47ec7b8ad95110484d292fc819" +checksum = "5d9f5e26f0a2713ea1393eb3117c148295fa4c5f7db529713b20323ca7cc37aa" dependencies = [ "anyhow", "bitcoin", @@ -387,14 +372,13 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", - "subtle", ] [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", @@ -403,9 +387,9 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "equivalent" @@ -595,15 +579,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", - "wasip2", - "wasip3", + "rand_core 0.10.1", ] [[package]] @@ -620,9 +603,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" dependencies = [ "atomic-waker", "bytes", @@ -698,20 +681,11 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - [[package]] name = "http" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -754,9 +728,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" dependencies = [ "atomic-waker", "bytes", @@ -888,12 +862,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "idna" version = "1.1.0" @@ -923,8 +891,6 @@ checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown 0.17.1", - "serde", - "serde_core", ] [[package]] @@ -954,13 +920,12 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.98" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] @@ -970,12 +935,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libc" version = "0.2.186" @@ -1005,9 +964,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "log-panics" @@ -1020,9 +979,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.16.4" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" +checksum = "0b6180140927ee907000b0aa540091f6ea512ead4447c92b8fc35bc72788a5a6" [[package]] name = "matchers" @@ -1035,15 +994,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "mio" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "wasi", @@ -1064,8 +1023,9 @@ checksum = "f0efe882e02d206d8d279c20eb40e03baf7cb5136a1476dc084a324fbc3ec42d" [[package]] name = "nostr" -version = "0.44.1" -source = "git+https://github.com/rust-nostr/nostr.git?rev=6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6#6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6" +version = "0.45.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e729c14441b72fca67727523e06242a5e173410d62118288de6fa308c199cceb" dependencies = [ "aes", "base64", @@ -1076,8 +1036,8 @@ dependencies = [ "chacha20", "chacha20poly1305", "faster-hex", - "rand", - "scrypt", + "opaquerr", + "rand 0.10.1", "secp256k1", "serde", "serde_json", @@ -1088,25 +1048,30 @@ dependencies = [ [[package]] name = "nostr-database" -version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr.git?rev=6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6#6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6" +version = "0.45.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "944c4b23b12e1e138e48557dd7628dfea5a7e48f306b31438dc2a5161fb0635c" dependencies = [ "btreecap", "nostr", + "opaquerr", ] [[package]] name = "nostr-gossip" -version = "0.44.0" -source = "git+https://github.com/rust-nostr/nostr.git?rev=6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6#6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6" +version = "0.45.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc19bd1f8cbd5b3a2e2b48cf8758dd34ead48535beb514e26c919afbd35e9d" dependencies = [ "nostr", + "opaquerr", ] [[package]] name = "nostr-sdk" -version = "0.44.1" -source = "git+https://github.com/rust-nostr/nostr.git?rev=6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6#6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6" +version = "0.45.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60df0948bd33d1853cb977782045ea083c0aaf140353cc7f52a8ce0a2c9f42d" dependencies = [ "async-utility", "async-wsocket", @@ -1117,6 +1082,7 @@ dependencies = [ "nostr", "nostr-database", "nostr-gossip", + "opaquerr", "tokio", "tokio-stream", "tracing", @@ -1144,6 +1110,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "opaquerr" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e55a7b7f0accc9fdd8e514f122a697adf824d55de481ca26c31ff8f9a8e813c5" + [[package]] name = "parking_lot" version = "0.12.5" @@ -1167,27 +1139,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "password-hash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", -] - [[package]] name = "percent-encoding" version = "2.3.2" @@ -1207,18 +1158,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.12" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf0d9e68100b3a7989b4901972f265cd542e560a3a8a724e1e20322f4d06ce9" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.12" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a990e22f43e84855daf260dded30524ef4a9021cc7541c26540500a50b624389" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", @@ -1281,9 +1232,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" dependencies = [ "bytes", "prost-derive", @@ -1291,9 +1242,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" +checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ "heck", "itertools", @@ -1312,9 +1263,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", "itertools", @@ -1325,18 +1276,82 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" +checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" dependencies = [ "prost", ] [[package]] -name = "pulldown-cmark" -version = "0.13.3" +name = "protoc-bin-vendored" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a14896dfa883796f1cb410461aef38810ea05f2b2c33c5aded3649095fdad" +checksum = "d1c381df33c98266b5f08186583660090a4ffa0889e76c7e9a5e175f645a67fa" +dependencies = [ + "protoc-bin-vendored-linux-aarch_64", + "protoc-bin-vendored-linux-ppcle_64", + "protoc-bin-vendored-linux-s390_64", + "protoc-bin-vendored-linux-x86_32", + "protoc-bin-vendored-linux-x86_64", + "protoc-bin-vendored-macos-aarch_64", + "protoc-bin-vendored-macos-x86_64", + "protoc-bin-vendored-win32", +] + +[[package]] +name = "protoc-bin-vendored-linux-aarch_64" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c350df4d49b5b9e3ca79f7e646fde2377b199e13cfa87320308397e1f37e1a4c" + +[[package]] +name = "protoc-bin-vendored-linux-ppcle_64" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55a63e6c7244f19b5c6393f025017eb5d793fd5467823a099740a7a4222440c" + +[[package]] +name = "protoc-bin-vendored-linux-s390_64" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dba5565db4288e935d5330a07c264a4ee8e4a5b4a4e6f4e83fad824cc32f3b0" + +[[package]] +name = "protoc-bin-vendored-linux-x86_32" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8854774b24ee28b7868cd71dccaae8e02a2365e67a4a87a6cd11ee6cdbdf9cf5" + +[[package]] +name = "protoc-bin-vendored-linux-x86_64" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b38b07546580df720fa464ce124c4b03630a6fb83e05c336fea2a241df7e5d78" + +[[package]] +name = "protoc-bin-vendored-macos-aarch_64" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89278a9926ce312e51f1d999fee8825d324d603213344a9a706daa009f1d8092" + +[[package]] +name = "protoc-bin-vendored-macos-x86_64" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81745feda7ccfb9471d7a4de888f0652e806d5795b61480605d4943176299756" + +[[package]] +name = "protoc-bin-vendored-win32" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95067976aca6421a523e491fce939a3e65249bac4b977adee0ee9771568e8aa3" + +[[package]] +name = "pulldown-cmark" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" dependencies = [ "bitflags", "memchr", @@ -1383,6 +1398,16 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "getrandom 0.4.3", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -1393,15 +1418,6 @@ dependencies = [ "rand_core 0.9.5", ] -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - [[package]] name = "rand_core" version = "0.9.5" @@ -1411,6 +1427,12 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "redox_syscall" version = "0.5.18" @@ -1422,9 +1444,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -1445,9 +1467,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "ring" @@ -1517,33 +1539,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "scrypt" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" -dependencies = [ - "password-hash", - "pbkdf2", - "salsa20", - "sha2", -] - [[package]] name = "secp256k1" version = "0.29.1" @@ -1564,12 +1565,6 @@ dependencies = [ "cc", ] -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - [[package]] name = "serde" version = "1.0.228" @@ -1602,9 +1597,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -1624,17 +1619,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -1646,9 +1630,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "slab" @@ -1658,15 +1642,15 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", "windows-sys 0.61.2", @@ -1686,9 +1670,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -1719,7 +1703,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys 0.61.2", @@ -1814,6 +1798,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tokio-happy-eyeballs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8564c32dfb6f4257f8bc6edfc178a34af97520e0b7b9815500c55eb3d092f29f" +dependencies = [ + "tokio", +] + [[package]] name = "tokio-macros" version = "2.7.0" @@ -1837,9 +1830,9 @@ dependencies = [ [[package]] name = "tokio-socks" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" +checksum = "a7e2948f60dbe26b35f2c7fb74ac2854c1fddded0fe9d7548fcc674a246f7615" dependencies = [ "either", "futures-util", @@ -2066,7 +2059,7 @@ dependencies = [ "http", "httparse", "log", - "rand", + "rand 0.9.4", "rustls", "rustls-pki-types", "sha1", @@ -2076,9 +2069,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.20.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "unicase" @@ -2101,12 +2094,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "universal-hash" version = "0.5.1" @@ -2156,11 +2143,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.23.1" +version = "1.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.4.3", "js-sys", "wasm-bindgen", ] @@ -2198,23 +2185,14 @@ version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen 0.46.0", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.121" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" dependencies = [ "cfg-if", "once_cell", @@ -2225,9 +2203,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.71" +version = "0.4.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" dependencies = [ "js-sys", "wasm-bindgen", @@ -2235,9 +2213,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.121" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2245,9 +2223,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.121" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" dependencies = [ "bumpalo", "proc-macro2", @@ -2258,52 +2236,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.121" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "web-sys" -version = "0.3.98" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" dependencies = [ "js-sys", "wasm-bindgen", @@ -2315,14 +2259,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.7", + "webpki-roots 1.0.8", ] [[package]] name = "webpki-roots" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" dependencies = [ "rustls-pki-types", ] @@ -2421,94 +2365,6 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "writeable" version = "0.6.3" @@ -2517,9 +2373,9 @@ checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "yoke" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -2540,18 +2396,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", @@ -2581,9 +2437,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zerotrie" diff --git a/Cargo.toml b/Cargo.toml index a0bb620..484d61b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,19 +12,20 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1", features = ["fs", "sync", "rt-multi-thread"] } -cln-rpc = "0.6" +cln-rpc = "0.7" # cln-rpc = { path = "../lightning/cln-rpc/", version = "^0.6" } -cln-plugin = "0.6" +cln-plugin = "0.7" # cln-plugin = { path = "../lightning/plugins/", version = "^0.6" } parking_lot = "0.12" -nostr-sdk = { git = "https://github.com/rust-nostr/nostr.git", rev = "6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6" } -nostr = { git = "https://github.com/rust-nostr/nostr.git", rev = "6eb03213c7d3d9eaaa9b74c142f2b8eb79445af6", features = [ - "nip47", - "nip04", - "nip44", -] } -# nostr-sdk = { version = "0.44", features = ["nip47", "nip04", "nip44"] } +# # nostr-sdk = { git = "https://github.com/rust-nostr/nostr.git", rev = "ff5be7d1416ea201887a02f648795010a3cbdf49" } +# nostr = { git = "https://github.com/rust-nostr/nostr.git", rev = "ff5be7d1416ea201887a02f648795010a3cbdf49", features = [ +# "nip47", +# "nip04", +# "nip44", +# ] } +nostr = { version = "0.45.0-alpha.4", features = ["nip47", "nip04", "nip44"] } +nostr-sdk = { version = "0.45.0-alpha.4" } futures = "0.3" @@ -44,6 +45,7 @@ tonic-prost = "0.14" [build-dependencies] tonic-prost-build = "0.14" +protoc-bin-vendored = "3" [profile.optimized] diff --git a/build.rs b/build.rs index 3e87891..04c8cef 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,6 @@ fn main() { + let protoc = protoc_bin_vendored::protoc_bin_path().unwrap(); + unsafe { std::env::set_var("PROTOC", protoc) }; tonic_prost_build::configure() .protoc_arg("--experimental_allow_proto3_optional") .compile_protos(&["protos/hold.proto"], &["protos"]) diff --git a/src/main.rs b/src/main.rs index dd70aad..a0e8a8b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -165,7 +165,7 @@ async fn shutdown_handler( _args: serde_json::Value, ) -> Result<(), anyhow::Error> { let mut locked_handles = plugin.state().handles.lock().await; - for (_x, (client, _client_pubkey)) in locked_handles.drain() { + for (_x, (client, _client_pubkey, _wallet_keys)) in locked_handles.drain() { client.shutdown().await; } std::process::exit(0) diff --git a/src/nwc.rs b/src/nwc.rs index b319853..3bcc1e7 100644 --- a/src/nwc.rs +++ b/src/nwc.rs @@ -14,11 +14,12 @@ use nostr::{ SecretKey, Tag, Timestamp, + event::FinalizeEventAsync, nips::{nip04, nip44, nip47}, - signer::SignerError, }; use nostr_sdk::{ - client::{self, Client, ClientNotification, Error}, + client::{self, Client, ClientNotification}, + error::Error, relay::RelayStatus, }; use tokio::{sync::oneshot, time}; @@ -49,13 +50,10 @@ pub async fn run_nwc( ) -> Result<(), Error> { let (method_capabilities, _) = build_capabilities(is_read_only_nwc(&nwc_store), &plugin); - let wallet_keys = Keys::new( - SecretKey::from_hex(&nwc_store.walletkey) - .map_err(|e| Error::Signer(SignerError::backend(e)))?, - ); - let client_pubkey = Keys::new(nwc_store.uri.secret.clone()).public_key(); + let wallet_keys = Keys::new(SecretKey::from_hex(&nwc_store.walletkey)?); + let client_keys = Keys::new(nwc_store.uri.secret.clone()); - let nostr_client = Client::builder().signer(wallet_keys.clone()).build(); + let nostr_client = Client::new(); log::debug!("relay_count:{}", nwc_store.uri.relays.len()); @@ -71,6 +69,8 @@ pub async fn run_nwc( let nostr_client_clone = nostr_client.clone(); let plugin_clone = plugin.clone(); let label_clone = label.clone(); + let client_keys_clone = client_keys.clone(); + let wallet_keys_clone = wallet_keys.clone(); tokio::spawn(async move { loop { nostr_client_clone @@ -100,7 +100,7 @@ pub async fn run_nwc( plugin_clone.clone(), nostr_client_clone.clone(), method_capabilities.clone(), - wallet_keys.clone(), + wallet_keys_clone.clone(), ) .await { @@ -112,7 +112,7 @@ pub async fn run_nwc( let filter = Filter::new() .kind(Kind::WalletConnectRequest) - .author(client_pubkey) + .author(client_keys_clone.public_key()) .since(Timestamp::now() - STARTUP_DELAY - 1); let mut notifications = nostr_client_clone.notifications(); @@ -140,8 +140,8 @@ pub async fn run_nwc( &nostr_client_clone, &plugin_clone, &label_clone, - &wallet_keys, - client_pubkey, + &wallet_keys_clone, + client_keys_clone.public_key(), ) .await { @@ -156,7 +156,7 @@ pub async fn run_nwc( let mut locked_handles = plugin.state().handles.lock().await; locked_handles.insert( label.clone(), - (nostr_client, Keys::new(nwc_store.uri.secret).public_key()), + (nostr_client, client_keys.public_key(), wallet_keys), ); Ok(()) } @@ -180,7 +180,7 @@ pub async fn send_nwc_info_event( .tag(Tag::parse(vec!["notifications", ¬ification_capabilities]).unwrap()); } - let info_event = match info_event_builder.sign_with_keys(&wallet_keys) { + let info_event = match info_event_builder.finalize_async(&wallet_keys).await { Ok(o) => o, Err(e) => { return Err(anyhow!("Could not sign info_event! {e}")); @@ -208,7 +208,7 @@ pub async fn send_nwc_info_event( pub async fn stop_nwc(plugin: Plugin, label: &String) { let mut locked_handles = plugin.state().handles.lock().await; - if let Some((client, _client_pubkey)) = locked_handles.remove(label) { + if let Some((client, _client_pubkey, _wallet_secret)) = locked_handles.remove(label) { client.shutdown().await; } @@ -299,7 +299,7 @@ async fn nwc_request_handler( }; let response_event = - match build_response_event(event.id, content, wallet_keys, client_pubkey, id) { + match build_response_event(event.id, content, wallet_keys, client_pubkey, id).await { Ok(o) => o, Err(e) => { log::warn!("Error signing reponse event! {e}"); @@ -427,7 +427,7 @@ fn encrypt_response_content( } } -fn build_response_event( +async fn build_response_event( event_id: EventId, content: String, wallet_keys: &Keys, @@ -440,7 +440,7 @@ fn build_response_event( if let Some(i) = id { response_builder = response_builder.tag(Tag::identifier(i)); } - match response_builder.sign_with_keys(wallet_keys) { + match response_builder.finalize_async(wallet_keys).await { Ok(o) => Ok(o), Err(e) => Err(e.into()), } diff --git a/src/nwc_notifications.rs b/src/nwc_notifications.rs index 4527a5c..34d2ca5 100644 --- a/src/nwc_notifications.rs +++ b/src/nwc_notifications.rs @@ -16,7 +16,15 @@ use cln_rpc::{ }, primitives::Sha256, }; -use nostr::{EventBuilder, Kind, Tag, Timestamp, key::PublicKey, nips::nip47}; +use nostr::{ + EventBuilder, + Kind, + Tag, + Timestamp, + event::FinalizeEventAsync, + key::{Keys, PublicKey, SecretKey}, + nips::{nip04, nip44, nip47}, +}; use nostr_sdk::client::Client; use crate::{ @@ -75,8 +83,8 @@ pub async fn payment_received_handler( let clients = plugin.state().handles.lock().await; - for (client, client_pubkey) in clients.values() { - send_notification(¬ification, client, client_pubkey).await?; + for (client, client_pubkey, wallet_secret) in clients.values() { + send_notification(¬ification, client, client_pubkey.clone(), wallet_secret).await?; } Ok(()) @@ -212,8 +220,8 @@ pub async fn payment_sent_handler( let clients = plugin.state().handles.lock().await; - for (client, client_pubkey) in clients.values() { - send_notification(¬ification, client, client_pubkey).await?; + for (client, client_pubkey, wallet_secret) in clients.values() { + send_notification(¬ification, client, client_pubkey.clone(), wallet_secret).await?; } Ok(()) @@ -335,14 +343,15 @@ async fn make_payment_sent_from_listpays( async fn send_notification( notification: &String, client: &Client, - client_pubkey: &PublicKey, + client_pubkey: PublicKey, + wallet_secret: &Keys, ) -> Result<(), anyhow::Error> { - let signer = client.signer().unwrap().clone(); log::debug!("NOTIFICATION: {notification}"); - let content_encrypted_nip04 = signer.nip04_encrypt(client_pubkey, notification).await?; + let content_encrypted_nip04 = + nip04::encrypt(wallet_secret.secret_key(), &client_pubkey, notification)?; let event_nip04 = EventBuilder::new(Kind::from_u16(23196), content_encrypted_nip04) - .tag(Tag::public_key(*client_pubkey)) - .sign_async(&signer) + .tag(Tag::public_key(client_pubkey)) + .finalize_async(wallet_secret) .await?; let nip04_result = client.send_event(&event_nip04).await?; if nip04_result.success.is_empty() { @@ -357,10 +366,15 @@ async fn send_notification( } log::debug!("NIP04 NOTIFICATION SENT: {event_nip04:?}"); - let content_encrypted_nip44 = signer.nip44_encrypt(client_pubkey, notification).await?; + let content_encrypted_nip44 = nip44::encrypt( + wallet_secret.secret_key(), + &client_pubkey, + notification, + nip44::Version::V2, + )?; let event_nip44 = EventBuilder::new(Kind::from_u16(23197), content_encrypted_nip44) - .tag(Tag::public_key(*client_pubkey)) - .sign_async(&signer) + .tag(Tag::public_key(client_pubkey)) + .finalize_async(wallet_secret) .await?; let nip44_result = client.send_event(&event_nip44).await?; if nip44_result.success.is_empty() { @@ -500,8 +514,8 @@ pub async fn holdinvoice_accepted_handler( }; let notification = serde_json::to_string(&content).unwrap(); - for (client, client_pubkey) in clients.values() { - send_notification(¬ification, client, client_pubkey).await?; + for (client, client_pubkey, wallet_secret) in clients.values() { + send_notification(¬ification, client, client_pubkey.clone(), wallet_secret).await?; } Ok(()) } diff --git a/src/structs.rs b/src/structs.rs index e7eea09..22c166b 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -15,7 +15,8 @@ pub const NOT_INV_ERR: &str = "Not an invoice or invalid invoice"; #[derive(Clone)] pub struct PluginState { pub config: Arc>, - pub handles: Arc>>, + pub handles: + Arc>>, pub rpc_lock: Arc>, pub budget_jobs: Arc>>>, pub hold_client: Arc>>>, From f6517cab7e896ade45845439a349a5e9c2ed22d9 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 26 Jul 2026 13:15:41 +0200 Subject: [PATCH 22/56] update dependencies --- Cargo.lock | 370 +++++++++++++++++++++++++++++------------------------ 1 file changed, 206 insertions(+), 164 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b9dd79f..c693f75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,25 +34,25 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "arrayvec" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[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", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -95,9 +95,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "base58ck" -version = "0.1.1" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1cba749a07c1efb1f4d87518f39cea4aec25d0991fb97e80459c057238f0d2" +checksum = "365c0acd5b2e8dd0111a46c4faea83fb3cfb6e39a49a7c73a06e090db7b2eff0" dependencies = [ "bitcoin_hashes", ] @@ -127,52 +127,73 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.32.10" +version = "0.32.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a121ccf1177a09084bd6ce97c52119919aac08ea3649967958eae41beceebf" +checksum = "bb0ce8bd5baaa0d303a19915a6d93afed161f528654e42da2a7a97d05c59499a" dependencies = [ "base58ck", "bech32", "bitcoin-io", "bitcoin-units", "bitcoin_hashes", - "hex-conservative", + "hex-conservative 0.2.2", "hex_lit", "secp256k1", "serde", ] [[package]] -name = "bitcoin-io" -version = "0.1.4" +name = "bitcoin-consensus-encoding" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" +checksum = "207311705279250ba465076a1bac4b1ac982855fff73fc5f67e22158ac58cdc9" +dependencies = [ + "bitcoin-internals", + "hex-conservative 1.2.0", + "serde", +] + +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + +[[package]] +name = "bitcoin-io" +version = "0.1.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78" +dependencies = [ + "bitcoin-consensus-encoding", +] [[package]] name = "bitcoin-units" -version = "0.1.4" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c202276cab20ab02cf6cc9d4df0d4284f7c784031619d3842236de8bc2bd5c6c" +checksum = "9cb95693f371d089a4b5b6fc41c6f3ea6e01ee8c15388335dfac8ea685173b51" dependencies = [ + "bitcoin-consensus-encoding", "serde", ] [[package]] name = "bitcoin_hashes" -version = "0.14.2" +version = "0.14.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ed83caece3afc59919481b33b472e1432d1abc4641ed9100be142ef5110b406" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" dependencies = [ "bitcoin-io", - "hex-conservative", + "hex-conservative 0.2.2", "serde", ] [[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" [[package]] name = "block-buffer" @@ -212,9 +233,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[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 = "cbc" @@ -227,9 +248,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.65" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" dependencies = [ "find-msvc-tools", "shlex", @@ -382,14 +403,14 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[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 = "equivalent" @@ -419,9 +440,9 @@ dependencies = [ [[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 = "find-msvc-tools" @@ -458,9 +479,9 @@ dependencies = [ [[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", @@ -473,9 +494,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", @@ -483,15 +504,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", @@ -500,38 +521,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", "quote", - "syn", + "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", @@ -675,6 +696,15 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hex-conservative" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex_lit" version = "0.1.1" @@ -693,9 +723,9 @@ dependencies = [ [[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", "http", @@ -703,9 +733,9 @@ dependencies = [ [[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", "futures-core", @@ -728,9 +758,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[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", @@ -920,9 +950,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.102" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", @@ -937,9 +967,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[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 = "linux-raw-sys" @@ -994,15 +1024,15 @@ 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 = "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", "wasi", @@ -1023,9 +1053,9 @@ checksum = "f0efe882e02d206d8d279c20eb40e03baf7cb5136a1476dc084a324fbc3ec42d" [[package]] name = "nostr" -version = "0.45.0-alpha.4" +version = "0.45.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e729c14441b72fca67727523e06242a5e173410d62118288de6fa308c199cceb" +checksum = "5e90ba68fc04b87254ef574e10aef1fe92d44839043f42e37a7c4c4a1e7e4c82" dependencies = [ "aes", "base64", @@ -1037,7 +1067,7 @@ dependencies = [ "chacha20poly1305", "faster-hex", "opaquerr", - "rand 0.10.1", + "rand 0.10.2", "secp256k1", "serde", "serde_json", @@ -1048,9 +1078,9 @@ dependencies = [ [[package]] name = "nostr-database" -version = "0.45.0-alpha.4" +version = "0.45.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "944c4b23b12e1e138e48557dd7628dfea5a7e48f306b31438dc2a5161fb0635c" +checksum = "77c37167c3f289fea43e82f8bb24bb2006a2fb803abaaa908b25f1d3ca0b2bcc" dependencies = [ "btreecap", "nostr", @@ -1059,9 +1089,9 @@ dependencies = [ [[package]] name = "nostr-gossip" -version = "0.45.0-alpha.4" +version = "0.45.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc19bd1f8cbd5b3a2e2b48cf8758dd34ead48535beb514e26c919afbd35e9d" +checksum = "bb24a1f82e3fe9e3e9b4549e916ec168d4c21c2e55c61fa6be9f98218872cd37" dependencies = [ "nostr", "opaquerr", @@ -1069,9 +1099,9 @@ dependencies = [ [[package]] name = "nostr-sdk" -version = "0.45.0-alpha.4" +version = "0.45.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60df0948bd33d1853cb977782045ea083c0aaf140353cc7f52a8ce0a2c9f42d" +checksum = "5b197627792d5f02ab257c1bff30d4475e0ebc85229a82515b05d6717c6a4fbc" dependencies = [ "async-utility", "async-wsocket", @@ -1173,7 +1203,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1218,14 +1248,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.119", ] [[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", ] @@ -1257,7 +1287,7 @@ dependencies = [ "pulldown-cmark", "pulldown-cmark-to-cmark", "regex", - "syn", + "syn 2.0.119", "tempfile", ] @@ -1271,7 +1301,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1369,9 +1399,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -1390,9 +1420,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[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", "rand_core 0.9.5", @@ -1400,9 +1430,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" dependencies = [ "getrandom 0.4.3", "rand_core 0.10.1", @@ -1444,9 +1474,9 @@ dependencies = [ [[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", @@ -1456,9 +1486,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", @@ -1500,9 +1530,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.40" +version = "0.23.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" dependencies = [ "log", "once_cell", @@ -1515,9 +1545,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] @@ -1535,9 +1565,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 = "scopeguard" @@ -1567,9 +1597,9 @@ dependencies = [ [[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", @@ -1577,29 +1607,29 @@ 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", "quote", - "syn", + "syn 3.0.3", ] [[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", @@ -1610,9 +1640,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", "cpufeatures", @@ -1648,9 +1678,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", @@ -1670,9 +1700,20 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.118" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" dependencies = [ "proc-macro2", "quote", @@ -1693,7 +1734,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1720,11 +1761,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]] @@ -1735,25 +1776,25 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "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", "quote", - "syn", + "syn 3.0.3", ] [[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", ] @@ -1770,9 +1811,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", ] @@ -1785,9 +1826,9 @@ 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", "libc", @@ -1809,13 +1850,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1842,9 +1883,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" dependencies = [ "futures-core", "pin-project-lite", @@ -1870,13 +1911,14 @@ 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", "futures-core", "futures-sink", + "libc", "pin-project-lite", "tokio", ] @@ -1919,7 +1961,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1944,7 +1986,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn", + "syn 2.0.119", "tempfile", "tonic-build", ] @@ -2000,7 +2042,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2059,11 +2101,11 @@ dependencies = [ "http", "httparse", "log", - "rand 0.9.4", + "rand 0.9.5", "rustls", "rustls-pki-types", "sha1", - "thiserror 2.0.18", + "thiserror 2.0.19", "utf-8", ] @@ -2143,9 +2185,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.23.3" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -2190,9 +2232,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -2203,9 +2245,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.75" +version = "0.4.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" dependencies = [ "js-sys", "wasm-bindgen", @@ -2213,9 +2255,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2223,31 +2265,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.102" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" dependencies = [ "js-sys", "wasm-bindgen", @@ -2259,14 +2301,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.8", + "webpki-roots 1.0.9", ] [[package]] name = "webpki-roots" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" dependencies = [ "rustls-pki-types", ] @@ -2390,28 +2432,28 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn", + "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", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2431,7 +2473,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] @@ -2471,11 +2513,11 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[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" From 61c07f6079c05716c745020d357946125c35c209 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 26 Jul 2026 14:48:42 +0200 Subject: [PATCH 23/56] refactor, clippy and ruff fixes --- src/main.rs | 6 +- src/nwc.rs | 17 +- src/nwc_keysend.rs | 297 ++++++------ src/nwc_lookups.rs | 613 +++++++++++++------------ src/nwc_notifications.rs | 58 +-- src/rpc.rs | 2 +- src/structs.rs | 9 +- tests/pyproject.toml | 8 +- tests/test_cln-nip47.py | 109 ++--- tests/uv.lock | 949 ++++++++++++++++++++++----------------- 10 files changed, 1134 insertions(+), 934 deletions(-) mode change 100755 => 100644 tests/test_cln-nip47.py diff --git a/src/main.rs b/src/main.rs index a0e8a8b..f80d21a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -165,8 +165,8 @@ async fn shutdown_handler( _args: serde_json::Value, ) -> Result<(), anyhow::Error> { let mut locked_handles = plugin.state().handles.lock().await; - for (_x, (client, _client_pubkey, _wallet_keys)) in locked_handles.drain() { - client.shutdown().await; + for (_x, wallet_service) in locked_handles.drain() { + wallet_service.client.shutdown().await; } std::process::exit(0) } @@ -299,6 +299,6 @@ fn do_certificates_exist(cert_dir: &Path) -> bool { required_files.iter().all(|file| { let path = cert_dir.join(file); - path.exists() && path.metadata().map(|m| m.len() > 0).unwrap_or(false) + path.exists() && path.metadata().is_ok_and(|m| m.len() > 0) }) } diff --git a/src/nwc.rs b/src/nwc.rs index 3bcc1e7..c381f94 100644 --- a/src/nwc.rs +++ b/src/nwc.rs @@ -38,11 +38,12 @@ use crate::{ nwc_keysend::pay_keysend_response, nwc_lookups::{list_transactions_response, lookup_invoice_response}, nwc_pay::pay_invoice_response, - structs::{NwcStore, PluginState}, + structs::{NwcStore, PluginState, WalletService}, tasks::budget_task, util::{build_capabilities, build_notifications_vec, is_read_only_nwc}, }; +#[allow(clippy::too_many_lines)] pub async fn run_nwc( plugin: Plugin, label: String, @@ -154,10 +155,12 @@ pub async fn run_nwc( }); let mut locked_handles = plugin.state().handles.lock().await; - locked_handles.insert( - label.clone(), - (nostr_client, client_keys.public_key(), wallet_keys), - ); + let wallet_service = WalletService { + client: nostr_client, + client_pubkey: client_keys.public_key(), + wallet_secret: wallet_keys, + }; + locked_handles.insert(label.clone(), wallet_service); Ok(()) } @@ -208,8 +211,8 @@ pub async fn send_nwc_info_event( pub async fn stop_nwc(plugin: Plugin, label: &String) { let mut locked_handles = plugin.state().handles.lock().await; - if let Some((client, _client_pubkey, _wallet_secret)) = locked_handles.remove(label) { - client.shutdown().await; + if let Some(wallet_service) = locked_handles.remove(label) { + wallet_service.client.shutdown().await; } stop_nwc_budget_job(&plugin, label); diff --git a/src/nwc_keysend.rs b/src/nwc_keysend.rs index c5c5673..dc1ee5e 100644 --- a/src/nwc_keysend.rs +++ b/src/nwc_keysend.rs @@ -2,13 +2,14 @@ use std::{collections::HashMap, str::FromStr}; use cln_plugin::Plugin; use cln_rpc::{ + ClnRpc, model::requests::{KeysendRequest, XkeysendRequest}, primitives::{Amount, PublicKey, TlvEntry, TlvStream}, }; -use nostr::nips::nip47; +use nostr::nips::nip47::{self}; use crate::{ - structs::PluginState, + structs::{NwcStore, PluginState}, util::{at_or_above_version, budget_amount_check, load_nwc_store, update_nwc_store}, }; @@ -57,7 +58,7 @@ async fn pay_keysend( }); } - let mut nwc_store = load_nwc_store(&mut rpc, label) + let nwc_store = load_nwc_store(&mut rpc, label) .await .map_err(|e| nip47::NIP47Error { code: nip47::ErrorCode::Internal, @@ -82,141 +83,161 @@ async fn pay_keysend( code: nip47::ErrorCode::Other, message: e.to_string(), })? { - let mut extratlvs = HashMap::with_capacity(params.tlv_records.len()); - for tlv in params.tlv_records { - extratlvs.insert(tlv.tlv_type.to_string(), tlv.value); - } - let extratlvs = if extratlvs.is_empty() { - None - } else { - Some(extratlvs) - }; - - match rpc - .call_typed(&XkeysendRequest { - extratlvs, - label: None, - maxdelay: None, - maxfee: None, - retry_for: None, - layers: None, - amount_msat: Amount::from_msat(params.amount), - destination: pubkey, - }) - .await - { - Ok(o) => { - if let Some(ref mut bdg) = nwc_store.budget_msat { - *bdg = bdg.saturating_sub(o.amount_sent_msat.msat()); - update_nwc_store(&mut rpc, label, nwc_store) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - })?; - } - - let preimage = hex::encode(o.payment_preimage.to_vec()); - - let fees_paid = o.amount_sent_msat.msat() - o.amount_msat.msat(); - - Ok(nip47::PayKeysendResponse { - preimage, - fees_paid: Some(fees_paid), - }) - } - Err(e) => match e.code { - Some(c) => match c { - 203 | 205 | 207 | 219 => Err(nip47::NIP47Error { - code: nip47::ErrorCode::PaymentFailed, - message: e.to_string(), - }), - 209 => Err(nip47::NIP47Error { - code: nip47::ErrorCode::Other, - message: e.to_string(), - }), - _ => Err(nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - }), - }, - None => Err(nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - }), - }, - } + xkeysend(&mut rpc, params, pubkey, nwc_store, label).await } else { - let mut extratlvs = TlvStream { - entries: Vec::new(), - }; - for tlv in params.tlv_records { - extratlvs.entries.push(TlvEntry { - typ: tlv.tlv_type, - value: tlv.value.as_bytes().to_owned(), - }); - } - let extratlvs = if extratlvs.entries.is_empty() { - None - } else { - Some(extratlvs) - }; - - match rpc - .call_typed(&KeysendRequest { - exemptfee: None, - extratlvs, - label: None, - maxdelay: None, - maxfee: None, - maxfeepercent: None, - retry_for: None, - routehints: None, - amount_msat: Amount::from_msat(params.amount), - destination: pubkey, - }) - .await - { - Ok(o) => { - if let Some(ref mut bdg) = nwc_store.budget_msat { - *bdg = bdg.saturating_sub(o.amount_sent_msat.msat()); - update_nwc_store(&mut rpc, label, nwc_store) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - })?; - } - - let preimage = hex::encode(o.payment_preimage.to_vec()); - - let fees_paid = o.amount_sent_msat.msat() - o.amount_msat.msat(); - - Ok(nip47::PayKeysendResponse { - preimage, - fees_paid: Some(fees_paid), - }) - } - Err(e) => match e.code { - Some(c) => match c { - 203 | 205 | 210 => Err(nip47::NIP47Error { - code: nip47::ErrorCode::PaymentFailed, - message: e.to_string(), - }), - 206 => Err(nip47::NIP47Error { - code: nip47::ErrorCode::InsufficientBalance, - message: e.to_string(), - }), - _ => Err(nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - }), - }, - None => Err(nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - }), - }, - } + keysend(&mut rpc, params, pubkey, nwc_store, label).await + } +} + +async fn xkeysend( + rpc: &mut ClnRpc, + params: nip47::PayKeysendRequest, + pubkey: PublicKey, + mut nwc_store: NwcStore, + label: &str, +) -> Result { + let mut extratlvs = HashMap::with_capacity(params.tlv_records.len()); + for tlv in params.tlv_records { + extratlvs.insert(tlv.tlv_type.to_string(), tlv.value); + } + let extratlvs = if extratlvs.is_empty() { + None + } else { + Some(extratlvs) + }; + + match rpc + .call_typed(&XkeysendRequest { + extratlvs, + label: None, + maxdelay: None, + maxfee: None, + retry_for: None, + layers: None, + amount_msat: Amount::from_msat(params.amount), + destination: pubkey, + }) + .await + { + Ok(o) => { + if let Some(ref mut bdg) = nwc_store.budget_msat { + *bdg = bdg.saturating_sub(o.amount_sent_msat.msat()); + update_nwc_store(rpc, label, nwc_store) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + } + + let preimage = hex::encode(o.payment_preimage.to_vec()); + + let fees_paid = o.amount_sent_msat.msat() - o.amount_msat.msat(); + + Ok(nip47::PayKeysendResponse { + preimage, + fees_paid: Some(fees_paid), + }) + } + Err(e) => match e.code { + Some(c) => match c { + 203 | 205 | 207 | 219 => Err(nip47::NIP47Error { + code: nip47::ErrorCode::PaymentFailed, + message: e.to_string(), + }), + 209 => Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: e.to_string(), + }), + _ => Err(nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + }), + }, + None => Err(nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + }), + }, + } +} + +async fn keysend( + rpc: &mut ClnRpc, + params: nip47::PayKeysendRequest, + pubkey: PublicKey, + mut nwc_store: NwcStore, + label: &str, +) -> Result { + let mut extratlvs = TlvStream { + entries: Vec::new(), + }; + for tlv in params.tlv_records { + extratlvs.entries.push(TlvEntry { + typ: tlv.tlv_type, + value: tlv.value.as_bytes().to_owned(), + }); + } + let extratlvs = if extratlvs.entries.is_empty() { + None + } else { + Some(extratlvs) + }; + + match rpc + .call_typed(&KeysendRequest { + exemptfee: None, + extratlvs, + label: None, + maxdelay: None, + maxfee: None, + maxfeepercent: None, + retry_for: None, + routehints: None, + amount_msat: Amount::from_msat(params.amount), + destination: pubkey, + }) + .await + { + Ok(o) => { + if let Some(ref mut bdg) = nwc_store.budget_msat { + *bdg = bdg.saturating_sub(o.amount_sent_msat.msat()); + update_nwc_store(rpc, label, nwc_store) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + } + + let preimage = hex::encode(o.payment_preimage.to_vec()); + + let fees_paid = o.amount_sent_msat.msat() - o.amount_msat.msat(); + + Ok(nip47::PayKeysendResponse { + preimage, + fees_paid: Some(fees_paid), + }) + } + Err(e) => match e.code { + Some(c) => match c { + 203 | 205 | 210 => Err(nip47::NIP47Error { + code: nip47::ErrorCode::PaymentFailed, + message: e.to_string(), + }), + 206 => Err(nip47::NIP47Error { + code: nip47::ErrorCode::InsufficientBalance, + message: e.to_string(), + }), + _ => Err(nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + }), + }, + None => Err(nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + }), + }, } } diff --git a/src/nwc_lookups.rs b/src/nwc_lookups.rs index 5b5090c..c75c079 100644 --- a/src/nwc_lookups.rs +++ b/src/nwc_lookups.rs @@ -2,9 +2,11 @@ use std::{cmp::Reverse, str::FromStr}; use cln_plugin::Plugin; use cln_rpc::{ + ClnRpc, model::{ requests::{DecodeRequest, ListinvoicesRequest, ListpaysRequest}, responses::{ + DecodeResponse, DecodeType, ListinvoicesInvoices, ListinvoicesInvoicesStatus, @@ -13,13 +15,13 @@ use cln_rpc::{ }, }, primitives::Sha256, - ClnRpc, }; -use nostr::{nips::nip47, Timestamp}; +use nostr::{Timestamp, nips::nip47}; +use tonic::transport::Channel; use crate::{ - hold::{self, list_request::Constraint, ListRequest}, - structs::{PluginState, NOT_INV_ERR}, + hold::{self, ListRequest, hold_client::HoldClient, list_request::Constraint}, + structs::{NOT_INV_ERR, PluginState}, }; pub async fn lookup_invoice_response( @@ -127,186 +129,7 @@ async fn lookup_invoice( if holdinvoice_support { let mut hold_client = plugin.state().hold_client.lock().clone().unwrap(); - - let (hold_invoice, invoice_decoded) = if let Some(ph) = ¶ms.payment_hash { - let payment_hash_hash = match hex::decode(ph) { - Ok(p) => p, - Err(_e) => { - return Err(nip47::NIP47Error { - code: nip47::ErrorCode::Other, - message: "Invalid payment hash".to_owned(), - }) - } - }; - let list_request = ListRequest { - constraint: Some(Constraint::PaymentHash(payment_hash_hash)), - }; - let hold_lookup = hold_client - .list(list_request) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Other, - message: format!("Could not fetch hold invoice: {e}"), - })? - .into_inner(); - - if hold_lookup.invoices.len() != 1 { - return Err(nip47::NIP47Error { - code: nip47::ErrorCode::Other, - message: "Transaction not found".to_owned(), - }); - } - - let hold_invoice = hold_lookup.invoices.into_iter().next().unwrap(); - let invoice_decoded = rpc - .call_typed(&DecodeRequest { - string: hold_invoice.invoice.clone(), - }) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - })?; - - (hold_invoice, invoice_decoded) - } else { - let invoice_decoded = rpc - .call_typed(&DecodeRequest { - string: params.invoice.unwrap(), - }) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - })?; - let ph = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => invoice_decoded.invoice_payment_hash.unwrap(), - DecodeType::BOLT11_INVOICE => invoice_decoded.payment_hash.unwrap().to_string(), - _ => todo!(), - }; - let payment_hash_hash = match hex::decode(&ph) { - Ok(p) => p, - Err(_e) => { - return Err(nip47::NIP47Error { - code: nip47::ErrorCode::Other, - message: "Invalid payment hash in invoice".to_owned(), - }) - } - }; - - let list_request = ListRequest { - constraint: Some(Constraint::PaymentHash(payment_hash_hash)), - }; - - let hold_lookup = hold_client - .list(list_request) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Other, - message: format!("Could not fetch hold invoice: {e}"), - })? - .into_inner(); - - if hold_lookup.invoices.len() != 1 { - return Err(nip47::NIP47Error { - code: nip47::ErrorCode::Other, - message: "Transaction not found".to_owned(), - }); - } - - let hold_invoice = hold_lookup.invoices.into_iter().next().unwrap(); - let invoice_decoded = rpc - .call_typed(&DecodeRequest { - string: hold_invoice.invoice.clone(), - }) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - })?; - (hold_invoice, invoice_decoded) - }; - let not_invoice_err = Err(nip47::NIP47Error { - code: nip47::ErrorCode::Other, - message: NOT_INV_ERR.to_owned(), - }); - - let description = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => invoice_decoded.offer_description, - DecodeType::BOLT11_INVOICE => invoice_decoded.description, - _ => return not_invoice_err, - }; - let description_hash = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => None, - DecodeType::BOLT11_INVOICE => invoice_decoded.description_hash.map(|h| h.to_string()), - _ => return not_invoice_err, - }; - - let created_at = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => { - Timestamp::from_secs(invoice_decoded.invoice_created_at.unwrap()) - } - DecodeType::BOLT11_INVOICE => Timestamp::from_secs(invoice_decoded.created_at.unwrap()), - _ => return not_invoice_err, - }; - - let amount = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => invoice_decoded.invoice_amount_msat.unwrap().msat(), - DecodeType::BOLT11_INVOICE => { - if let Some(amt) = invoice_decoded.amount_msat { - amt.msat() - } else { - // amount: `any` but have to put a value... - 0 - } - } - _ => return not_invoice_err, - }; - - let expires_at = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => invoice_decoded - .invoice_relative_expiry - .map(|e_at| created_at + Timestamp::from_secs(u64::from(e_at))), - DecodeType::BOLT11_INVOICE => invoice_decoded - .expiry - .map(|e_at| created_at + Timestamp::from_secs(e_at)), - _ => return not_invoice_err, - }; - - let state = match hold_invoice.state() { - hold::InvoiceState::Unpaid => nip47::TransactionState::Pending, - hold::InvoiceState::Accepted => nip47::TransactionState::Accepted, - hold::InvoiceState::Paid => nip47::TransactionState::Settled, - hold::InvoiceState::Cancelled => nip47::TransactionState::Expired, - }; - - let settled_at = if hold_invoice.settled_at() != 0 { - Some(Timestamp::from_secs(hold_invoice.settled_at())) - } else { - None - }; - - let preimage = if hold_invoice.state() == hold::InvoiceState::Paid { - Some(hex::encode(hold_invoice.preimage())) - } else { - None - }; - - return Ok(nip47::LookupInvoiceResponse { - transaction_type: Some(nip47::TransactionType::Incoming), - invoice: Some(hold_invoice.invoice.clone()), - description, - description_hash, - preimage, - payment_hash: hex::encode(hold_invoice.payment_hash), - amount, - fees_paid: 0, - created_at, - expires_at, - settled_at, - metadata: None, - state: Some(state), - }); + return lookup_holdinvoice(&mut hold_client, &mut rpc, params).await; } Err(nip47::NIP47Error { @@ -315,6 +138,202 @@ async fn lookup_invoice( }) } +async fn lookup_holdinvoice( + hold_client: &mut HoldClient, + rpc: &mut ClnRpc, + params: nip47::LookupInvoiceRequest, +) -> Result { + log::debug!("Looking up hold invoice for params {params:#?}"); + let (hold_invoice, invoice_decoded) = + get_and_decode_holdinvoice(rpc, hold_client, params).await?; + let not_invoice_err = Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: NOT_INV_ERR.to_owned(), + }); + + let description = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded.offer_description, + DecodeType::BOLT11_INVOICE => invoice_decoded.description, + _ => return not_invoice_err, + }; + let description_hash = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => None, + DecodeType::BOLT11_INVOICE => invoice_decoded.description_hash.map(|h| h.to_string()), + _ => return not_invoice_err, + }; + + let created_at = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => { + Timestamp::from_secs(invoice_decoded.invoice_created_at.unwrap()) + } + DecodeType::BOLT11_INVOICE => Timestamp::from_secs(invoice_decoded.created_at.unwrap()), + _ => return not_invoice_err, + }; + + let amount = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded.invoice_amount_msat.unwrap().msat(), + DecodeType::BOLT11_INVOICE => { + if let Some(amt) = invoice_decoded.amount_msat { + amt.msat() + } else { + // amount: `any` but have to put a value... + 0 + } + } + _ => return not_invoice_err, + }; + + let expires_at = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded + .invoice_relative_expiry + .map(|e_at| created_at + Timestamp::from_secs(u64::from(e_at))), + DecodeType::BOLT11_INVOICE => invoice_decoded + .expiry + .map(|e_at| created_at + Timestamp::from_secs(e_at)), + _ => return not_invoice_err, + }; + + let state = match hold_invoice.state() { + hold::InvoiceState::Unpaid => nip47::TransactionState::Pending, + hold::InvoiceState::Accepted => nip47::TransactionState::Accepted, + hold::InvoiceState::Paid => nip47::TransactionState::Settled, + hold::InvoiceState::Cancelled => nip47::TransactionState::Expired, + }; + + let settled_at = if hold_invoice.settled_at() != 0 { + Some(Timestamp::from_secs(hold_invoice.settled_at())) + } else { + None + }; + + let preimage = if hold_invoice.state() == hold::InvoiceState::Paid { + Some(hex::encode(hold_invoice.preimage())) + } else { + None + }; + + Ok(nip47::LookupInvoiceResponse { + transaction_type: Some(nip47::TransactionType::Incoming), + invoice: Some(hold_invoice.invoice.clone()), + description, + description_hash, + preimage, + payment_hash: hex::encode(hold_invoice.payment_hash), + amount, + fees_paid: 0, + created_at, + expires_at, + settled_at, + metadata: None, + state: Some(state), + }) +} + +async fn get_and_decode_holdinvoice( + rpc: &mut ClnRpc, + hold_client: &mut HoldClient, + params: nip47::LookupInvoiceRequest, +) -> Result<(hold::Invoice, DecodeResponse), nip47::NIP47Error> { + if let Some(ph) = ¶ms.payment_hash { + let payment_hash_hash = match hex::decode(ph) { + Ok(p) => p, + Err(_e) => { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Invalid payment hash".to_owned(), + }); + } + }; + let list_request = ListRequest { + constraint: Some(Constraint::PaymentHash(payment_hash_hash)), + }; + let hold_lookup = hold_client + .list(list_request) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: format!("Could not fetch hold invoice: {e}"), + })? + .into_inner(); + + if hold_lookup.invoices.len() != 1 { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Transaction not found".to_owned(), + }); + } + + let hold_invoice = hold_lookup.invoices.into_iter().next().unwrap(); + let invoice_decoded = rpc + .call_typed(&DecodeRequest { + string: hold_invoice.invoice.clone(), + }) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + + Ok((hold_invoice, invoice_decoded)) + } else { + let invoice_decoded = rpc + .call_typed(&DecodeRequest { + string: params.invoice.unwrap(), + }) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + let ph = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded.invoice_payment_hash.unwrap(), + DecodeType::BOLT11_INVOICE => invoice_decoded.payment_hash.unwrap().to_string(), + _ => todo!(), + }; + let payment_hash_hash = match hex::decode(&ph) { + Ok(p) => p, + Err(_e) => { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Invalid payment hash in invoice".to_owned(), + }); + } + }; + + let list_request = ListRequest { + constraint: Some(Constraint::PaymentHash(payment_hash_hash)), + }; + + let hold_lookup = hold_client + .list(list_request) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: format!("Could not fetch hold invoice: {e}"), + })? + .into_inner(); + + if hold_lookup.invoices.len() != 1 { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Transaction not found".to_owned(), + }); + } + + let hold_invoice = hold_lookup.invoices.into_iter().next().unwrap(); + let invoice_decoded = rpc + .call_typed(&DecodeRequest { + string: hold_invoice.invoice.clone(), + }) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + Ok((hold_invoice, invoice_decoded)) + } +} + pub async fn list_transactions_response( plugin: Plugin, params: nip47::ListTransactionsRequest, @@ -389,112 +408,8 @@ async fn list_transactions( if holdinvoice_support { let mut hold_client = plugin.state().hold_client.lock().clone().unwrap(); - - let lookup_request = ListRequest { constraint: None }; - - let hold_lookup = hold_client - .list(lookup_request) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Other, - message: format!("Could not fetch hold invoices: {e}"), - })? - .into_inner(); - - for hold_invoice in hold_lookup.invoices { - let invoice_decoded = rpc - .call_typed(&DecodeRequest { - string: hold_invoice.invoice.clone(), - }) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - })?; - - let description = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => invoice_decoded.offer_description, - DecodeType::BOLT11_INVOICE => invoice_decoded.description, - _ => continue, - }; - let description_hash = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => None, - DecodeType::BOLT11_INVOICE => { - invoice_decoded.description_hash.map(|h| h.to_string()) - } - _ => continue, - }; - - let created_at = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => { - Timestamp::from_secs(invoice_decoded.invoice_created_at.unwrap()) - } - DecodeType::BOLT11_INVOICE => { - Timestamp::from_secs(invoice_decoded.created_at.unwrap()) - } - _ => continue, - }; - - let amount = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => { - invoice_decoded.invoice_amount_msat.unwrap().msat() - } - DecodeType::BOLT11_INVOICE => { - if let Some(amt) = invoice_decoded.amount_msat { - amt.msat() - } else { - // amount: `any` but have to put a value... - 0 - } - } - _ => continue, - }; - - let expires_at = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => invoice_decoded - .invoice_relative_expiry - .map(|e_at| created_at + Timestamp::from_secs(u64::from(e_at))), - DecodeType::BOLT11_INVOICE => invoice_decoded - .expiry - .map(|e_at| created_at + Timestamp::from_secs(e_at)), - _ => continue, - }; - - let state = match hold_invoice.state() { - hold::InvoiceState::Unpaid => nip47::TransactionState::Pending, - hold::InvoiceState::Accepted => nip47::TransactionState::Accepted, - hold::InvoiceState::Paid => nip47::TransactionState::Settled, - hold::InvoiceState::Cancelled => nip47::TransactionState::Expired, - }; - - let settled_at = if hold_invoice.settled_at() != 0 { - Some(Timestamp::from_secs(hold_invoice.settled_at())) - } else { - None - }; - - let preimage = if hold_invoice.state() == hold::InvoiceState::Paid { - Some(hex::encode(hold_invoice.preimage())) - } else { - None - }; - - transactions.push(nip47::LookupInvoiceResponse { - transaction_type: Some(nip47::TransactionType::Incoming), - invoice: Some(hold_invoice.invoice.clone()), - description, - description_hash, - preimage, - payment_hash: hex::encode(hold_invoice.payment_hash), - amount, - fees_paid: 0, - created_at, - expires_at, - settled_at, - metadata: None, - state: Some(state), - }); - } + list_holdinvoices_to_transactions(&mut hold_client, &mut rpc, &mut transactions) + .await?; } } @@ -548,6 +463,114 @@ async fn list_transactions( Ok(transactions) } +async fn list_holdinvoices_to_transactions( + hold_client: &mut HoldClient, + rpc: &mut ClnRpc, + transactions: &mut Vec, +) -> Result<(), nip47::NIP47Error> { + let lookup_request = ListRequest { constraint: None }; + + let hold_lookup = hold_client + .list(lookup_request) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: format!("Could not fetch hold invoices: {e}"), + })? + .into_inner(); + + for hold_invoice in hold_lookup.invoices { + let invoice_decoded = rpc + .call_typed(&DecodeRequest { + string: hold_invoice.invoice.clone(), + }) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + + let description = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded.offer_description, + DecodeType::BOLT11_INVOICE => invoice_decoded.description, + _ => continue, + }; + let description_hash = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => None, + DecodeType::BOLT11_INVOICE => invoice_decoded.description_hash.map(|h| h.to_string()), + _ => continue, + }; + + let created_at = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => { + Timestamp::from_secs(invoice_decoded.invoice_created_at.unwrap()) + } + DecodeType::BOLT11_INVOICE => Timestamp::from_secs(invoice_decoded.created_at.unwrap()), + _ => continue, + }; + + let amount = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded.invoice_amount_msat.unwrap().msat(), + DecodeType::BOLT11_INVOICE => { + if let Some(amt) = invoice_decoded.amount_msat { + amt.msat() + } else { + // amount: `any` but have to put a value... + 0 + } + } + _ => continue, + }; + + let expires_at = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded + .invoice_relative_expiry + .map(|e_at| created_at + Timestamp::from_secs(u64::from(e_at))), + DecodeType::BOLT11_INVOICE => invoice_decoded + .expiry + .map(|e_at| created_at + Timestamp::from_secs(e_at)), + _ => continue, + }; + + let state = match hold_invoice.state() { + hold::InvoiceState::Unpaid => nip47::TransactionState::Pending, + hold::InvoiceState::Accepted => nip47::TransactionState::Accepted, + hold::InvoiceState::Paid => nip47::TransactionState::Settled, + hold::InvoiceState::Cancelled => nip47::TransactionState::Expired, + }; + + let settled_at = if hold_invoice.settled_at() != 0 { + Some(Timestamp::from_secs(hold_invoice.settled_at())) + } else { + None + }; + + let preimage = if hold_invoice.state() == hold::InvoiceState::Paid { + Some(hex::encode(hold_invoice.preimage())) + } else { + None + }; + + transactions.push(nip47::LookupInvoiceResponse { + transaction_type: Some(nip47::TransactionType::Incoming), + invoice: Some(hold_invoice.invoice.clone()), + description, + description_hash, + preimage, + payment_hash: hex::encode(hold_invoice.payment_hash), + amount, + fees_paid: 0, + created_at, + expires_at, + settled_at, + metadata: None, + state: Some(state), + }); + } + + Ok(()) +} + async fn make_lookup_response_from_listinvoices( rpc: &mut ClnRpc, list_invoice: ListinvoicesInvoices, @@ -557,10 +580,12 @@ async fn make_lookup_response_from_listinvoices( message: NOT_INV_ERR.to_owned(), }); - let invstring = if list_invoice.bolt11.is_some() { - list_invoice.bolt11.as_ref().unwrap() + let invstring = if let Some(bolt11) = list_invoice.bolt11 { + bolt11 + } else if let Some(bolt12) = list_invoice.bolt12 { + bolt12 } else { - list_invoice.bolt12.as_ref().unwrap() + return not_invoice_err; }; let invoice_decoded = rpc .call_typed(&DecodeRequest { @@ -628,7 +653,7 @@ async fn make_lookup_response_from_listinvoices( Ok(nip47::LookupInvoiceResponse { transaction_type: Some(nip47::TransactionType::Incoming), - invoice: Some(invstring.to_owned()), + invoice: Some(invstring), description, description_hash, preimage, diff --git a/src/nwc_notifications.rs b/src/nwc_notifications.rs index 34d2ca5..164e246 100644 --- a/src/nwc_notifications.rs +++ b/src/nwc_notifications.rs @@ -22,15 +22,13 @@ use nostr::{ Tag, Timestamp, event::FinalizeEventAsync, - key::{Keys, PublicKey, SecretKey}, nips::{nip04, nip44, nip47}, }; -use nostr_sdk::client::Client; use crate::{ OPT_NOTIFICATIONS, hold::{InvoiceState, ListRequest, TrackRequest, list_request::Constraint}, - structs::{NOT_INV_ERR, PluginState}, + structs::{NOT_INV_ERR, PluginState, WalletService}, }; pub async fn payment_received_handler( @@ -66,10 +64,14 @@ pub async fn payment_received_handler( let invoice = invoice_resp .first() .ok_or_else(|| anyhow!("invoice not found"))?; - let invstring = if invoice.bolt11.is_some() { - invoice.bolt11.as_ref().unwrap() + let invstring = if let Some(bolt11) = &invoice.bolt11 { + bolt11.clone() + } else if let Some(bolt12) = &invoice.bolt12 { + bolt12.clone() } else { - invoice.bolt12.as_ref().unwrap() + return Err(anyhow!( + "Listinvoices has neither returned bolt11 or bolt12 field" + )); }; let invoice_decoded = rpc @@ -83,8 +85,8 @@ pub async fn payment_received_handler( let clients = plugin.state().handles.lock().await; - for (client, client_pubkey, wallet_secret) in clients.values() { - send_notification(¬ification, client, client_pubkey.clone(), wallet_secret).await?; + for wallet_service in clients.values() { + send_notification(¬ification, wallet_service).await?; } Ok(()) @@ -92,7 +94,7 @@ pub async fn payment_received_handler( fn make_payment_received_from_listinvoices( invoice: &ListinvoicesInvoices, - invstring: &str, + invstring: String, invoice_decoded: DecodeResponse, ) -> Result { let not_invoice_err = Err(anyhow!(NOT_INV_ERR.to_owned())); @@ -160,7 +162,7 @@ fn make_payment_received_from_listinvoices( notification_type: nip47::NotificationType::PaymentReceived, notification: nip47::NotificationResult::PaymentReceived(nip47::PaymentNotification { transaction_type: Some(nip47::TransactionType::Incoming), - invoice: invstring.to_owned(), + invoice: invstring, description: description.clone(), description_hash: description_hash.clone(), preimage: preimage.clone(), @@ -220,8 +222,8 @@ pub async fn payment_sent_handler( let clients = plugin.state().handles.lock().await; - for (client, client_pubkey, wallet_secret) in clients.values() { - send_notification(¬ification, client, client_pubkey.clone(), wallet_secret).await?; + for wallet_service in clients.values() { + send_notification(¬ification, wallet_service).await?; } Ok(()) @@ -342,18 +344,19 @@ async fn make_payment_sent_from_listpays( async fn send_notification( notification: &String, - client: &Client, - client_pubkey: PublicKey, - wallet_secret: &Keys, + wallet_service: &WalletService, ) -> Result<(), anyhow::Error> { log::debug!("NOTIFICATION: {notification}"); - let content_encrypted_nip04 = - nip04::encrypt(wallet_secret.secret_key(), &client_pubkey, notification)?; + let content_encrypted_nip04 = nip04::encrypt( + wallet_service.wallet_secret.secret_key(), + &wallet_service.client_pubkey, + notification, + )?; let event_nip04 = EventBuilder::new(Kind::from_u16(23196), content_encrypted_nip04) - .tag(Tag::public_key(client_pubkey)) - .finalize_async(wallet_secret) + .tag(Tag::public_key(wallet_service.client_pubkey)) + .finalize_async(&wallet_service.wallet_secret) .await?; - let nip04_result = client.send_event(&event_nip04).await?; + let nip04_result = wallet_service.client.send_event(&event_nip04).await?; if nip04_result.success.is_empty() { log::warn!( "None of the relays accepted our nip04 notification: {}", @@ -367,16 +370,16 @@ async fn send_notification( log::debug!("NIP04 NOTIFICATION SENT: {event_nip04:?}"); let content_encrypted_nip44 = nip44::encrypt( - wallet_secret.secret_key(), - &client_pubkey, + wallet_service.wallet_secret.secret_key(), + &wallet_service.client_pubkey, notification, nip44::Version::V2, )?; let event_nip44 = EventBuilder::new(Kind::from_u16(23197), content_encrypted_nip44) - .tag(Tag::public_key(client_pubkey)) - .finalize_async(wallet_secret) + .tag(Tag::public_key(wallet_service.client_pubkey)) + .finalize_async(&wallet_service.wallet_secret) .await?; - let nip44_result = client.send_event(&event_nip44).await?; + let nip44_result = wallet_service.client.send_event(&event_nip44).await?; if nip44_result.success.is_empty() { log::warn!( "None of the relays accepted our nip44 notification: {}", @@ -392,6 +395,7 @@ async fn send_notification( Ok(()) } +#[allow(clippy::too_many_lines)] pub async fn holdinvoice_accepted_handler( plugin: Plugin, payment_hash: Vec, @@ -514,8 +518,8 @@ pub async fn holdinvoice_accepted_handler( }; let notification = serde_json::to_string(&content).unwrap(); - for (client, client_pubkey, wallet_secret) in clients.values() { - send_notification(¬ification, client, client_pubkey.clone(), wallet_secret).await?; + for wallet_service in clients.values() { + send_notification(¬ification, wallet_service).await?; } Ok(()) } diff --git a/src/rpc.rs b/src/rpc.rs index 06387aa..8b46ad0 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -155,7 +155,7 @@ pub async fn nwc_budget( clients .get(&label) .ok_or_else(|| anyhow!("No client found for label: {label}"))? - .0 + .client .clone(), method_capabilities, wallet_keys, diff --git a/src/structs.rs b/src/structs.rs index 22c166b..8f3b86f 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -15,8 +15,7 @@ pub const NOT_INV_ERR: &str = "Not an invoice or invalid invoice"; #[derive(Clone)] pub struct PluginState { pub config: Arc>, - pub handles: - Arc>>, + pub handles: Arc>>, pub rpc_lock: Arc>, pub budget_jobs: Arc>>>, pub hold_client: Arc>>>, @@ -33,6 +32,12 @@ impl PluginState { } } +pub struct WalletService { + pub client: client::Client, + pub client_pubkey: nostr::PublicKey, + pub wallet_secret: nostr::Keys, +} + #[derive(Clone, Debug)] pub struct Config { pub relays: Vec, diff --git a/tests/pyproject.toml b/tests/pyproject.toml index 874fadb..70483b9 100644 --- a/tests/pyproject.toml +++ b/tests/pyproject.toml @@ -6,12 +6,12 @@ description = "python dependencies for running tests" [dependency-groups] dev = [ - "pytest>=7.4,<9", + "pytest>=8,<10", "pytest-asyncio>=0.23.8,<2", "pytest-xdist>=3.7,<4", "pytest-timeout>=2.4,<3", "nostr-sdk>=0.44", - "pyln-testing>=25.5", - "pyln-client>=25.5", - "pyln-proto>=25.5", + "pyln-testing>=25.9", + "pyln-client>=25.9", + "pyln-proto>=25.9", ] diff --git a/tests/test_cln-nip47.py b/tests/test_cln-nip47.py old mode 100755 new mode 100644 index 03210a9..9b46a41 --- a/tests/test_cln-nip47.py +++ b/tests/test_cln-nip47.py @@ -1,43 +1,46 @@ +# ruff: noqa: DTZ005 + +import asyncio import hashlib import inspect import json import logging -import time -import asyncio import secrets +import time import uuid +from collections.abc import Awaitable, Callable from datetime import datetime, timedelta -from typing import Any, Awaitable, Callable, Union +from typing import Any, Union import pytest -from pyln.testing.fixtures import * # noqa: F403 -from pyln.testing.utils import RpcError, wait_for, TIMEOUT -from util import generate_random_label, get_plugin, get_hold # noqa: F401 - from nostr_sdk import ( Client, - RelayUrl, + Event, EventBuilder, Filter, - Event, + HandleNotification, Keys, KeysendTlvRecord, Kind, - HandleNotification, ListTransactionsRequest, LookupInvoiceRequest, MakeInvoiceRequest, + Method, NostrSdkError, NostrSigner, NostrWalletConnectUri, Nwc, PayInvoiceRequest, PayKeysendRequest, + PublicKey, + RelayUrl, Tag, TagKind, - Method, - PublicKey, + TransactionType, ) +from pyln.testing.fixtures import * +from pyln.testing.utils import TIMEOUT, RpcError, wait_for +from util import generate_random_label, get_hold, get_plugin # noqa: F401 LOGGER = logging.getLogger(__name__) @@ -83,7 +86,7 @@ async def fetch_event_responses( handler = NotificationHandler(events, stop_after) task = asyncio.create_task(client.handle_notifications(handler)) - time.sleep(1) + await asyncio.sleep(1) if inspect.iscoroutine(action): action_result = await action elif inspect.iscoroutinefunction(action): @@ -123,7 +126,7 @@ async def fetch_info_event( while events.len() < 1 and (datetime.now() - start_time) < timedelta( seconds=TIMEOUT ): - time.sleep(1) + await asyncio.sleep(1) events = await client.fetch_events( response_filter, timeout=timedelta(seconds=1) ) @@ -135,7 +138,7 @@ async def fetch_info_event( @pytest.mark.asyncio async def test_get_balance(nostr_relay, node_factory, get_plugin): # noqa: F811 url = nostr_relay - l1, l2 = node_factory.line_graph( + l1, _l2 = node_factory.line_graph( 2, wait_for_announce=True, opts=[ @@ -237,7 +240,7 @@ async def test_get_info(nostr_relay, node_factory, get_plugin): # noqa: F811 }, ) l1.daemon.wait_for_log("All NWC's loaded") - time.sleep(5) + await asyncio.sleep(5) await client.connect() info_event = await fetch_info_event(client, uri) get_info = await nwc.get_info() @@ -341,7 +344,7 @@ async def test_make_invoice(nostr_relay, node_factory, get_plugin): # noqa: F81 MakeInvoiceRequest( amount=3001, description="test2", - description_hash=hashlib.sha256("test2".encode()).hexdigest(), + description_hash=hashlib.sha256(b"test2").hexdigest(), expiry=120, ) ) @@ -367,7 +370,7 @@ async def test_make_invoice(nostr_relay, node_factory, get_plugin): # noqa: F81 MakeInvoiceRequest( amount=3001, description=None, - description_hash=hashlib.sha256("test2".encode()).hexdigest(), + description_hash=hashlib.sha256(b"test2").hexdigest(), expiry=120, ) ) @@ -378,7 +381,7 @@ async def test_make_invoice(nostr_relay, node_factory, get_plugin): # noqa: F81 MakeInvoiceRequest( amount=3001, description="test1", - description_hash=hashlib.sha256("test2".encode()).hexdigest(), + description_hash=hashlib.sha256(b"test2").hexdigest(), expiry=120, ) ) @@ -562,7 +565,7 @@ async def test_lookup_invoice(nostr_relay, node_factory, get_plugin): # noqa: F MakeInvoiceRequest( amount=3001, description="test2", - description_hash=hashlib.sha256("test2".encode()).hexdigest(), + description_hash=hashlib.sha256(b"test2").hexdigest(), expiry=1000, ) ) @@ -584,9 +587,7 @@ async def test_lookup_invoice(nostr_relay, node_factory, get_plugin): # noqa: F assert invoice_lookup.created_at.as_secs() == pytest.approx( invoice_decode["created_at"], abs=3 ) - assert ( - invoice_lookup.description_hash == hashlib.sha256("test2".encode()).hexdigest() - ) + assert invoice_lookup.description_hash == hashlib.sha256(b"test2").hexdigest() assert invoice_lookup.expires_at.as_secs() == pytest.approx( listpays_rpc["expires_at"], abs=3 ) @@ -613,9 +614,7 @@ async def test_lookup_invoice(nostr_relay, node_factory, get_plugin): # noqa: F assert invoice_lookup.created_at.as_secs() == pytest.approx( invoice_decode["created_at"], abs=3 ) - assert ( - invoice_lookup.description_hash == hashlib.sha256("test2".encode()).hexdigest() - ) + assert invoice_lookup.description_hash == hashlib.sha256(b"test2").hexdigest() assert invoice_lookup.expires_at.as_secs() == pytest.approx( listpays_rpc["expires_at"], abs=3 ) @@ -693,9 +692,9 @@ async def test_list_transactions(nostr_relay, node_factory, get_plugin): # noqa ], ) l1.rpc.call( - "pay", + "xpay", { - "bolt11": l2.rpc.call( + "invstring": l2.rpc.call( "invoice", { "amount_msat": 500000000, @@ -710,7 +709,7 @@ async def test_list_transactions(nostr_relay, node_factory, get_plugin): # noqa l2.rpc.call("listpeerchannels", [l1.info["id"]])["channels"][0][ "spendable_msat" ] - > 30001 + > 400000000 ) ) uri_str = l1.rpc.call("nip47-create", ["test1"])["uri"] @@ -741,7 +740,7 @@ async def test_list_transactions(nostr_relay, node_factory, get_plugin): # noqa amount=3000, description="test2", description_hash=None, expiry=None ) ) - result = l2.rpc.call("pay", [invoice.invoice]) + result = l2.rpc.call("xpay", [invoice.invoice]) invoice = await nwc.make_invoice( MakeInvoiceRequest( @@ -762,19 +761,23 @@ async def test_list_transactions(nostr_relay, node_factory, get_plugin): # noqa ) assert len(result) == 22 for tx in result: - tx.description is not None - tx.invoice is not None - tx.amount is not None - tx.created_at is not None - tx.description_hash is None - tx.expires_at is None - tx.preimage is not None - tx.settled_at is not None - tx.metadata is None - tx.transaction_type is not None - tx.state is not None - tx.payment_hash is not None - tx.fees_paid is not None + assert tx.description is not None + assert tx.invoice is not None + assert tx.amount is not None + assert tx.created_at is not None + assert tx.description_hash is None + assert tx.preimage is not None + assert tx.settled_at is not None + assert tx.metadata is None + assert tx.transaction_type is not None + assert tx.state is not None + assert tx.payment_hash is not None + assert tx.fees_paid is not None + + if tx.transaction_type == TransactionType.INCOMING: + assert tx.expires_at is not None + else: + assert tx.expires_at is None @pytest.mark.asyncio @@ -927,7 +930,7 @@ async def test_notifications(nostr_relay, node_factory, get_plugin): # noqa: F8 }, ) l1.daemon.wait_for_log("All NWC's loaded") - time.sleep(3) + await asyncio.sleep(3) await client.connect() await fetch_info_event(client, uri) @@ -940,7 +943,7 @@ async def test_notifications(nostr_relay, node_factory, get_plugin): # noqa: F8 }, ) with pytest.raises(AssertionError, match="0 == 1"): - (responses3, pay3) = await fetch_event_responses( + (_responses3, _pay3) = await fetch_event_responses( client, client_pubkey, 23196, @@ -1036,7 +1039,7 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 }, ) l1.daemon.wait_for_log("All NWC's loaded") - time.sleep(3) + await asyncio.sleep(3) uri = NostrWalletConnectUri.parse(uri_str) signer = NostrSigner.keys(Keys(uri.secret())) client = Client(signer) @@ -1066,7 +1069,7 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 }, ) l1.daemon.wait_for_log("All NWC's loaded") - time.sleep(3) + await asyncio.sleep(3) await client.connect() await fetch_info_event(client, uri) with pytest.raises(NostrSdkError.Generic, match="Payment exceeds budget"): @@ -1107,7 +1110,7 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 PayInvoiceRequest(id=None, amount=None, invoice=invoice_exceeded["bolt11"]) ) - time.sleep(11) + await asyncio.sleep(11) list = l1.rpc.call("nip47-list", ["test1"])[0] assert list["test1"]["budget_msat"] == 3000 @@ -1138,7 +1141,7 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 }, ) l1.daemon.wait_for_log("All NWC's loaded") - time.sleep(3) + await asyncio.sleep(3) await client.connect() await fetch_info_event(client, uri) @@ -1147,7 +1150,7 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 PayInvoiceRequest(id=None, amount=None, invoice=invoice_exceeded["bolt11"]) ) - time.sleep(11) + await asyncio.sleep(11) list = l1.rpc.call("nip47-list", ["test1"])[0] assert list["test1"]["budget_msat"] == 3000 @@ -1242,7 +1245,7 @@ async def test_budget_command(nostr_relay, node_factory, get_plugin): # noqa: F == "payment_received payment_sent" ) - time.sleep(18) + await asyncio.sleep(18) balance = await nwc.get_balance() assert balance == 5000 @@ -1706,7 +1709,7 @@ async def test_hold_invoice( start_time = datetime.now() while (datetime.now() - start_time) < timedelta(seconds=10): - time.sleep(1) + await asyncio.sleep(1) try: await nwc.lookup_invoice( LookupInvoiceRequest( @@ -1757,7 +1760,7 @@ async def test_hold_invoice( start_time = datetime.now() while (datetime.now() - start_time) < timedelta(seconds=10): - time.sleep(1) + await asyncio.sleep(1) try: await nwc.lookup_invoice( LookupInvoiceRequest( diff --git a/tests/uv.lock b/tests/uv.lock index 7baa4f7..d90c9ac 100644 --- a/tests/uv.lock +++ b/tests/uv.lock @@ -1,14 +1,9 @@ version = 1 revision = 3 requires-python = ">=3.10" - -[[package]] -name = "asn1crypto" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/de/cf/d547feed25b5244fcb9392e288ff9fdc3280b10260362fc45d37a798a6ee/asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c", size = 121080, upload-time = "2022-03-15T14:46:52.889Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/7f/09065fd9e27da0eda08b4d6897f1c13535066174cc023af248fc2a8d5e5a/asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67", size = 105045, upload-time = "2022-03-15T14:46:51.055Z" }, +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version < '3.11'", ] [[package]] @@ -40,88 +35,88 @@ wheels = [ [[package]] name = "bitarray" -version = "3.8.1" +version = "3.9.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/47/b5da717e7bbe97a6dc4c986f053ca55fd3276078d78f68f9e8b417d1425a/bitarray-3.8.1.tar.gz", hash = "sha256:f90bb3c680804ec9630bcf8c0965e54b4de84d33b17d7da57c87c30f0c64c6f5", size = 152471, upload-time = "2026-04-02T16:29:01.712Z" } +sdist = { url = "https://files.pythonhosted.org/packages/53/71/dd598d2d546d11d7aca6cd25f05875e2dad194df0a663f1892690d4fb90d/bitarray-3.9.2.tar.gz", hash = "sha256:37342f81c8f8e10ee5d1e23d5eda2e8dbd9d8d3a9d90e8285181fad57f21cdc1", size = 160932, upload-time = "2026-07-25T06:39:21.234Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/fc/4352b1dd55a50c85f7b502c011d40279a66a05eb0c6a5d3d44160838d9a4/bitarray-3.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30d42c34da2974a5e2e0b51c57ecf89892c1e83ed67e1084d1e27eefc27add91", size = 149074, upload-time = "2026-04-02T16:26:16.319Z" }, - { url = "https://files.pythonhosted.org/packages/34/06/104c9ff50e5230f6581056d6f4b0d1e0db14aba41549cae4b0541be0369c/bitarray-3.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0793c51d3b1c7410bde1f7254fff71fabff1bc0cdeba1fa51319ac4e7931df3d", size = 146031, upload-time = "2026-04-02T16:26:18.453Z" }, - { url = "https://files.pythonhosted.org/packages/65/0b/99d65fa6ceb3616c4b96ab9fef2dcd4994ad05fa48f595706ba001f13ba7/bitarray-3.8.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133648c3405564e7fef9103f1768cb018de1b4976f3d8beff09cd4acea73bfe4", size = 325129, upload-time = "2026-04-02T16:26:19.77Z" }, - { url = "https://files.pythonhosted.org/packages/18/d4/e0913c6b15fbd1e6b4d60a541a6784eb5d8f1fddcbcbb8c076240f665f2e/bitarray-3.8.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4fd3399eaf6f1c77ea3132611efbc3d7a8c0eb899793387b3266be221dc75fd", size = 353126, upload-time = "2026-04-02T16:26:21.274Z" }, - { url = "https://files.pythonhosted.org/packages/49/1b/0fd86dece4eca8078a99e54ca01183d5b660195dea8f2c8ad5740b190e9f/bitarray-3.8.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3b9790ae107fc8648155f120e80a58ef8e94424efefff5b355de84061de6a18b", size = 363588, upload-time = "2026-04-02T16:26:22.638Z" }, - { url = "https://files.pythonhosted.org/packages/ec/2d/e62ddf9e52a0124a19f2cd83be5dfa256c6c1f20722fb0bb4b0aed51bb0b/bitarray-3.8.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:af01133e78e5528ee282ceb1cf4bc54aecb937c2001913e751452ad7dffbbeb1", size = 331725, upload-time = "2026-04-02T16:26:24.296Z" }, - { url = "https://files.pythonhosted.org/packages/cd/fc/ea1c532169d56747c128f4e0256a3ad1f0c91ed00ca83cdf93964a60fec3/bitarray-3.8.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2da2ca9495668ab77132a911f6bd530d2bfe686d10467584894efc3b66e9ffb5", size = 322939, upload-time = "2026-04-02T16:26:25.904Z" }, - { url = "https://files.pythonhosted.org/packages/5b/01/8fe68993779f077c713fc4c21c0d9ba2719beeea596bcdc37f9660b6f181/bitarray-3.8.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:72a0e87b2196120523fc6194ca6b580fcffa12d7daa4d57a16d7838e60f82d0e", size = 351084, upload-time = "2026-04-02T16:26:27.396Z" }, - { url = "https://files.pythonhosted.org/packages/96/c5/f5cb62b60e0da428ef9457e7e1d9a3d3d8874b4f0f925adfff4b9ab3a319/bitarray-3.8.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:defa3c12cb06b2fd2066a9e21bf00aab96465be84d9585c8c05195f080510506", size = 347489, upload-time = "2026-04-02T16:26:28.935Z" }, - { url = "https://files.pythonhosted.org/packages/88/71/bb9baadbdd305f80def4220ce38266f53404433661492fc2c3d894129bfb/bitarray-3.8.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7eae9e763fbd32f19f2a66dfc2e37906f8422e0c4ad4a6c9dcf9d3246740812e", size = 328394, upload-time = "2026-04-02T16:26:30.585Z" }, - { url = "https://files.pythonhosted.org/packages/0c/1a/c4d487b029488bebef38a4c04df34294d27f313b5ab3491aa3a051394f24/bitarray-3.8.1-cp310-cp310-win32.whl", hash = "sha256:3b9358f6437a5fa0c765ffae5810c9830547baf4bcf469438b82845c3f33f998", size = 143245, upload-time = "2026-04-02T16:26:32.414Z" }, - { url = "https://files.pythonhosted.org/packages/98/ae/adadedf7cdd49fb8d81b8013d3471c193d6208035a0748205c808b1709cd/bitarray-3.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:6f92d12a46b2a67d56194bb5d226dabf586b386d1f1a5e25be5b745a3080dbba", size = 149976, upload-time = "2026-04-02T16:26:33.868Z" }, - { url = "https://files.pythonhosted.org/packages/26/de/2a7a8c2868d85e671a6cdb5282bbb299d98cdc0b4c4ade0cfa9a2a21d91d/bitarray-3.8.1-cp310-cp310-win_arm64.whl", hash = "sha256:8e12d50d4d65c74bd877e15c276992263b878456a7cfcf72521e7205a553557f", size = 146729, upload-time = "2026-04-02T16:26:35.216Z" }, - { url = "https://files.pythonhosted.org/packages/05/5c/32ace44d0313b4a9986d2abc3a1349744920dafcfb6a4e454a10ed09ef5a/bitarray-3.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:660e11b9932f58f10151d0febd11f77d3b0d48d6fa4dd4686d8983f40187101e", size = 149069, upload-time = "2026-04-02T16:26:36.671Z" }, - { url = "https://files.pythonhosted.org/packages/6d/85/7bd0a218478f0a226ddfb756dd64286f8ee3c61a17991a1a50aae8d89dca/bitarray-3.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fb1df55f5700187c6db4b47dbdaf8a0653a111341ac7fccc596b397aa3399e65", size = 146036, upload-time = "2026-04-02T16:26:38.179Z" }, - { url = "https://files.pythonhosted.org/packages/e8/e9/e4e6aec6874efac185959f4627b6a61a88c0dad3ec92eee433fd395daa78/bitarray-3.8.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:838fd67b3d00c5a64181073282a2c0bf8f76465da4844d5e79d2dbbc64c987dc", size = 333036, upload-time = "2026-04-02T16:26:39.723Z" }, - { url = "https://files.pythonhosted.org/packages/50/5f/d493eb77f79b58eaa489e9e032aa1c91f6af844287b341c6be681df11b0d/bitarray-3.8.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5743f532e408cfd716fa16776b5a6447b83ff2cf39021fb5f8d052aa0f331508", size = 361247, upload-time = "2026-04-02T16:26:41.023Z" }, - { url = "https://files.pythonhosted.org/packages/24/a3/2e3f33c66f61754b5bb4724d54c9c1122699facc580bcb416d44f1164ffc/bitarray-3.8.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0c8c66f5d8055cb84ad0ea14af57b3579cb0b6db589f2086f5e33f0922cf2354", size = 371922, upload-time = "2026-04-02T16:26:42.373Z" }, - { url = "https://files.pythonhosted.org/packages/05/03/4dfca9a69dfa69cde6fdbcfafbc039e069e105ea2443688177f6873d8444/bitarray-3.8.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8c3fe25871f1758519a3ad8dcafb1bd95c5d1aaeb122e6492ac739ab11fa5907", size = 339203, upload-time = "2026-04-02T16:26:43.915Z" }, - { url = "https://files.pythonhosted.org/packages/14/5d/a2275da6c935893f275624c88afab6cdd5b6aa916d0b45c50dd400cafb20/bitarray-3.8.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e9ff57452fcadfd1a379314234657b8f4e9967ae64480ddf7c2fd82139bc8cf8", size = 330956, upload-time = "2026-04-02T16:26:45.675Z" }, - { url = "https://files.pythonhosted.org/packages/2d/fd/7f4041c7a7e94ef3e7de86fdb4102d3fe366998b507de77ba0fe5dff6c44/bitarray-3.8.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4e34f1cb6cdb036c5f4a839a2b74419f75fa36177a70c4bab2867f48973cbe44", size = 358882, upload-time = "2026-04-02T16:26:47.327Z" }, - { url = "https://files.pythonhosted.org/packages/29/4e/2d0c381327c0f5bc49681b799bbe7d80d5e629079f9609a79d39da6e8b8f/bitarray-3.8.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:698c37fca3761af69a09a1d39cc0492f7e8cb9e263af39a288dce8f3b8a9e2bc", size = 355761, upload-time = "2026-04-02T16:26:48.665Z" }, - { url = "https://files.pythonhosted.org/packages/e8/d9/66644d45d9f844d1c78b80f3517c8717ac4b4d9853ec61bd02b3cabc06e6/bitarray-3.8.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:81ede1f094f26eeaff62e029ff1bc4e84e9d568f20d4669f64dcf7c7b18a28fc", size = 336422, upload-time = "2026-04-02T16:26:49.988Z" }, - { url = "https://files.pythonhosted.org/packages/ad/7d/4ea3fd2424535630d4d236bc0c721621260b39878eed669dbc1deb5c6b22/bitarray-3.8.1-cp311-cp311-win32.whl", hash = "sha256:8a345b5dc8ab8cafdf338e08530d48fe3f73df27f4ff569be793c7a7e7bb6b6b", size = 143391, upload-time = "2026-04-02T16:26:51.69Z" }, - { url = "https://files.pythonhosted.org/packages/d0/4f/46309fcf9e1793c7184e3fc1aa73d7daf2b6a2b0fa1efbcf8d497101690e/bitarray-3.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:ddcd25a1f72b2b545fb27e17882046a6c161f3f24514b2e028c00c58ed73a2dd", size = 150143, upload-time = "2026-04-02T16:26:52.9Z" }, - { url = "https://files.pythonhosted.org/packages/0e/1e/10289fb8e44fdd2d01adcc24d64b5c45ead709fbec76ee973f42e22b3059/bitarray-3.8.1-cp311-cp311-win_arm64.whl", hash = "sha256:dc2cab92c42991b711132bc52405680e075d1505d4356c4468bc6e9c93d49137", size = 147024, upload-time = "2026-04-02T16:26:54.151Z" }, - { url = "https://files.pythonhosted.org/packages/5d/4f/6ab3767b6642a6cbee4353f10a71fe25ade9899d539fae47c3d50686ebe2/bitarray-3.8.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4494c599effa16064f2b600f6eb28115182d6826847d795a55691339788d8a4d", size = 149202, upload-time = "2026-04-02T16:26:55.635Z" }, - { url = "https://files.pythonhosted.org/packages/eb/53/22bfffd13dd0a266f90011338b24eec45f25c91d37155bb2aa330351e17d/bitarray-3.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ff2ca039a161d49a8c713f5380def315c6f793df5fe348b94782b1dbee37a644", size = 145999, upload-time = "2026-04-02T16:26:56.849Z" }, - { url = "https://files.pythonhosted.org/packages/5d/dc/60aff29c88b648e18248921001cf9d7169abeda4d8db96f2dc1a24ed98ca/bitarray-3.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df3ffa6ef88166bb36f5d1492e71e664868b9b8b6afd55821e0ac0cb96625441", size = 335945, upload-time = "2026-04-02T16:26:58.403Z" }, - { url = "https://files.pythonhosted.org/packages/83/c8/225380610a01ae0d8f2f5256e531bae7135b2ade6f4607156424718ec43a/bitarray-3.8.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:478b9f0ea86f957624dd2b159066855716f78db94666e9b04babe85fc013e01b", size = 364213, upload-time = "2026-04-02T16:26:59.742Z" }, - { url = "https://files.pythonhosted.org/packages/6c/df/83899be9a74ec5878972e8b636f645ef1771e146c6425a161fdafdd74aaa/bitarray-3.8.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e127b2e7fc533728295196f9265d12834530f475bc6cd6f74619df415d04b8b1", size = 375409, upload-time = "2026-04-02T16:27:01.081Z" }, - { url = "https://files.pythonhosted.org/packages/6c/93/38bc15cb097107d220a942eb66dc50882496d7da54f41e5eea6c31b1c443/bitarray-3.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6ef49462a615de062dcac8281944d0b036fe1e9c96a6c690bf6cf5e4b5488f0e", size = 343645, upload-time = "2026-04-02T16:27:02.577Z" }, - { url = "https://files.pythonhosted.org/packages/5a/c3/75fae6991946f8bf643ec50233432ea81b5b65bfdb2918b09d7e37605380/bitarray-3.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4da256fc567a57ded2a4aa962fc9e9d430ab740e5c67be9e98a63ef4eb467f2f", size = 333844, upload-time = "2026-04-02T16:27:03.963Z" }, - { url = "https://files.pythonhosted.org/packages/b7/7e/649e7c3bb12ba938c387bcad6a6c0b84312663c9807ec1457888936690d8/bitarray-3.8.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b46b7aec9272fd81c984e723e599957629a91204120b3e7f0933f138e0792fdf", size = 361267, upload-time = "2026-04-02T16:27:05.361Z" }, - { url = "https://files.pythonhosted.org/packages/cd/5f/db0fb71a7c6c3ef047b84256157e96fa35e10ed8b79b80e892d354ab37f6/bitarray-3.8.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2dc07dab252c63c4f6600e200b26fa05207db6b650d41ae88ab0cec4d6c59459", size = 359373, upload-time = "2026-04-02T16:27:07.106Z" }, - { url = "https://files.pythonhosted.org/packages/b6/b6/a082d84cba7ba509b48d160034f6a2d31df6bf4fff0471801e888bba96c9/bitarray-3.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:29c8c10a49d6a9586f592116618b99c3dabcb24d881b7a649e0691ef87f314c4", size = 340633, upload-time = "2026-04-02T16:27:08.794Z" }, - { url = "https://files.pythonhosted.org/packages/7f/b7/1ba7ec1f3aa62933dfef505b09de0b75778a3cb05984ee8bb798539381db/bitarray-3.8.1-cp312-cp312-win32.whl", hash = "sha256:67125404d12547443d74113862a80c10310cf875aff8dbfc5548fee1d9737123", size = 143521, upload-time = "2026-04-02T16:27:10.423Z" }, - { url = "https://files.pythonhosted.org/packages/82/30/5ff9d30a1121810f336517e51b1cbdea0fa92e92b142efe0741e335dc14e/bitarray-3.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:ba0339d6aa80615a17f47fabc5700485e9469121d658458f95cdd2003288c28b", size = 150451, upload-time = "2026-04-02T16:27:11.993Z" }, - { url = "https://files.pythonhosted.org/packages/a6/08/51e49eb09ca45ecda4a5f05b70a10977a5f0ac39967c79479e9d3e41cb29/bitarray-3.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:c0b367a00e8c88a714b2384c97dedcc85340547b3a54b6037a42fca5554d0576", size = 147218, upload-time = "2026-04-02T16:27:13.566Z" }, - { url = "https://files.pythonhosted.org/packages/13/79/015a30f40f716a0372907a7ac5c399db5428209dcf264b85ef1305f9b3e2/bitarray-3.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:55f4b105a1686eb486069a9e578d502d1998e890d8144012225de9e0450aeabd", size = 149201, upload-time = "2026-04-02T16:27:15.383Z" }, - { url = "https://files.pythonhosted.org/packages/23/fe/f70b150ea9a330daecc546a5a63576ba2d6b3bacc1ccde42abc9dd35a1ad/bitarray-3.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b3118ec012a799456f7fca6cc002c078590578b7640fbaab52d8ecb9a651f1c1", size = 146001, upload-time = "2026-04-02T16:27:17.041Z" }, - { url = "https://files.pythonhosted.org/packages/78/49/2c637658851ea0408c7375f5f278c0ebb69cbe861f8fcc9477db14ee7fa2/bitarray-3.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2762db8049b230520358ac742cbc57bceaacebe34e5d25c096f2b4bc3887a3a8", size = 335162, upload-time = "2026-04-02T16:27:18.587Z" }, - { url = "https://files.pythonhosted.org/packages/d3/3c/ae665a0b2d6183cc706c03b683b7f9ad53195731379ab82dfa537e73f70f/bitarray-3.8.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5b67b869f860eb19055e2560844d8c7d0935245938935bdb764b3e683e2014e2", size = 363031, upload-time = "2026-04-02T16:27:19.98Z" }, - { url = "https://files.pythonhosted.org/packages/2a/ee/7b7c37fbb15209525f0daff1a51a042c035e931ebd526aabb483fdc7a476/bitarray-3.8.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0a661f3492462e7adf8a054fb7414a22fc8251f1e18b9d8cbcf008d2dc85f012", size = 374623, upload-time = "2026-04-02T16:27:21.468Z" }, - { url = "https://files.pythonhosted.org/packages/96/dd/26a17534742561974e5b2a3448d70fd8d370ed885bd88bbbb36bdd022875/bitarray-3.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:300e3026d17ae3328320ba78d3165bdb1c43d0dfdbc461a69ebbdc005d9ce0b3", size = 342850, upload-time = "2026-04-02T16:27:22.814Z" }, - { url = "https://files.pythonhosted.org/packages/58/f1/97410e88a8b441c1a6e5841c651e483787c3c87f2b98c1d2421aee23790d/bitarray-3.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ad5a71c1ef4a2e404c2c888db09226c821d9d14eff8813e1da873572f5fbb89d", size = 333109, upload-time = "2026-04-02T16:27:24.271Z" }, - { url = "https://files.pythonhosted.org/packages/64/1a/74a3af2d314ec6a035ae8f139491ace4fc8b3362bfdc86aee652b8f15be5/bitarray-3.8.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:78cbda57a2808d994517b53571eaa2d9299359f63aa71cf4bc94210169aad8b1", size = 360334, upload-time = "2026-04-02T16:27:25.999Z" }, - { url = "https://files.pythonhosted.org/packages/d8/86/01ea58ca9795401489f9de662ef9ba759d6712870696a5806441b2c14224/bitarray-3.8.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:89c7c125a0913d71ba9cc1fa8e14c7cfe1517b1c1f45416e1f9babcedd3b545d", size = 358674, upload-time = "2026-04-02T16:27:27.597Z" }, - { url = "https://files.pythonhosted.org/packages/68/c9/14587fd3c712047af60a875889ad69926386c3fdbf8061e9baf23d12d997/bitarray-3.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7875abfd90f2ae3aa22d50f3fa1c93bbae456458cc73d3179b838f07bed1fc10", size = 339689, upload-time = "2026-04-02T16:27:29.14Z" }, - { url = "https://files.pythonhosted.org/packages/5b/82/a8cc5172dba50c90d7cc89d9f5c1cfb99deb77af10b4762eb75ece52e20a/bitarray-3.8.1-cp313-cp313-win32.whl", hash = "sha256:21add0aa968496a2bd8341d85720d09808e22e0adc7dbefc1e0f8f67c4b83f36", size = 143503, upload-time = "2026-04-02T16:27:30.948Z" }, - { url = "https://files.pythonhosted.org/packages/7f/b2/f647dcd098c275a67b89d21c92471180996a797cec11e308b4d1936d170d/bitarray-3.8.1-cp313-cp313-win_amd64.whl", hash = "sha256:40d1b57012bf9b4fefd25345aaa95aab3ca510cc693f33c2cb02a4b771d8e51a", size = 150441, upload-time = "2026-04-02T16:27:32.642Z" }, - { url = "https://files.pythonhosted.org/packages/ba/78/bde39d566f70149c6858c7e61c0a0d902a643a136a56dd37b6135cc59a68/bitarray-3.8.1-cp313-cp313-win_arm64.whl", hash = "sha256:72b32d8c471930c95d49640ec99f7694f9b040ca1342ff03ed69d3aea90f9339", size = 147209, upload-time = "2026-04-02T16:27:34.289Z" }, - { url = "https://files.pythonhosted.org/packages/b0/3b/d07ee3ef120a3b3f1db2434c4b955fbf900bb3f878e25a71ee82408e9d91/bitarray-3.8.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fe989bbed9d6f332c1e24d333936f3fa1375f380cd8028da0b985dcdefa6015a", size = 149181, upload-time = "2026-04-02T16:27:35.608Z" }, - { url = "https://files.pythonhosted.org/packages/ab/bf/43bf76bbf95354e74b80923e8aa7d6cb178e25546eeab0705524ad4d5171/bitarray-3.8.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:75e33c9187da271d1dbeb2582ab2df2e441346492098f67559b09173ea4edde4", size = 146020, upload-time = "2026-04-02T16:27:37.279Z" }, - { url = "https://files.pythonhosted.org/packages/89/8c/868644e4f61220529ceea0be6dff1c659a7c20dc354f8c5aa367409e6150/bitarray-3.8.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fd7e3158be382f8f140caccc0dc7742a7553ce4bf2978982abe3054d2cedd705", size = 335102, upload-time = "2026-04-02T16:27:39.065Z" }, - { url = "https://files.pythonhosted.org/packages/f9/6f/0eb0ed1214bb6436e078a44006127685b587b6aeb1600bc2f77bf53e96b9/bitarray-3.8.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9fa5620f7f352f9706924c0e2071a212be36421f09ee064b0fd7e1128289fcdb", size = 363405, upload-time = "2026-04-02T16:27:40.596Z" }, - { url = "https://files.pythonhosted.org/packages/25/eb/07d6ec5ad40792ff92857ac51cb91c01f855e3edb7b589eb099937420722/bitarray-3.8.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:190b20cbffc9cd7f308f7a57d406119c3af3ae197613325fd2d92d99c8882ad6", size = 374225, upload-time = "2026-04-02T16:27:42.101Z" }, - { url = "https://files.pythonhosted.org/packages/46/53/2c5d688ea0f91025d3fdd08e13f9d5195c384953961070ce79719efd18b3/bitarray-3.8.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ec3d0a6c37a816ea6e3550697c60d90861c9b0f982a98a40b59ac1f7a360bfa9", size = 342742, upload-time = "2026-04-02T16:27:43.706Z" }, - { url = "https://files.pythonhosted.org/packages/2e/8a/25a932f02a8d1ca0c97cae62399f475d219669dbfecca3b2d7567effec73/bitarray-3.8.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:746e25f17ba4203b5933773782cf2d30bca5cdb66a9ba5d48a53a6c795aedc57", size = 333236, upload-time = "2026-04-02T16:27:45.237Z" }, - { url = "https://files.pythonhosted.org/packages/fe/a2/83fc66eb64ee0e74dc04894fbe8ae7e2d083c824ae9c1396d68a14c50760/bitarray-3.8.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ab363a5baae965fb3438f2137583853ad9c77d7e45f2a62ba63e609a34d792ea", size = 360526, upload-time = "2026-04-02T16:27:46.724Z" }, - { url = "https://files.pythonhosted.org/packages/a1/60/eee517c36956d9fc8d4ae2b2fbcf9122477b0730dacd52a2800226b11e61/bitarray-3.8.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5e30d8e399f38ae1ec86aa9be76d20ba15872dd0c41b4b46d1b78905857363b9", size = 358208, upload-time = "2026-04-02T16:27:48.221Z" }, - { url = "https://files.pythonhosted.org/packages/f4/a5/2d35fb2d1abc8afa4fef93f3ad96192eebd81595c3f9389a95f5d01ee782/bitarray-3.8.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0f099a4a77daf9bb99787070854894fe588c7d6988ea729f970ba2b3b82c7559", size = 339373, upload-time = "2026-04-02T16:27:49.911Z" }, - { url = "https://files.pythonhosted.org/packages/f4/b5/64d6d485725076472e0f151643ac4fa78ed54f10f6b7bf9620690a24af7a/bitarray-3.8.1-cp314-cp314-win32.whl", hash = "sha256:539880ddf9a8cc54c9e6126e7d072c991563f0c90ef73b3519a783d53df00352", size = 142632, upload-time = "2026-04-02T16:27:51.371Z" }, - { url = "https://files.pythonhosted.org/packages/04/e9/cf02dfac88f4c7d3de2dbafec4ec0616eaf9547dd7be98e81dc0fde97a77/bitarray-3.8.1-cp314-cp314-win_amd64.whl", hash = "sha256:c08cd5b19c570e1e9e094a6ce70d35bb39d12360e0763474ed9374229f174fcc", size = 149180, upload-time = "2026-04-02T16:27:53.027Z" }, - { url = "https://files.pythonhosted.org/packages/ef/a8/8e56397347bad7b042aefee9afc0cb085f2a779f7c8cc38954b12671d37c/bitarray-3.8.1-cp314-cp314-win_arm64.whl", hash = "sha256:0da5f17bed67ffe1d72f79fbf98403513a6e51a4f9b8293c1ff8a64e121242be", size = 146389, upload-time = "2026-04-02T16:27:54.759Z" }, - { url = "https://files.pythonhosted.org/packages/ff/a6/52bcd001c5cdf5c381a7317b07157070be1a6bc7fa5d58314ef6da33626b/bitarray-3.8.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:154a19e1dcd430494fdad7d1a0fb36383baaa363e1cb9d5a7b744cd2418c44d2", size = 150091, upload-time = "2026-04-02T16:27:56.154Z" }, - { url = "https://files.pythonhosted.org/packages/b6/22/86f51124a9d0e622be0bd171b797e0d507d5c9d6f76f5b97cb12e4ecf113/bitarray-3.8.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:814bb54db2a016026efc055a3527461e5eb551c0d91b32eeade003829ff84311", size = 147130, upload-time = "2026-04-02T16:27:57.916Z" }, - { url = "https://files.pythonhosted.org/packages/eb/d4/2153c6de23e26d0bfa65e0994ef771f06f8697a9ae65473923f6922ab1b9/bitarray-3.8.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac49519fcfeb4a7ecdf6b7d0ec6cac409e59f94c1bb54630db577a97893b6e38", size = 343168, upload-time = "2026-04-02T16:27:59.345Z" }, - { url = "https://files.pythonhosted.org/packages/55/bd/6ff9be5965c11e2f67bec674cd1bbe41e81531ec970251986f4d4978a72d/bitarray-3.8.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:329b994944993c45c3845047476ef4f231fe1a53972f18f8d005fd12fac163e1", size = 371961, upload-time = "2026-04-02T16:28:00.956Z" }, - { url = "https://files.pythonhosted.org/packages/7d/24/9aca563d253ed28be47b9a8d5f2fe0942e0191bc4ef49589e7670177807c/bitarray-3.8.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1d7b786a1ddd9b8dda17c445060a94a465cba2e113603ae7bdc5364efc1efd11", size = 381778, upload-time = "2026-04-02T16:28:02.589Z" }, - { url = "https://files.pythonhosted.org/packages/be/e2/81ac4c98694857b7eabdb9ada77db5b44fb6b6d5d19a3a716fb8a486c251/bitarray-3.8.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd9b848c17ef034f2ae31b2a1bd9276710c2baf03509f1f3fa4dc4382b0a1b53", size = 348165, upload-time = "2026-04-02T16:28:04.431Z" }, - { url = "https://files.pythonhosted.org/packages/ee/c5/2107bf1474a139f934621703135985f2acfae92d786561edde62ec557f60/bitarray-3.8.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0a33f8931ac91ebc23ce4decb99ed8fdddba2bafd2af3bb2781bcfd9878d4822", size = 340225, upload-time = "2026-04-02T16:28:05.899Z" }, - { url = "https://files.pythonhosted.org/packages/31/95/a3d2571055279a09373d1f93249404ac37a34045e334935adbc2ce780f83/bitarray-3.8.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07626f76a248fce5ebbb10fb0d4899d3c7f908ba21cb2fb4f5a7a9daf24c20cd", size = 369440, upload-time = "2026-04-02T16:28:07.49Z" }, - { url = "https://files.pythonhosted.org/packages/ef/56/7fad5bb52c0b9cdcdea4405f5d9a41f5efbf2873045d668bc2b8db10213b/bitarray-3.8.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:18f3a2c8908e63a66d3994808254397a5f989b1fb91087c33739f62bf1a1a064", size = 364733, upload-time = "2026-04-02T16:28:09.086Z" }, - { url = "https://files.pythonhosted.org/packages/10/b0/a23a1c312206c65146021aea68d69c8c7d817ae2f99698cbc23b3c744bba/bitarray-3.8.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ced27af6aee28782260bfa5643797937e96a6489bca972202834017208cf74f5", size = 343729, upload-time = "2026-04-02T16:28:11.09Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a0/fe5dbdfadcba2314551614d023db100e3aea7b09da2cdcbf1386f5c797a6/bitarray-3.8.1-cp314-cp314t-win32.whl", hash = "sha256:cf99e36c0f6ae5643ecef7ad7e1194aeb4a9798d9cff60b20ac041533fa6db0a", size = 144160, upload-time = "2026-04-02T16:28:12.702Z" }, - { url = "https://files.pythonhosted.org/packages/4e/0a/e95ed44f6d89e63ed666755fc0773223b10df0058d5de30d529f4cf35948/bitarray-3.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:9befda0dbd27ed95fba1c26be4bf98a49ba166b3c91beb5fc04364c130ce950c", size = 150852, upload-time = "2026-04-02T16:28:14.149Z" }, - { url = "https://files.pythonhosted.org/packages/0f/90/454b88b193743b4cd0fce0819a11b1c43b7f629cb2533f6ddc62cbb5e097/bitarray-3.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:4b7d7d10a1c82050efbb9a83d7a43974f70cf8f021afb86463b42e4ac4e5a46b", size = 147343, upload-time = "2026-04-02T16:28:15.632Z" }, + { url = "https://files.pythonhosted.org/packages/df/0b/8509a00dc5ab2945e9856582f8a4908a78f15a7a08b425dc067f733299d1/bitarray-3.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a334e162a24c4e85babffb78823d83aa04c882a7dd40c2106b668a8cde3da752", size = 156246, upload-time = "2026-07-25T06:37:14.799Z" }, + { url = "https://files.pythonhosted.org/packages/0a/1e/40d32865af46ff2c8a4ee488c60f1dd3724f3ff2c8405cb6594e75685c7d/bitarray-3.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8855aab242b4865e7ac7e14b6ce80f4cdf213984f260f31d92505d6877c620f9", size = 152874, upload-time = "2026-07-25T06:37:16.141Z" }, + { url = "https://files.pythonhosted.org/packages/12/07/0a646e68fa1fdb73e260474fdca595b93a7ecae6da49ac1d4c34be2b3b08/bitarray-3.9.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf06a3a5263268a35ed0d5513309791040ec8e8d3089bb5af12f51979dbf0a2a", size = 350232, upload-time = "2026-07-25T06:37:17.071Z" }, + { url = "https://files.pythonhosted.org/packages/38/5c/c1926f34269fb112d4b8b2b4fb0b84fc47b02268c30b300d237cccf07060/bitarray-3.9.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b82130cfba659e6a9da81298f43be678e99c598e64d814cfa86f0df8c1e230aa", size = 372334, upload-time = "2026-07-25T06:37:18.165Z" }, + { url = "https://files.pythonhosted.org/packages/42/b8/9937340228a814d72194543a1d452d552e3ea45d1537e27914deff41bbe0/bitarray-3.9.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ebc32a4fd8659a848d2c92ca5d75a1029cff9711bedbf3a32c515a19d4b3868", size = 384431, upload-time = "2026-07-25T06:37:19.251Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f1/311991e38cae3aa2b0f48726578e98aa584157c1f5bf4c8df51ed5a273d0/bitarray-3.9.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:047e6ad06ffe606cdc9d75b83effc5f14ab11f30bf6ca38fd98553410e23880b", size = 353328, upload-time = "2026-07-25T06:37:20.383Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e0/7087f78801ad89fedf4812bd9cf46c204e48c1cfbf83bd0d8eede7dbd076/bitarray-3.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:42e7735c20e3e8e8044bd566ffa8461ad4870a8459c5d3fe9d43984eaa2d6104", size = 348330, upload-time = "2026-07-25T06:37:21.579Z" }, + { url = "https://files.pythonhosted.org/packages/71/b9/42b7265266ceee164942e34c278e3982618ff022b40771004693c6a64055/bitarray-3.9.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:23aa70b05518ee20d965a8cbd1e168ba4085647f12d7bdb654412a253c476652", size = 370012, upload-time = "2026-07-25T06:37:22.584Z" }, + { url = "https://files.pythonhosted.org/packages/11/11/a80fde6c902cdef198a20e218a1005f09d9bc5266a8db310cfd2468d0e7b/bitarray-3.9.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0197a8970233d65e04357308d52e5310ea8219e07587f8db8a98d7ac21b7de50", size = 365747, upload-time = "2026-07-25T06:37:23.791Z" }, + { url = "https://files.pythonhosted.org/packages/30/6b/01ae2bfc9778ff2bc78c20c1fe5a80b7290baa1b57e98f04435eafa09a68/bitarray-3.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1a5b7919ab373df270f268ab35c49aa311ed06412290313cecfae9406003ee", size = 350281, upload-time = "2026-07-25T06:37:25.015Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ec/1ccde07e363791274c6f4f376d1e844254d97ea762fd25a2372a1e56f92a/bitarray-3.9.2-cp310-cp310-win32.whl", hash = "sha256:3aba64f5b449f50498185c81ba16dc9b819b06d985630f1b630e836081cf5051", size = 148006, upload-time = "2026-07-25T06:37:26.138Z" }, + { url = "https://files.pythonhosted.org/packages/85/26/ee9cbd0b3df4cb9939bbbe41b0e9fca5185c33b0788eb5129a432784bf58/bitarray-3.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:3a6bda7bd4a77926a18dc9890acad77ae96c2e4d9ff163a20438a67a23d7d956", size = 157184, upload-time = "2026-07-25T06:37:27.093Z" }, + { url = "https://files.pythonhosted.org/packages/bd/ae/04a86f8fc6a57205b27bad5e201aaa3bdafd70324eb4407c764018b712a2/bitarray-3.9.2-cp310-cp310-win_arm64.whl", hash = "sha256:4f9576e2e417eb4b45257c135b40d5e465969382dbf4291377f4b7327ca74603", size = 153173, upload-time = "2026-07-25T06:37:28.061Z" }, + { url = "https://files.pythonhosted.org/packages/61/cf/1c8efde1c873445d7609e2b1576edb29a7b946a8135a8a5f60b5e775fb79/bitarray-3.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:38c850db9ae8602e21c5c29193546a7ac85f1a70adfd4d65853154825675fce2", size = 156241, upload-time = "2026-07-25T06:37:29.185Z" }, + { url = "https://files.pythonhosted.org/packages/bb/2e/21ebb66fb272295c1e90ee04245e00f3655109b95881cf014485fae35526/bitarray-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:06df682d5b9ef233a21fab71912f131b2c295913202f2ad5370e44b0e0b26241", size = 152869, upload-time = "2026-07-25T06:37:30.329Z" }, + { url = "https://files.pythonhosted.org/packages/e7/db/4af8e55d377c25ca37a1da550d1852e44fd280ce5b550bef91d5aa52d2e9/bitarray-3.9.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0c064263c54fffe8fdfe8e20074816c67ec70608b0efb42a7d9fdf35ef67271", size = 359514, upload-time = "2026-07-25T06:37:31.384Z" }, + { url = "https://files.pythonhosted.org/packages/b6/20/464b03e4388d2093998bb355de1396d318728f9d8e1cc715770295babda7/bitarray-3.9.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0a61eac6a5a47cc7bc458b1f0737a701767543949580605cd85272c48121fb0a", size = 381209, upload-time = "2026-07-25T06:37:32.499Z" }, + { url = "https://files.pythonhosted.org/packages/02/fd/f24ec964e5737180957366473b664f8587a34dc7792e674a9d6c839db2e6/bitarray-3.9.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b62d7de8e4dc4ffbff407e1c7f868b84a00ac4a9919e28755fd299e86740fced", size = 393701, upload-time = "2026-07-25T06:37:33.501Z" }, + { url = "https://files.pythonhosted.org/packages/f4/c2/0ac619df1eb86593c7b7d833325cfa6cf5a3bcd55f3fa0871e30e1d6036d/bitarray-3.9.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ea75f495073f95e96e3f3049411854b5db1693901d8b324439a170ef45088fa", size = 362278, upload-time = "2026-07-25T06:37:34.691Z" }, + { url = "https://files.pythonhosted.org/packages/23/6b/3325564d17cebb71f6beca6b41d463d1083acc68ae06abb3ef2729f78b46/bitarray-3.9.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f3eb99f3cc4ef93b4830e8532ba506be4105f15daddba9302bbd96f24b1cf72", size = 357205, upload-time = "2026-07-25T06:37:35.764Z" }, + { url = "https://files.pythonhosted.org/packages/16/37/6253ca3aa44d257c8a70ce9bb98c0d05302bff586979c22b2264c9ae4457/bitarray-3.9.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e22236531f0193457277a7340d8bdd006f202f563f22f29abb873dc0850eb472", size = 378079, upload-time = "2026-07-25T06:37:36.958Z" }, + { url = "https://files.pythonhosted.org/packages/04/4f/e7595cb740e1b296f601b0d1292702b867292204e8f4ff010772f3a75c02/bitarray-3.9.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:92a438122d35d5e5703983c7dda7de90fc25c50282ce60f73ad61a6ced3ce0bc", size = 374941, upload-time = "2026-07-25T06:37:38.021Z" }, + { url = "https://files.pythonhosted.org/packages/87/61/135e3b0b42d65577725f2ddada426fce35cf816bc7d07e954ff6194b1193/bitarray-3.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:47ec58fceafd38d5839bb9d8262ca6150e222f81d1e4adc8feafda6caf360334", size = 359249, upload-time = "2026-07-25T06:37:39.079Z" }, + { url = "https://files.pythonhosted.org/packages/a1/7e/9b7a01e4d5d634f36fc1151854fdc61c09678b53dfd667f66baaff7efbff/bitarray-3.9.2-cp311-cp311-win32.whl", hash = "sha256:e8ec38401d9370684709f8cc3a0febbb742edf9fbacb1e432bcad8d023990e6d", size = 148130, upload-time = "2026-07-25T06:37:40.15Z" }, + { url = "https://files.pythonhosted.org/packages/fe/be/ee0813622d5fc2c6419aa5ec8e12407593f56c41a6e70fe1b8092b158c82/bitarray-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:30e2516878f7745957948e1a2d877f56ceda13b5f6b51d92d2174ef1756ab473", size = 157489, upload-time = "2026-07-25T06:37:41.158Z" }, + { url = "https://files.pythonhosted.org/packages/26/4c/1ad11f39eb6b1851777fb25185e839d19d273f2350c5e1bcbf0e014a7fda/bitarray-3.9.2-cp311-cp311-win_arm64.whl", hash = "sha256:89c87ffdb399a53821cb0f5566a7d04e0f2870096a4964b038866a894b40c9c6", size = 153465, upload-time = "2026-07-25T06:37:42.151Z" }, + { url = "https://files.pythonhosted.org/packages/73/c3/6d33775d73e22cfe3fe23dae2ed50dbabdb38ea651e8b5c983262c39ce26/bitarray-3.9.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c967be9bd2fadd58e0d2c4e139e6e7443b5dd9162e52e43ef5e2489218c4fecb", size = 156385, upload-time = "2026-07-25T06:37:43.505Z" }, + { url = "https://files.pythonhosted.org/packages/9c/9f/24b22bfa3016fdd50966966f7dec0ecf11a911429be5178880228068e108/bitarray-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c17c918e569a81a2dd87794e15991c9643accf72b5c005e8791cdedcb2d6189", size = 152941, upload-time = "2026-07-25T06:37:44.64Z" }, + { url = "https://files.pythonhosted.org/packages/48/61/affb7fd0ce338529b57eea7d1fda983b4f4adde89eb7ea8e4201c4aa4e47/bitarray-3.9.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eea0dc21749eeb53b163d45c3dd0524e62c0301dd512bfb4f5ef34ccce7be6ad", size = 362020, upload-time = "2026-07-25T06:37:45.687Z" }, + { url = "https://files.pythonhosted.org/packages/31/e1/43f6eed3ae86d08c11eb424ad0c7ffd40bb081eb50c682c76adeead3ed0d/bitarray-3.9.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:64fb672905884fc236ef200b9fbde529c6673a8217c62bf8651bdce4842ba366", size = 382732, upload-time = "2026-07-25T06:37:47.212Z" }, + { url = "https://files.pythonhosted.org/packages/12/f1/cf664f54fef3c5c921e7133a2795201e508c2c984ffe7c0f3f851f2b976d/bitarray-3.9.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:be25330b9da322a806b29bae0147e6ea3730b6f85a5036e25f8e50247fee013a", size = 396696, upload-time = "2026-07-25T06:37:48.309Z" }, + { url = "https://files.pythonhosted.org/packages/35/fc/6d6409ec30dabd388b6f3ee6c461eae353f04d95dc287f7be19fffff8fcc/bitarray-3.9.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7e14fd457ccea13e0adca60d93ffcd17b70184e7f485a986523241a077f3ac9", size = 366507, upload-time = "2026-07-25T06:37:49.671Z" }, + { url = "https://files.pythonhosted.org/packages/37/47/d241a6dde522a213cd624a820e0be998b0e5548d141098e990bba03913cd/bitarray-3.9.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f719003b0d692d3088e8f783ec684bf7caf00bddb5392efbe7842ba1ff15e8e", size = 359937, upload-time = "2026-07-25T06:37:50.725Z" }, + { url = "https://files.pythonhosted.org/packages/d3/a6/0f4dd5a813f87b79e07e4bece697e872f4836486fca92903c63cc75c42db/bitarray-3.9.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a72e6b82f1800a08dff90a9d401ffd3f8c278644e4f6711a7458c9f16ee9446e", size = 379627, upload-time = "2026-07-25T06:37:51.884Z" }, + { url = "https://files.pythonhosted.org/packages/cc/27/cbc55ea191b52710c19702cbbae7814ff55082b02a4c23aba11c23dd1990/bitarray-3.9.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d043ef192ae741f0524b3d3404a2e6e8dbb6affeda421d40d46456cf6cfa1064", size = 378404, upload-time = "2026-07-25T06:37:53.43Z" }, + { url = "https://files.pythonhosted.org/packages/fc/70/20179544538444c0896099a10a035d857c2f4ce707a950834aa62d645714/bitarray-3.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:23fe97bfed91ae4c927871b779caa159000da958fe5521d92420a09ed524efdf", size = 363092, upload-time = "2026-07-25T06:37:54.796Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/c9b206363aad0239cf966ea4eefaaa0da2717cd695ac11af1effbe3819bb/bitarray-3.9.2-cp312-cp312-win32.whl", hash = "sha256:ff72bbdd7c01ca661cb6712e706c0e0f0bc45a6b2426f69e9517cf7508b8d294", size = 148523, upload-time = "2026-07-25T06:37:55.915Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2a/67f161485ba266ad76064405cd19dda50057f466d796b4ad9d9e2645b877/bitarray-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:12c2e7556f7d7f3d2a2017d66159790ed6cadf849a3aa436d9c3541dd3facd6c", size = 157829, upload-time = "2026-07-25T06:37:57.285Z" }, + { url = "https://files.pythonhosted.org/packages/58/cc/aefdf64e5c3b057538ed6e5f244fa055bc825b10cb3a08ead0895ba9ff67/bitarray-3.9.2-cp312-cp312-win_arm64.whl", hash = "sha256:76c0a98d85b2ed7090ff6c0fff9917a2ddffaffb82c2d0ce7d424f0830dc8dee", size = 153677, upload-time = "2026-07-25T06:37:58.332Z" }, + { url = "https://files.pythonhosted.org/packages/9f/33/a15849ec7ca05b55835b52baa4f226ee1d75cffd0943cf0a648c52bf9048/bitarray-3.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5156ae1fb0d56238b2ba64b8c0da9a14dbc2e16c9d79114ad3850b7aff513b0a", size = 156131, upload-time = "2026-07-25T06:37:59.76Z" }, + { url = "https://files.pythonhosted.org/packages/53/98/f097b0e50c31c4e1cc8f8612e21b8d193a66c0d14ca07f06eba376c5f090/bitarray-3.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:054fc478842a95d8e9e200117a7e170cd8ec17608e3cfa451e44fc78e5ab5007", size = 152618, upload-time = "2026-07-25T06:38:00.769Z" }, + { url = "https://files.pythonhosted.org/packages/f2/c2/ea3786bb4231564482f23f1aaf478a1ddf8e8d08cfb90167c17cca89623d/bitarray-3.9.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d5e428a70d6e395b9fd415bba70bfb463d2b9e8010d471cb99eb0f82e91d179", size = 360335, upload-time = "2026-07-25T06:38:01.829Z" }, + { url = "https://files.pythonhosted.org/packages/fd/93/5772f7c4ffa4d6a1e9e486e8623f4f89041268e8c91b5761ad0920fc53e8/bitarray-3.9.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a34d717b362f96a767b03f350cefecd0f613a44ce774868d3635423eec1e5d75", size = 381029, upload-time = "2026-07-25T06:38:03.1Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6b/e84a9a61cbf7db68703f6ebeb853c4d323c9b1d83bd4d863ceb424575785/bitarray-3.9.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:068a100b2d3507e1ebd6f0cb3515551ddf3c7db1c98426dc854ddff985508eb8", size = 394606, upload-time = "2026-07-25T06:38:04.149Z" }, + { url = "https://files.pythonhosted.org/packages/1a/3c/127d48763eaf8941556486fc7883096d9024139276dfc19150523bfc6e31/bitarray-3.9.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a797165662ce08029a5975e05e928b77f979d8322cc719ba46eb8b3d9ab5c9b", size = 364341, upload-time = "2026-07-25T06:38:05.401Z" }, + { url = "https://files.pythonhosted.org/packages/1e/9b/d58b96fc10bec25fa59aa894964dadb5cbfb150fe4b30aeecea162c2cda4/bitarray-3.9.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6468d775054cb4fe777d585c661df89e99f9398fa9cb31e428a9b42105e6ce48", size = 358005, upload-time = "2026-07-25T06:38:06.464Z" }, + { url = "https://files.pythonhosted.org/packages/61/5f/25989fb53e1e4f079b2c75b24d0bcb500b407cb0be2c35953fee092a872a/bitarray-3.9.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9bd26caa42d12c747af2c3a3995b08db32ac7afa0c5262c0b6c6931293067805", size = 378038, upload-time = "2026-07-25T06:38:07.818Z" }, + { url = "https://files.pythonhosted.org/packages/fa/42/e80c45b496d60e216af776ed284c65cc55434cfbe39cd459975f1fb6a0ca/bitarray-3.9.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:11574711bae2b501141f64cf6d7a9819c9f45e7d8edcd2d9fe282b377dadb900", size = 376295, upload-time = "2026-07-25T06:38:09.079Z" }, + { url = "https://files.pythonhosted.org/packages/04/84/8bc4a8e12a802199b20912f57427e3ba6b8cc3649190fecb7c97bb35a69d/bitarray-3.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c6e16d6a8d93362f65336fc57e76fb1fe94f257bb54a843cf1a83c86887ac1ca", size = 360959, upload-time = "2026-07-25T06:38:10.299Z" }, + { url = "https://files.pythonhosted.org/packages/34/ca/3ebd885ae0df8f9ec508315361e602fada65c5c75d8b1d1660c81659cce0/bitarray-3.9.2-cp313-cp313-win32.whl", hash = "sha256:7fd8d649b10ba26c032c76ffb2610385496ebf5e52c44c26864e9b16ac8102b3", size = 148350, upload-time = "2026-07-25T06:38:11.473Z" }, + { url = "https://files.pythonhosted.org/packages/6b/92/7ecff07e9bbf6f51092bc3a01361bd5bae5814cd3326abc8423a2ae637a0/bitarray-3.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:0df2bd4b5fea65be332cf9c17351d4c68d3eb442c0e13a5bda8019554cb36ce1", size = 157627, upload-time = "2026-07-25T06:38:12.591Z" }, + { url = "https://files.pythonhosted.org/packages/7c/c4/28122fd875b6bdbebfc15bf60ea3704149540b0f3f43e15f247a7e9604ba/bitarray-3.9.2-cp313-cp313-win_arm64.whl", hash = "sha256:94278c9d37a6f4316ae1f100e98e6582369f25b1f8186bb595ee1578fbc5c52e", size = 153528, upload-time = "2026-07-25T06:38:13.863Z" }, + { url = "https://files.pythonhosted.org/packages/5c/b2/307b9857295d37489b3269eb3cbcf62ab55d19e17c6c567602a2fbf3a589/bitarray-3.9.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b0debddd2eb7d1a0417f602b2520dd6fb6897ab140c1d69b8c42a69d164ef61b", size = 156152, upload-time = "2026-07-25T06:38:14.92Z" }, + { url = "https://files.pythonhosted.org/packages/95/55/3d13cb858176c2a9189c35becccef29b45c32720e7a1f36c57af431ff7c0/bitarray-3.9.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5d0387459ac35f6be40c4b809c34f34afe14aa930493e0b06f6df5d327f405c3", size = 152690, upload-time = "2026-07-25T06:38:16.096Z" }, + { url = "https://files.pythonhosted.org/packages/59/1f/7d01c3130fac3d7cd9f52697019e4dbf992fc67befb7c915faf456bc1c1c/bitarray-3.9.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5bde034041875a48ea8c0a4b272e9a6e29174b234bd4905c86a65484f34cd140", size = 360268, upload-time = "2026-07-25T06:38:17.27Z" }, + { url = "https://files.pythonhosted.org/packages/25/61/2b6f05eb6c855727799dd3092b70e933ff433e9464740245042c2bfc68f7/bitarray-3.9.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:37c76ff084118ab1db8b826e35c01ce4d0137e66b611542e16b1f296c96e6aa6", size = 381244, upload-time = "2026-07-25T06:38:18.462Z" }, + { url = "https://files.pythonhosted.org/packages/ee/b0/72d15249f41ffcfad56b4cea7d2a518d936c33c3548f6f474de96ae9da88/bitarray-3.9.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d12c7b574e2903477124a0d77bbc903fd4c3beaefa841f4a63c199014192447b", size = 393965, upload-time = "2026-07-25T06:38:19.823Z" }, + { url = "https://files.pythonhosted.org/packages/0f/6c/e880b52ebed9441a423afdb9f8f3b71186b689cefdcdc6fcb434ca4775ee/bitarray-3.9.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bbff5cccf3a1f4129c8ad43b2bcb789882fba47c87422b30a74bc3bee98a292", size = 363960, upload-time = "2026-07-25T06:38:21.06Z" }, + { url = "https://files.pythonhosted.org/packages/5e/39/2fb0d14bc884de3dda42a6cfbe2325d76af45733e6f97709924352db2bf8/bitarray-3.9.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c374884716ab641c36ee4f51859fa4274fbdf0355ae7242290a29af3cf49381d", size = 357843, upload-time = "2026-07-25T06:38:22.263Z" }, + { url = "https://files.pythonhosted.org/packages/af/ca/5f7fe5898ea35e34ce79b653ae513d4c67bc0ae607229c51a09d8278195e/bitarray-3.9.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2fe1d36b6d816512af098699c85e9237ce5978863a336a2833ebc03f59545781", size = 378105, upload-time = "2026-07-25T06:38:23.436Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c9/c6b3121421783f10481836140ab976cba7b73bceb9f58216eabf1cee073b/bitarray-3.9.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4f719595fde8bdf030282c8221c738248a3a73928db3087ea62db2c29de5342d", size = 375952, upload-time = "2026-07-25T06:38:24.695Z" }, + { url = "https://files.pythonhosted.org/packages/30/95/05f50342d799d7f49688371ebd3695b79c6fa65db4eeb9fc335c72aa755e/bitarray-3.9.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:28c4edee03ece6768ee51c1889c5d4df05764f8ad956fed6295912dbe4aef729", size = 360654, upload-time = "2026-07-25T06:38:25.944Z" }, + { url = "https://files.pythonhosted.org/packages/30/88/836ad7a1421cd4dd888ed602eb7e7fb297de866e64dab19150be596e9abf/bitarray-3.9.2-cp314-cp314-win32.whl", hash = "sha256:f01b94e8bd0229c214f0873f8d19cf4db4602a230064317a8be00428922f7987", size = 147264, upload-time = "2026-07-25T06:38:27.106Z" }, + { url = "https://files.pythonhosted.org/packages/2a/cd/ca5feb145af0ad602c88a2bb43ff3c44067846324c58e8c972a397adf2ee/bitarray-3.9.2-cp314-cp314-win_amd64.whl", hash = "sha256:7892775de516287fbc850cfb9476599bc399b971105fbbe216513ac4cb67d6bf", size = 156687, upload-time = "2026-07-25T06:38:28.268Z" }, + { url = "https://files.pythonhosted.org/packages/5e/f4/8d9516a9aa91b18c9c0075451386b09c342acf3cca536331438d38e8c955/bitarray-3.9.2-cp314-cp314-win_arm64.whl", hash = "sha256:7dcbf363dce364722a3cd3875be5453a36db8c476af551bc5b194ea9960c591f", size = 153104, upload-time = "2026-07-25T06:38:29.357Z" }, + { url = "https://files.pythonhosted.org/packages/38/ac/62db07d42f4713d8032acb2c5776550f99da74ead726d3ab49845ac2b768/bitarray-3.9.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:732fc6464eebdfbbdb28e7a2e98424ba7633619665fb065cb6d756ffef5f6b08", size = 159619, upload-time = "2026-07-25T06:38:30.939Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4a/7ddb362b4dc3f7beb428e96c129fbc96196c7a716658d5d677dffe8fffbd/bitarray-3.9.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c23f1dc6f118c3a885ee2a281261da37e98c35b4361dcf2c9a9d3d70370b32e6", size = 155980, upload-time = "2026-07-25T06:38:32.085Z" }, + { url = "https://files.pythonhosted.org/packages/0f/78/08b2ac57ff404a33e98193481d1a4fe716999ed2255335fbd56ddda7737d/bitarray-3.9.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ada39b08677af76c48a64f1f14e8e518315cf478004ddf4b6c580ce3625c1a9d", size = 378321, upload-time = "2026-07-25T06:38:33.376Z" }, + { url = "https://files.pythonhosted.org/packages/e5/79/5a0734dbc080748b11680483d74f29469c85b56ff6b490028c301732c658/bitarray-3.9.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:70e5a48906a8cab0b8d84ad356d8cf8f0fb533b93475da26437a4a3aed8a9a4b", size = 397775, upload-time = "2026-07-25T06:38:34.619Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2f/a4d87cc0a1a01851fb76cb13633413fc8d2187cfbf00d9f85397edba301c/bitarray-3.9.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b14351e75d76662d10c1479c8864f4076ab393a297323b85277d2253ccfb96e", size = 411104, upload-time = "2026-07-25T06:38:35.945Z" }, + { url = "https://files.pythonhosted.org/packages/0e/df/5b2d12198c9403a8b757dfc54fee1199e202ec361ad7fb4cb84f295e917e/bitarray-3.9.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:920edc84385932401ddabe62e244774acaa69e991072768fcf7e621c29599f6f", size = 380204, upload-time = "2026-07-25T06:38:37.14Z" }, + { url = "https://files.pythonhosted.org/packages/ee/5f/b6daaa21059439703d53349d9e9158860fa04d31b8a5613dfdc45c8f4b9c/bitarray-3.9.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3d6dd06fd0e1c88e6da86e4c0479457c74f03f66a2dc16c0c60ecce12c5dee1d", size = 374273, upload-time = "2026-07-25T06:38:38.435Z" }, + { url = "https://files.pythonhosted.org/packages/1f/4f/625e820bdc9235f56ef9644892c7b6b89a96cb7f5a0823748e9a912f1d1a/bitarray-3.9.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:ee339e8f9a763499c5fc855927c8174680183e8751f03f797234a0bd1223c3ff", size = 394096, upload-time = "2026-07-25T06:38:39.785Z" }, + { url = "https://files.pythonhosted.org/packages/02/27/04ee7179dd670781a3c457969e94503a10c516e43340faf04f6862c7c0ef/bitarray-3.9.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:50f0ff3b5e5cfa7ca42e2469418a26d098a42e47b0e171adf9c6c69ea800d055", size = 391958, upload-time = "2026-07-25T06:38:41.011Z" }, + { url = "https://files.pythonhosted.org/packages/b8/7d/ef4ce2906e6c3056021196d11efb220f351f7db74783882e802c8f81c17f/bitarray-3.9.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c898478e6dde8ca75970b91a602b1f48ebedff49841b89cb8495c1949b6d762c", size = 376178, upload-time = "2026-07-25T06:38:42.535Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/ff961878f0671e921d03a498705b4a15bb96b53474685f2d04abb79204f4/bitarray-3.9.2-cp314-cp314t-win32.whl", hash = "sha256:415675dbae4d1c58a5ae31550db6224e0785443e41d9ed74cf4371aec38d61d3", size = 150524, upload-time = "2026-07-25T06:38:43.939Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ec/a3eb15a4a9730e3f48315f8d510867944f9051b82a465f4fe1d28b9a9e76/bitarray-3.9.2-cp314-cp314t-win_amd64.whl", hash = "sha256:de17695646139d2a9863cf1d8ff83f1335aa3d593f2730c9ae442fb08ef80241", size = 160476, upload-time = "2026-07-25T06:38:45.145Z" }, + { url = "https://files.pythonhosted.org/packages/c9/81/73c274307ec6f74fb072623ec2644e6467f36b24f7464f0be2ed236c183c/bitarray-3.9.2-cp314-cp314t-win_arm64.whl", hash = "sha256:19105d5ff84450b341a0dc1021c7f8d63cb1ffedcdc78b46e62f4cd76f28a7f1", size = 156219, upload-time = "2026-07-25T06:38:46.274Z" }, ] [[package]] @@ -148,198 +143,208 @@ wheels = [ [[package]] name = "certifi" -version = "2026.4.22" +version = "2026.7.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, + { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, ] [[package]] name = "cffi" -version = "2.0.0" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +sdist = { url = "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036, upload-time = "2026-07-06T21:34:30.382Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44", size = 184283, upload-time = "2025-09-08T23:22:08.01Z" }, - { url = "https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49", size = 180504, upload-time = "2025-09-08T23:22:10.637Z" }, - { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, - { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, - { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, - { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, - { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, - { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, - { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, - { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, - { url = "https://files.pythonhosted.org/packages/e2/cc/027d7fb82e58c48ea717149b03bcadcbdc293553edb283af792bd4bcbb3f/cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a", size = 172184, upload-time = "2025-09-08T23:22:23.328Z" }, - { url = "https://files.pythonhosted.org/packages/33/fa/072dd15ae27fbb4e06b437eb6e944e75b068deb09e2a2826039e49ee2045/cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739", size = 182790, upload-time = "2025-09-08T23:22:24.752Z" }, - { url = "https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe", size = 184344, upload-time = "2025-09-08T23:22:26.456Z" }, - { url = "https://files.pythonhosted.org/packages/4f/8b/f0e4c441227ba756aafbe78f117485b25bb26b1c059d01f137fa6d14896b/cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c", size = 180560, upload-time = "2025-09-08T23:22:28.197Z" }, - { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, - { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, - { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, - { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, - { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, - { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, - { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, - { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c0/015b25184413d7ab0a410775fdb4a50fca20f5589b5dab1dbbfa3baad8ce/cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5", size = 172076, upload-time = "2025-09-08T23:22:40.95Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5", size = 182820, upload-time = "2025-09-08T23:22:42.463Z" }, - { url = "https://files.pythonhosted.org/packages/95/5c/1b493356429f9aecfd56bc171285a4c4ac8697f76e9bbbbb105e537853a1/cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d", size = 177635, upload-time = "2025-09-08T23:22:43.623Z" }, - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, + { url = "https://files.pythonhosted.org/packages/c0/e9/6d7724983b3d5a0908dbf74f64038ade77c18646ff6636ec7894fd392ce1/cffi-2.1.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:b65f590ef2a44640f9a05dbb548a429b4ade77913ce683ac8b1480777658a6c0", size = 183837, upload-time = "2026-07-06T21:32:09.655Z" }, + { url = "https://files.pythonhosted.org/packages/69/aa/24580a278de21fd7322635556334d9b535f1cbc00b0a3919447cdf464c65/cffi-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164bff1657b2a74f0b6d54e11c9b375bc97b931f2ca9c43fcf875838da1570dd", size = 184226, upload-time = "2026-07-06T21:32:11.196Z" }, + { url = "https://files.pythonhosted.org/packages/88/a9/02cae418ec4beb282ace11958d9d4737793439d561fadc7e6d56f2e2b354/cffi-2.1.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:c941bb58d5a6e1c3892d86e42927ed6c180302f07e6d395d08c416e594b98b46", size = 211107, upload-time = "2026-07-06T21:32:12.328Z" }, + { url = "https://files.pythonhosted.org/packages/3b/30/c806937ed5e4c2c7ac30d9d6b76b5dc57ff8b75d83800d9bb11a8253cf2a/cffi-2.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a016194dbe13d14ee9556e734b772d8d67b947092b268d757fd4290e3ba2dfc2", size = 218733, upload-time = "2026-07-06T21:32:13.67Z" }, + { url = "https://files.pythonhosted.org/packages/f9/cf/398272b8bbfd58aa314fda5a7f1cdbb26d1d78ae324a11211521315dd1f0/cffi-2.1.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:03e9810d18c646077e501f661b682fbf5dee4676048527ca3cffe66faa9960dd", size = 205543, upload-time = "2026-07-06T21:32:15.148Z" }, + { url = "https://files.pythonhosted.org/packages/45/ca/f91641185cdd90c36d317a9dc7f85e88ef8682d8b300977baff5e23c35d8/cffi-2.1.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:19c54ac121cad98450b4896fa9a43ee0180d57bc4bc911a33db6cab1efab6cd3", size = 205460, upload-time = "2026-07-06T21:32:16.479Z" }, + { url = "https://files.pythonhosted.org/packages/38/66/04781a77b411f0bb5b234d62c1814754ab75ebe455ccff1b08e8d7aae98f/cffi-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d433a51f1870e43a13b6732f92aaf540ff77c2015097c78556f75a2d6c030e0", size = 218760, upload-time = "2026-07-06T21:32:17.98Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9a/bb1d5ed9c3fcae158e9f6391bf309c95d98c2ac37ed56573228471d0af5e/cffi-2.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3d7f118b5adbfdfead90c25822690b02bc8074fba949bb7858bec4ebd55adb43", size = 221230, upload-time = "2026-07-06T21:32:19.407Z" }, + { url = "https://files.pythonhosted.org/packages/41/aa/3c1409cdd26094efacd1c36c66e0a6eb9d4296e4fd4f9901b8b2042f4323/cffi-2.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5f5df567f6eb216de69be06ce55c8b714090fae02b18a3b40da8163b8c5fa9c", size = 213524, upload-time = "2026-07-06T21:32:20.828Z" }, + { url = "https://files.pythonhosted.org/packages/fa/75/74dfb7c3fc6ebbd408038476bd4c1d7e925c62614e7b9c534ecc34218288/cffi-2.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:11b3fb55f4f8ad92274ed26705f65d8f91457de71f5380061eb6d125a768fecd", size = 220341, upload-time = "2026-07-06T21:32:21.9Z" }, + { url = "https://files.pythonhosted.org/packages/70/b6/9003c33a3e7d2c1306f5962e646457dcfe5a8cd8fce6bbe02d7af25db783/cffi-2.1.0-cp310-cp310-win32.whl", hash = "sha256:9d72af0cf10a76a600a9690078fe31c63b9588c8e86bf9fd353f713c84b5db0f", size = 174578, upload-time = "2026-07-06T21:32:23.073Z" }, + { url = "https://files.pythonhosted.org/packages/8a/26/710688310447531c7a22f857c7f79d9855ec18b03e04494ced723fb37e2f/cffi-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb62edb5bb52cca65fab91a63afa7561607120d26090a7e8fda6fb9f064726da", size = 185071, upload-time = "2026-07-06T21:32:24.671Z" }, + { url = "https://files.pythonhosted.org/packages/d3/67/85c89a59ba36a671e79638f44d466749f08179266a57e4f2ffdf92174072/cffi-2.1.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:02cb7ff33ded4f1532476731f89ede53e2e488a8e6205515a82144246ffa7dcc", size = 183845, upload-time = "2026-07-06T21:32:26.32Z" }, + { url = "https://files.pythonhosted.org/packages/ea/dd/e3b0baa2d3d6a857ac72b7efbf18e32e487c9cdafcc13049ad765495b15e/cffi-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f5bce581e6b8c235e566a14768a943b172ada3ed73537bb0c0be1edee312d4e7", size = 184186, upload-time = "2026-07-06T21:32:28.025Z" }, + { url = "https://files.pythonhosted.org/packages/65/68/9f3ef890cf3c6ab97bd531c5677f67613d302165d16f8142b2811782a614/cffi-2.1.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:30b65779d598c370374fefabf138d456fd6f3216bfa7bedfab1ba82025b0cd93", size = 211892, upload-time = "2026-07-06T21:32:29.565Z" }, + { url = "https://files.pythonhosted.org/packages/22/d7/1a74539db16d8bfd839ff1515948948efbb162e574650fd3d846896eea95/cffi-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88023dfe18799507b73f1dbb0d14326a17465de1bc9c9c7655c22845e9ddc3a2", size = 218793, upload-time = "2026-07-06T21:32:30.951Z" }, + { url = "https://files.pythonhosted.org/packages/ec/d1/9a5b7169499e8e8d8e636de70b97ac7c9447104d2ff1a2cd94790cea5162/cffi-2.1.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0a96b74cda968eebbad56d973efe5098974f0a9fb323865bf99ea1fd24e3e64c", size = 205737, upload-time = "2026-07-06T21:32:32.216Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b0/e131a9c41f10607926278453d9596163594fe1c4ebc46efe3b5e5b34eb84/cffi-2.1.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a5781494d4d400a3f47f8f1da94b324f6e6b440a53387774002890a2a2f4b50f", size = 204909, upload-time = "2026-07-06T21:32:33.655Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d2/4398416cd699b35167947c6e22aca52c47e69ad5695073c9f1f2c52e04aa/cffi-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aa7a1b53a2a4452ada2d1b5dade9960b2522f1e61293a811a077439e39029565", size = 217883, upload-time = "2026-07-06T21:32:35.173Z" }, + { url = "https://files.pythonhosted.org/packages/a2/a5/d4fe77b589e5e82d43ebc809bf2e6474afe8e48e32ea050b9357645b6471/cffi-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d8272c0e483b024e1b9ad029821470ed8ec65631dbd90217469da0e7cd89f1c", size = 221251, upload-time = "2026-07-06T21:32:36.527Z" }, + { url = "https://files.pythonhosted.org/packages/22/f0/a2fc43084c0433caf7f461bccc013e28f848d04ee1c5ed7fce71423cf4d9/cffi-2.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7762faa47e8ff7eb80bd261d9a7d8eea2d8baa69de5e95b70c1f338bbe712f02", size = 214250, upload-time = "2026-07-06T21:32:37.852Z" }, + { url = "https://files.pythonhosted.org/packages/04/8c/b925975448cf20634a9fbd5efceb807219db452653648d2897c0989cab2d/cffi-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:89095c1968b4ba8285840e131bf2891b09ae137fe2146905acae0354fbce1b5e", size = 219441, upload-time = "2026-07-06T21:32:39.146Z" }, + { url = "https://files.pythonhosted.org/packages/eb/da/5c4918a2d61d86fa927d716cb3d8e4626ef8dc8f605a599d32f33897f59a/cffi-2.1.0-cp311-cp311-win32.whl", hash = "sha256:64c753a0f87a256020004f37a1c8c02c480e725f910f0b2a0f3f07debd1b2479", size = 174496, upload-time = "2026-07-06T21:32:40.467Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c8/6c2de1d55cf35ef8b92885d5ef280790f0fb9634d87ea1cc315176aecd61/cffi-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:4f26194e3d95e06501b942642855aed4f953d55e95d7d01b7c4483db3ecff458", size = 185113, upload-time = "2026-07-06T21:32:41.761Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4e/e8d7cb5783f1841a3c8fb3a7735838d7484d08ec08c9f984b14cac1ac0e9/cffi-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:35aaea0c7ee0e58a5cd8c2fd1a48fdf7ece0d2699b7ecdda08194e9ce5dd9b3d", size = 179927, upload-time = "2026-07-06T21:32:42.961Z" }, + { url = "https://files.pythonhosted.org/packages/1e/85/990925db5df586ec90beb97529c853497e7f85ba0234830447faf41c3057/cffi-2.1.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:df2b82571a1b30f58a87bf4e5a9e78d2b1eff6c6ce8fd3aa3757221f93f0863f", size = 184829, upload-time = "2026-07-06T21:32:44.324Z" }, + { url = "https://files.pythonhosted.org/packages/4b/92/e7bb136ad6b5352603732cf907ef862ca103f20f2031c1735a46300c20c9/cffi-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78474632761faa0fb96f30b1c928c84ebcf68713cbb80d15bab09dfe61640fde", size = 184728, upload-time = "2026-07-06T21:32:45.683Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c0/d1ec30ffb370f748f2fb54425972bfef9871e0132e82fb589c46b6676049/cffi-2.1.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5972433ad71a9e46516584ef60a0fda12d9dc459938d1539c3ddecf9bdc1368d", size = 214815, upload-time = "2026-07-06T21:32:48.557Z" }, + { url = "https://files.pythonhosted.org/packages/1b/dc/5620cf930688be01f2d673804291de757a934c90b946dbdc3d84130c2ea4/cffi-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6422532152adf4e59b110cb2808cee7a033800952f5c036b4af047ee43199e7", size = 222429, upload-time = "2026-07-06T21:32:49.848Z" }, + { url = "https://files.pythonhosted.org/packages/4b/a4/77b53abbf7a1e0beb9637edbef2a94d15f9c822f591e85d439ffd91519a6/cffi-2.1.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:46b1c8db8f6122420f32d02fffb924c2fe9bc772d228c7c711748fff56aabb2b", size = 210315, upload-time = "2026-07-06T21:32:51.221Z" }, + { url = "https://files.pythonhosted.org/packages/58/0c/f528df19cc94b675087324d4760d9e6d5bfae97d6217aa4fac43de4f5fcc/cffi-2.1.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9fafc5aa2e2a39aaf7f8cc0c1f044a9b07fca12e558dca53a3cc5c654ad67a7", size = 208859, upload-time = "2026-07-06T21:32:52.512Z" }, + { url = "https://files.pythonhosted.org/packages/62/f2/c9522a81c32132799a1972c39f5c5f8b4c8b9f00488a23feaa6c06f07741/cffi-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e9f50d192a3e525b15a75ab5114e442d83d657b7ec29182a991bc9a88fd3a66", size = 221844, upload-time = "2026-07-06T21:32:53.704Z" }, + { url = "https://files.pythonhosted.org/packages/6e/28/bd53988b9833e8f8ad539d26f4c07a6b3f6bcb1e9e02e7ca038250b3428d/cffi-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98fff996e983a36d3aa2eca83af40c5821202e7e6f32d13ae94e3d2286f10cfe", size = 225287, upload-time = "2026-07-06T21:32:54.907Z" }, + { url = "https://files.pythonhosted.org/packages/79/99/0d0fd37f055224085f42bbb2c022d002e17dde4a97972822327b07d84101/cffi-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:379de10ce1ba048b1448599d1b37b24caee16309d1ac98d3982fc997f768700b", size = 223681, upload-time = "2026-07-06T21:32:56.329Z" }, + { url = "https://files.pythonhosted.org/packages/b0/80/c138990aa2a70b1a269f6e06348729836d733d6f970867943f61d367f8cc/cffi-2.1.0-cp312-cp312-win32.whl", hash = "sha256:9b8f0f26ca4e7513c534d351eca551947d053fac438f2a04ac96d882909b0d3a", size = 175269, upload-time = "2026-07-06T21:32:57.777Z" }, + { url = "https://files.pythonhosted.org/packages/a8/eb/f636456ff21a83fc13c032b58cc5dde061691546ac79efa284b2989b7982/cffi-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:c97f080ea627e2863524c5af3836e2270b5f5dfff1f104392b959f8df0c5d384", size = 185881, upload-time = "2026-07-06T21:32:59.253Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/400ea43e721727dca8a65c4521390e9196757caba4a45643acb2b63271b8/cffi-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:6d194185eabd279f1c05ebe3504265ddfc5ad2b58d0714f7db9f01da592e9eb6", size = 180088, upload-time = "2026-07-06T21:33:02.278Z" }, + { url = "https://files.pythonhosted.org/packages/96/88/a996879e2eeccb815f6e3a5967b12a308257412acec882039d386bd2aa7b/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:10537b1df4967ca26d21e5072d7d54188354483b91dc75058968d3f0cf13fbda", size = 194331, upload-time = "2026-07-06T21:33:03.697Z" }, + { url = "https://files.pythonhosted.org/packages/58/85/7ae00d5c8dd6266f4e944c3db630f3c5c9a98b61d469c714d848b1d8138a/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a95b05f9baf29b91171b3a8bd2020b028835243e7b0ff6bb23e2a3c228518b1b", size = 196966, upload-time = "2026-07-06T21:33:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e9/45c3a76ad8d43ad9261f4c95436da61128d3ca545d72b9612c0ab5be0b1c/cffi-2.1.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:15faec4adfff450819f3aee0e2e02c812de6edb88203aa58807955db2003472a", size = 184795, upload-time = "2026-07-06T21:33:06.699Z" }, + { url = "https://files.pythonhosted.org/packages/84/4c/82f132cb4418ee6d953d982b19191e87e2a6372c8a4ce36e50b69d6ade4a/cffi-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:716ff8ec22f20b4d988b12884086bcef0fc99737043e503f7a3935a6be99b1ea", size = 184746, upload-time = "2026-07-06T21:33:08.071Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1c/4ed5a0e5bdca6cbc275556de3328dd1b76fd0c11cc13c88fe66d1d8715f2/cffi-2.1.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:63960549e4f8dc41e31accb97b975abaecfc44c03e396c093a6436763c2ea7db", size = 214747, upload-time = "2026-07-06T21:33:09.671Z" }, + { url = "https://files.pythonhosted.org/packages/3a/a6/e879bb68cc23a2bc9ba8f4b7d8019f0c2694bad2ab6c4a3701d429439f58/cffi-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff067a8d8d880e7809e4ac88eb009bb848870115317b306666502ccad30b147f", size = 222392, upload-time = "2026-07-06T21:33:10.896Z" }, + { url = "https://files.pythonhosted.org/packages/88/f6/01890cfd63c08f8eb96a8319b0443690197d240a8bd6346048cf7bde9190/cffi-2.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3b926723c13eba9f81d2ef3820d63aeceec3b2d4639906047bf675cb8a7a500d", size = 210285, upload-time = "2026-07-06T21:33:12.251Z" }, + { url = "https://files.pythonhosted.org/packages/a6/cf/2b684132056f438567b61e19d690dd31cd0921ace051e0a458be6074369e/cffi-2.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:47ff3a8bfd8cb9da1af7524b965127095055654c177fcfc7578debcb015eecd0", size = 208801, upload-time = "2026-07-06T21:33:13.617Z" }, + { url = "https://files.pythonhosted.org/packages/6f/08/f2e7d62c460faae0926f2d6e423694aa409ced3bc1fe2927a0a6e5f05416/cffi-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:799416bae98336e400981ff6e532d67d5c709cfb30afb79865a1315f94b0e224", size = 221808, upload-time = "2026-07-06T21:33:15.466Z" }, + { url = "https://files.pythonhosted.org/packages/38/37/04f54b8e63a02f3d908332c9effbf8c366167c6f733ed8a3d4f79b7e2a1e/cffi-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:961be50688f7fba2fa65f63712d3b9b341a22311f5253460ce933f52f0de1c8c", size = 225241, upload-time = "2026-07-06T21:33:16.869Z" }, + { url = "https://files.pythonhosted.org/packages/a9/d6/c72eecca433cd3e681c65ed313ab4835d9d4a379704d0f628a6a05f51c2e/cffi-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf5c6cf48238b0eb4c086978c492ad1cbc22373fc5b2d7353b3a598ce6db887a", size = 223588, upload-time = "2026-07-06T21:33:18.239Z" }, + { url = "https://files.pythonhosted.org/packages/c6/4b/e706f67279140f92939da3475ad610df18bfd52d50f14953a8e5fede71d5/cffi-2.1.0-cp313-cp313-win32.whl", hash = "sha256:db3eb7d46527159a878ec3460e9d40615bc25ba337d477db681aea6e4f05c5d2", size = 175248, upload-time = "2026-07-06T21:33:19.799Z" }, + { url = "https://files.pythonhosted.org/packages/5a/47/59eb7975cb0e4ef0afa764ea945b29a5bb4537a9f771cb7d6c8a5dd74c95/cffi-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:8e74a6135550c4748af665b1b1118b6aab33b1fc6a16f9aff630af107c3b4512", size = 185717, upload-time = "2026-07-06T21:33:21.47Z" }, + { url = "https://files.pythonhosted.org/packages/5a/af/34fee85c48f8d94efc8597bc09470c9dd274c145f1c12e0fbc6ab6d38d74/cffi-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:2282cd5e38aa8accd03e99d1256af8411c84cdbee6a89d841b563fdbd1f3e50f", size = 180114, upload-time = "2026-07-06T21:33:22.515Z" }, + { url = "https://files.pythonhosted.org/packages/d8/f0/81478e482afa03f6d18dc8f2afb5edc45b3080853b634b5ed91961be0998/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d2117334c3af3bdcb9a88522b844a2bdb5efdc4f71c6c822df55486ae1c3347a", size = 194142, upload-time = "2026-07-06T21:33:23.657Z" }, + { url = "https://files.pythonhosted.org/packages/7d/95/8de304305cd9204974b0ca051b86d307cafca13aa575a0ef1b44d92c0d8c/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:702c436735fbe99d59ada02a1f65cfc0d31c0ee8b7290912f8fbc5cd1e4b16c3", size = 196819, upload-time = "2026-07-06T21:33:25.007Z" }, + { url = "https://files.pythonhosted.org/packages/20/71/7c8372d30e42415602ed9f268f7cfd66f1b855fed881ecd168bcb45dbc0b/cffi-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1ff3456eab0d889592d1936d6125bbfbc7ae4d3354a700f8bd80450a66445d4d", size = 184965, upload-time = "2026-07-06T21:33:26.605Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5c/584e626835f0375c928176c04137c96927165cb8733cdb3150ec04e5ee5e/cffi-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c4165821e131d6d4ca444347c2b694e2311bcfa3fe5a861cc72968f28867beac", size = 184952, upload-time = "2026-07-06T21:33:27.823Z" }, + { url = "https://files.pythonhosted.org/packages/2e/d2/065fcae1c73979fac8e054462478d0ff8a29c40cdc2ed7ea5676a061df53/cffi-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:276f20fffd7b396e12516ba8edf9509210ac248cbbc5acbc39cd512f9f59ebe6", size = 222353, upload-time = "2026-07-06T21:33:29.178Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a5/e8bbb1ce5b3ac2f53ad6a10bde44318a5a8d99d4f4a000d44a6e39aeb3e4/cffi-2.1.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7d5980a3433d4b71a5e120f9dd551403d7824e31e2e67124fe2769c404c06913", size = 210051, upload-time = "2026-07-06T21:33:30.534Z" }, + { url = "https://files.pythonhosted.org/packages/28/ed/c127d3ac36e899c965e3361357c3befacd6578c03f40125183e41c3b219e/cffi-2.1.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6ca4919c6e4f89aa99c42510b42cf54596892c00b3f9077f6bdd1505e24b9c8d", size = 208630, upload-time = "2026-07-06T21:33:31.753Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d7/97d3136f81db489ec8d1d67748c110d6c994268fd7528014aa9f2b085e4e/cffi-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d53d10f7da99ae46f7373b9150393e9c5eab9b224909982b43832668de4779f5", size = 221593, upload-time = "2026-07-06T21:33:33.044Z" }, + { url = "https://files.pythonhosted.org/packages/d3/27/93195977168ee63aed233a1a0993a2178798654d1f4bddcdd321d6fd3b21/cffi-2.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c351efb95e832a853a29361675f33a7ce53de1a109cd73fd47af0712213aa4ce", size = 225146, upload-time = "2026-07-06T21:33:34.224Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c1/6dbd291ee2ae5a50a034aa057207081f545923bbf15dad4511e985aafff5/cffi-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7c7a88e2bac086f06d14577332760bdeecc42bdec8ac4077f6260557d9326", size = 223240, upload-time = "2026-07-06T21:33:35.57Z" }, + { url = "https://files.pythonhosted.org/packages/0f/6f/ade5ce9863a57992a6ea3d0d10d7e29b8749fc127204b3d493d667b2815f/cffi-2.1.0-cp314-cp314-win32.whl", hash = "sha256:1854b724d00f6654c742097d5387569021be12d3a0f770eae1df8f8acfcc6acd", size = 177723, upload-time = "2026-07-06T21:33:51.626Z" }, + { url = "https://files.pythonhosted.org/packages/41/de/92b9eeed4ae4a21d6fd9b2a2c8505cbed573299902ea73981cc13f7ff62c/cffi-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:1b96bfe2c4bd825681b7d311ad6d9b7280a091f43e8f63da5729638083cd3bfb", size = 187937, upload-time = "2026-07-06T21:33:53.403Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1a/cc6ae6c2913a03aab8898eee57963cf1035b8df5872ed8b9115fcc7e2be8/cffi-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:7d28dff1db6764108bc30788d85d61c876beff416d9a49cb9dd7c5a9f34f5804", size = 183001, upload-time = "2026-07-06T21:33:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/14/f0/134c00ce0779ec86dea2aa1aac69339c2741a8045072676763512363a2ea/cffi-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7ea6b3e2c4250ff1de21c630fe72d0f63eb95c2c32ffbf64a358cf4a8836d714", size = 188538, upload-time = "2026-07-06T21:33:36.792Z" }, + { url = "https://files.pythonhosted.org/packages/50/d8/3b86aba791cb610d24e8a3e1b2cd529e71fa15096b04e4d4e360049d4a4c/cffi-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6af371f3767faeffc6ac1ef57cdfd25844403e9d3f476c5537caee499de96376", size = 188230, upload-time = "2026-07-06T21:33:38.011Z" }, + { url = "https://files.pythonhosted.org/packages/14/d0/117dcd9209255ad8571fbc8c92ef32593a1d294dcec91ddc4e4db50606f2/cffi-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb4e8997a49aa2c08a3e43c9045d224448b8941d88e7ac163c7d383e560cbf98", size = 223899, upload-time = "2026-07-06T21:33:39.514Z" }, + { url = "https://files.pythonhosted.org/packages/b6/3d/f20f8b886b254e3ad10e15cd4186d3aed49f3e6a35ab37aab9f8f25f7c03/cffi-2.1.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bf01d8c84cbea96b944c73b22182e6c7c432b3475632b8111dbfdc95ddad6e13", size = 211652, upload-time = "2026-07-06T21:33:40.851Z" }, + { url = "https://files.pythonhosted.org/packages/28/3b/fad54de07260b93ddeef4b96d0131d57ea900675df1d410ae1deee52d7a6/cffi-2.1.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:33eb1ad83ebe8f313e0df035c406227d55a79456704a863fad9842136af5ad7d", size = 210755, upload-time = "2026-07-06T21:33:42.183Z" }, + { url = "https://files.pythonhosted.org/packages/cc/82/3d5c705acb7abbba9bbd7d79b8e62e0f25b6120eb7ae6ac49f1b721722fe/cffi-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ac0f1a2d0cfa7eea3f2aaf006ab6e70e8feeb16b75d65b7e5939982ca2f11056", size = 223933, upload-time = "2026-07-06T21:33:43.603Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d0/47e338384ab6b1004241002fa616301020cea4fc95f283506565d252f276/cffi-2.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c16914df9fb7f500e440e6875fa23ff5e0b31db01fa9c06af98d59a91f0dc2e4", size = 226749, upload-time = "2026-07-06T21:33:45.046Z" }, + { url = "https://files.pythonhosted.org/packages/70/25/65bd5b58ea4bfdfc15cde02cb5365f89ef8ab8b2adfb8fe5c4bd4233382f/cffi-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5ecbd0499275d57506d397eebe1981cee87b47fcd9ef5c22cab7ed7644a39a94", size = 225703, upload-time = "2026-07-06T21:33:46.374Z" }, + { url = "https://files.pythonhosted.org/packages/dc/78/aa01ac599a8a4322533d45a1f9bc93b338276d2d59dabbe7c6d92a775c81/cffi-2.1.0-cp314-cp314t-win32.whl", hash = "sha256:7d034dcffa09e9a46c93fa3a3be402096cb5354ac6e41ab8e5cc9cd8b642ad76", size = 182857, upload-time = "2026-07-06T21:33:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/b9/26/d00496b22de4d4228f32dde94ad996f350c8aad676d63bcca0743c8dea4d/cffi-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0582a58f3051372229ca8e7f5f589f9e5632678208d8636fea3676711fdf7fe5", size = 194065, upload-time = "2026-07-06T21:33:48.953Z" }, + { url = "https://files.pythonhosted.org/packages/d5/dd/0c7dbf815a579ff005008a2d815a55d6bb047c349eef536d9dc53d3f0a8d/cffi-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:510aeeeac94811b138077451da1fb18b308a5feab47dd2b603af55804155e1c8", size = 186404, upload-time = "2026-07-06T21:33:50.309Z" }, + { url = "https://files.pythonhosted.org/packages/55/c7/8c8c50cb11c6750051daf12164098a9a6f027ac4356967fd4d800a07f242/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:2e9dabb9abcb7ad15938c7196ad5c1718a4e6d33cc79b4c0209bdb64c4a54a5c", size = 194121, upload-time = "2026-07-06T21:33:56.109Z" }, + { url = "https://files.pythonhosted.org/packages/99/e2/67680bf19a6b60d2bb7ff83baefa2a4c3d2d7dc0f3277034b802e1fc504c/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:37f525a7e7e50c017fdebe58b787be310ad59357ae43a053943a6e1a6c526001", size = 196820, upload-time = "2026-07-06T21:33:57.288Z" }, + { url = "https://files.pythonhosted.org/packages/ed/da/4bbe583a3b3a5c8c60892124fe17f3fa3656523faf0d3484eae90f091853/cffi-2.1.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:95f2954c2c9473d892eca6e0409f3568b37ab62a8eedb122461f73cc273476e3", size = 184936, upload-time = "2026-07-06T21:33:58.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/4b/1f4c36ab273980d7aa75bb126ea4f8971f24a96108acad3a0a084028c57b/cffi-2.1.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:cdf2448aab5f661c9315308ec8b93f4e8a1a67a3c733f8631067a2b67d5913dc", size = 185045, upload-time = "2026-07-06T21:34:00.085Z" }, + { url = "https://files.pythonhosted.org/packages/ef/c3/ad299dc38f3583f8d916b299f028af418a9ec98bc695fcbebeae7420691c/cffi-2.1.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:90bec57cf82089383bd06a605b3eb8daebf7e5a668520beaf6e327a83a947699", size = 222342, upload-time = "2026-07-06T21:34:01.814Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d8/df4543cc087245044ed02ef3ad8e0a26619d0075ac7a77a12dc81177851b/cffi-2.1.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6274dcb2d15cef48daa73ed1be5a40d501d74dccd0cd6db364776d12cb6ba022", size = 210073, upload-time = "2026-07-06T21:34:03.255Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/fac738d73728c6cea2a88a2883dca54892496cbba88a1dc1f2909cb8a6f5/cffi-2.1.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b71d409cccee78310ab5dec549aed052aaea483346e282c7b02362596e01bb0", size = 208551, upload-time = "2026-07-06T21:34:04.433Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3f/0b04a700dd64f465c93020253a793a82c9b4dff9961f48facd0df945d9b8/cffi-2.1.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d3538f9c0e50670f4deb93dbb696576e60590369cae2faf7de681e597a8a1f1", size = 221649, upload-time = "2026-07-06T21:34:06.157Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7c/b7379a5704c79eda57ce075869ba70a0368d1c850f803b3c0d078d39dcaf/cffi-2.1.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:8f9ec95b8a043d3dfbc74d9abc6f7baf524dd27a8dc160b0a32ff9cdab650c28", size = 225203, upload-time = "2026-07-06T21:34:07.489Z" }, + { url = "https://files.pythonhosted.org/packages/5a/02/d5e6c43ea85c41bda2a184a3418f195fe7cf602967a8d2b94e085b83deef/cffi-2.1.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:af5e2915d41fe6c961694d7bfdc8562942638200f3ce2765dfb8b745cf997629", size = 223263, upload-time = "2026-07-06T21:34:08.712Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d8/772b8259bf75749adffb1c546828978381fb516f60cf701f6c83daf60c85/cffi-2.1.0-cp315-cp315-win32.whl", hash = "sha256:0a42c688d19fca6e095a53c6a6e2295a5b050a8b289f109adab02a9e61a25de6", size = 177696, upload-time = "2026-07-06T21:34:26.355Z" }, + { url = "https://files.pythonhosted.org/packages/2f/dd/afa2191fc6d57fedd26e5844a2fe2fcc0bbfa00961bbaa5a41e4921e7cca/cffi-2.1.0-cp315-cp315-win_amd64.whl", hash = "sha256:bccbbb5ee76a61f9d99b5bf3846a51d7fca4b6a732fe46f89295610edaf41853", size = 187914, upload-time = "2026-07-06T21:34:27.58Z" }, + { url = "https://files.pythonhosted.org/packages/05/ef/6cd4f8c671517162379dc79cfae5aea9106bc38abb89628d5c16adf6a838/cffi-2.1.0-cp315-cp315-win_arm64.whl", hash = "sha256:8d35c139744adb3e727cd51b1a18324bbe44b8bd41bf8322bca4d41289f48eda", size = 183004, upload-time = "2026-07-06T21:34:28.905Z" }, + { url = "https://files.pythonhosted.org/packages/11/b6/12fc55092817a5faa26fb8c40c7f9d662e11a46ee248c137aafc42517d92/cffi-2.1.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:f9912624a0c0b834b7520d7769b3644453aabc0a7e1c839da7359f050750e9bc", size = 188378, upload-time = "2026-07-06T21:34:09.926Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2e/cdac88979f295fde5daa69622c7d2111e56e7ceb94f211357fbe452339e4/cffi-2.1.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:df92f2aba50eb4d96718b68ef76f2e57a57b54f2fa62333496d16c6d585a85ca", size = 188319, upload-time = "2026-07-06T21:34:11.101Z" }, + { url = "https://files.pythonhosted.org/packages/e0/27/1d0b408497e41a74795af122d7b603c418c5fed0171450f899afd04e594f/cffi-2.1.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0520e1f4c35f44e209cbbb421b67eec42e6a157f59444dfb6058874ff3610e5d", size = 223904, upload-time = "2026-07-06T21:34:12.606Z" }, + { url = "https://files.pythonhosted.org/packages/8b/31/e115c985105dd7ffb32444505f18ceb874bb42d992af05d5dced7ecf1980/cffi-2.1.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3681e031db29958a7502f5c0c9d6bbc4c36cb20f7b104086fa642d1799631ff8", size = 211554, upload-time = "2026-07-06T21:34:13.987Z" }, + { url = "https://files.pythonhosted.org/packages/5a/67/9e6e09409336d9e515c58367e7cfcf4f89df06ad25252675595a58eb59d5/cffi-2.1.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:762f99479dcb369f60ab9017ad4ab97a36a1dd7c1ee5a3b15db0f4b8659120cd", size = 210795, upload-time = "2026-07-06T21:34:15.972Z" }, + { url = "https://files.pythonhosted.org/packages/19/e5/d3cc82a4a0be7902af279c04181ad038449c096734464a5ae1de3e1401bd/cffi-2.1.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0611e7ebf90573a535ebdc33ae9da222d037853983e13359f580fab781ca017f", size = 223843, upload-time = "2026-07-06T21:34:17.509Z" }, + { url = "https://files.pythonhosted.org/packages/b9/65/b434abc97ce7cecc2c640fde160507c0ecc7e21544b483ba3325d2e2ea17/cffi-2.1.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:86cf8755a791f72c85dc287128cc62d4f24d392e3f1e15837245623f4a33cccc", size = 226773, upload-time = "2026-07-06T21:34:19.05Z" }, + { url = "https://files.pythonhosted.org/packages/b5/9f/d4dc66ca651eb1145a133314cda721abf13cfac3d28c4a0402263ae6ad75/cffi-2.1.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:ba00f661f8ba35d075c937174e27c2c421cec3942fd2e0ea3e66996757c0fdd9", size = 225719, upload-time = "2026-07-06T21:34:20.576Z" }, + { url = "https://files.pythonhosted.org/packages/68/5a/e536c528bc8057496c360c0978559a2dc45653f89dd6151078aa7d8fca1a/cffi-2.1.0-cp315-cp315t-win32.whl", hash = "sha256:cb96698e3c7413d906ce83f8ffd245ec1bd94707541f299d0ce4d6b0193e982b", size = 182760, upload-time = "2026-07-06T21:34:22.059Z" }, + { url = "https://files.pythonhosted.org/packages/d3/0b/0ffe8b82d3875bced5fa1e7986a7a46b748262a40ab7f60b475eb9fb1bb3/cffi-2.1.0-cp315-cp315t-win_amd64.whl", hash = "sha256:f146d154428a2523f9cc7936c02353c2459b8f6cf07d3cd1ee1c0a611109c5d5", size = 193769, upload-time = "2026-07-06T21:34:23.589Z" }, + { url = "https://files.pythonhosted.org/packages/a0/17/1073b53b68c9b5ca6914adf5f8bf55aacc2d3be102418c90700160ea8605/cffi-2.1.0-cp315-cp315t-win_arm64.whl", hash = "sha256:cbb7640ce37159548d2147b5b8c241f962143d4c71231431820783f4dc78f210", size = 186405, upload-time = "2026-07-06T21:34:24.857Z" }, ] [[package]] name = "charset-normalizer" -version = "3.4.7" +version = "3.4.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/2a/23f34ec9d04624958e137efdc394888716353190e75f25dd22c7a2c7a8aa/charset_normalizer-3.4.9.tar.gz", hash = "sha256:673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b", size = 152439, upload-time = "2026-07-07T14:34:58.454Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", size = 315182, upload-time = "2026-04-02T09:25:40.673Z" }, - { url = "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", size = 209329, upload-time = "2026-04-02T09:25:42.354Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", size = 231230, upload-time = "2026-04-02T09:25:44.281Z" }, - { url = "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", size = 225890, upload-time = "2026-04-02T09:25:45.475Z" }, - { url = "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", size = 216930, upload-time = "2026-04-02T09:25:46.58Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", size = 202109, upload-time = "2026-04-02T09:25:48.031Z" }, - { url = "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", size = 214684, upload-time = "2026-04-02T09:25:49.245Z" }, - { url = "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", size = 212785, upload-time = "2026-04-02T09:25:50.671Z" }, - { url = "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", size = 203055, upload-time = "2026-04-02T09:25:51.802Z" }, - { url = "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", size = 232502, upload-time = "2026-04-02T09:25:53.388Z" }, - { url = "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", size = 214295, upload-time = "2026-04-02T09:25:54.765Z" }, - { url = "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", size = 227145, upload-time = "2026-04-02T09:25:55.904Z" }, - { url = "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", size = 218884, upload-time = "2026-04-02T09:25:57.074Z" }, - { url = "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", size = 148343, upload-time = "2026-04-02T09:25:58.199Z" }, - { url = "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", size = 159174, upload-time = "2026-04-02T09:25:59.322Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", size = 147805, upload-time = "2026-04-02T09:26:00.756Z" }, - { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, - { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, - { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, - { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, - { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, - { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, - { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, - { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, - { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, - { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, - { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, - { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, - { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, - { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, - { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, - { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, - { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, - { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, - { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, - { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, - { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, - { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, - { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, - { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, - { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, - { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, - { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, - { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, - { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, - { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, - { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, - { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, - { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, - { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, - { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, - { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, - { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, - { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, - { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, - { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, - { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, - { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, - { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, - { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, - { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, - { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, - { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, - { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, - { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, - { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, - { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, - { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, - { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, - { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, + { url = "https://files.pythonhosted.org/packages/ad/81/8e983840c6e5b93b33c2ba81aa3d52c2e42f0e9a690ce7607a2e61da4a5c/charset_normalizer-3.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd6280cf040f233bd7d3407b743b4b4c74f70e8e1c4199cb112a62c941c0772a", size = 322240, upload-time = "2026-07-07T14:32:36.236Z" }, + { url = "https://files.pythonhosted.org/packages/de/d1/b4319dc3229d8272fba305e206fc0a148e2de8d4087917ce62ae6382f359/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa99adc8f081b475a12843953db36831eaf83ec33eb46a90629ca6a5de45a616", size = 216475, upload-time = "2026-07-07T14:32:38.142Z" }, + { url = "https://files.pythonhosted.org/packages/80/33/6c99c1b3e6b8bf730e1bc809b9a2608f224145069114c479a2e9e1494346/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c1225416b463483160e4af85d5fc3a9690ccb53fd4b1865a6437825f5ede3209", size = 238670, upload-time = "2026-07-07T14:32:39.658Z" }, + { url = "https://files.pythonhosted.org/packages/7f/f4/ffbb83546e1f198ecc70ecd372b65cf2b50f9068b380abd67640f17a8e18/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:16d10d789dd9bcca1173c95af82c58433122564b7bc39385124be735a35cbe99", size = 233476, upload-time = "2026-07-07T14:32:41.155Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5f/b98b8da398637b551e427e7be922bdec19177dc54d6811dcdaa503f23aac/charset_normalizer-3.4.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bb41182d93ea91f60b4bc8fbf4c820c69ef8a12ab2d917f3f1834f1acad07e8", size = 223817, upload-time = "2026-07-07T14:32:42.592Z" }, + { url = "https://files.pythonhosted.org/packages/36/31/a276bb2e66243072a3fd06fdcab9cbb61a305b02143d70d2bda21d888fa8/charset_normalizer-3.4.9-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:bcf74c1df76758a395bf0af608c04c82257523f55c9868b334f06270d0f2112b", size = 207974, upload-time = "2026-07-07T14:32:44.258Z" }, + { url = "https://files.pythonhosted.org/packages/5e/be/7ee4453d7e88dfbc4104ccd34900b9f2c7c17dac22881865fe0e82424a25/charset_normalizer-3.4.9-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b5314963fce9b0b12743891de876e724997864ee22aa496f903f426c7e2fa5b2", size = 221655, upload-time = "2026-07-07T14:32:45.64Z" }, + { url = "https://files.pythonhosted.org/packages/1d/85/181c652953eb5276d198f375b1dd641047392050098100a3a02d6534f657/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e9701d0049d92c16703a42771b98d560b95248949f23f8cf7b4eddd201814fb9", size = 219229, upload-time = "2026-07-07T14:32:47.376Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e7/aaf6da33fc9f4691cda8f7efbc9f69179d3d39ec8a4799baf273ee1d8db0/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:65a7ff3f705e57d392f7261b6d0550fe137c3019477431f1c355e0db0a7d3e15", size = 209704, upload-time = "2026-07-07T14:32:48.855Z" }, + { url = "https://files.pythonhosted.org/packages/63/01/f2fb3bd3a73be48b173ee0c6aa8d2497af97d5663a8c4c4b491de4c62f7a/charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79580094b00d1789d1f93ea55bc43cb2f611910c72235b7657f3482ddcc1b22d", size = 226243, upload-time = "2026-07-07T14:32:50.239Z" }, + { url = "https://files.pythonhosted.org/packages/c4/02/c57a22739fe05246b0b5783b3bfb6afaac4eebb46f3ececdfb2f048f780e/charset_normalizer-3.4.9-cp310-cp310-win32.whl", hash = "sha256:432786d3561e69aeeae6c7e8648964ce0ad05736120135601f87ac26b9c83381", size = 150935, upload-time = "2026-07-07T14:32:51.676Z" }, + { url = "https://files.pythonhosted.org/packages/37/8d/ca39a7559a4797505530d084fd3a49a2c959efbbbff146302fb7be4e3b35/charset_normalizer-3.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:8c041122946b7ba21bb32c45b1aa57b1be35527690aeb3c5c234521085632eee", size = 162314, upload-time = "2026-07-07T14:32:53.193Z" }, + { url = "https://files.pythonhosted.org/packages/01/da/a44bd7a13d426e69e4894557106cd58669097bfad4a8681123b618fbfc5d/charset_normalizer-3.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:375b83ed0aecfce76c16d198fbc21f3b11b337d68662bea0a995046682a11419", size = 153075, upload-time = "2026-07-07T14:32:54.554Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e3/85ec501f206fb049259288c1f3506e53876937fb00edb47009348e66756b/charset_normalizer-3.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0e94703ec9684807f20cfb5eed95c70f67f2a8f21ad620146d7b5a13677b93e5", size = 317075, upload-time = "2026-07-07T14:32:56.021Z" }, + { url = "https://files.pythonhosted.org/packages/c3/69/2a5385192e67175f7d8bd5ce4f57c24bc956439adeae5c13a99aa28a53d1/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a441ea71902098ffe78c5abe6c494f44160b4af614ed16c3d9a3b1d17fd8ee2", size = 213837, upload-time = "2026-07-07T14:32:57.78Z" }, + { url = "https://files.pythonhosted.org/packages/b3/46/03ddc7da576d814fe0a36dd1f0fd3258e95404b4b2e3c026b7923d7e133f/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:304b13570067b2547562e308af560b3963857b1fa90bd6afd978130130fe2d6a", size = 235503, upload-time = "2026-07-07T14:32:59.205Z" }, + { url = "https://files.pythonhosted.org/packages/4e/6e/de0229a7ef40f6f9d28a837eebf4ec47bdca5dab4e900c84f22919af636a/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4773092f8019072343a7447203308b176e10199920eb02d6195e81bbb3274c29", size = 229944, upload-time = "2026-07-07T14:33:00.803Z" }, + { url = "https://files.pythonhosted.org/packages/a5/34/49b9060e8418b14fb5cba9cf6bfb383111e2538a03a1fb18e66a95aeb3d5/charset_normalizer-3.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:04ce310cb89c15df659582aee80a0603788732a5e017d5bd5c81158106ce249c", size = 221276, upload-time = "2026-07-07T14:33:02.199Z" }, + { url = "https://files.pythonhosted.org/packages/44/95/80282cce0fae9c3061203d723ee87da996aed79679e65d8935050ee7ca1f/charset_normalizer-3.4.9-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:c0323c9daef75ef2e5083624b4585018a0c9d5e3b40f607eed81a311270b934b", size = 205260, upload-time = "2026-07-07T14:33:03.698Z" }, + { url = "https://files.pythonhosted.org/packages/0c/74/2f62c8821b969ea3bd67cc2e6976834f48ca5d12664d2559ebcd9bcfbed7/charset_normalizer-3.4.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:871ff67ea1aad4dfd91736464934d56b32dac49f9fbe16cddba36198a7b3a0db", size = 217786, upload-time = "2026-07-07T14:33:05.12Z" }, + { url = "https://files.pythonhosted.org/packages/d9/8d/feabb82cb49fcad14515b1d7d1ca4787b0da7fc723a212bf89bc9e0fac52/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:67830fc78e67501f47bb950471b2dcb9b35b140084429318e862895a8e89c993", size = 216798, upload-time = "2026-07-07T14:33:06.629Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ff/c946d63bc3786d5b84d960b0f7ab7e25b828486a946b5aa997625bcaf6a6/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3d92613ec25e43b05f042302531ec0f00b8445190e43325880cbd6ab7c2581da", size = 206429, upload-time = "2026-07-07T14:33:08.006Z" }, + { url = "https://files.pythonhosted.org/packages/af/ba/5e5007c370702f85d2ef75791fac7943ed41e080364a673b20142e430e3e/charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:280081916dc341820640489a66e4696049401ef1cf6dd672f672e70ad915aca3", size = 223066, upload-time = "2026-07-07T14:33:09.783Z" }, + { url = "https://files.pythonhosted.org/packages/83/d5/9096aa3cf532dfad237861544eb47a0f20d5adbf1039760fed8eaae935d9/charset_normalizer-3.4.9-cp311-cp311-win32.whl", hash = "sha256:ac351b3b8014eead140e77e9717e2992c6bbe30b63bc3422422eb84865412e3d", size = 150456, upload-time = "2026-07-07T14:33:11.217Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a1/e29995109e455dc8eff8d0fac6ae509be39561318a7cfeac5d33ad029213/charset_normalizer-3.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:6366a16e1a25018694d6a5d784d09b046edc9eac40ea2b54065c3052672516a1", size = 161410, upload-time = "2026-07-07T14:33:12.743Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8d/1569f4d0032d6ba2a4fe4591c35bf87868c600c41a71eb5c2e1ffa8464c2/charset_normalizer-3.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:1d22856ffbe153a602df38e4a5464f0b748a54002e0d69ac6d2ad0a197cc99ec", size = 152649, upload-time = "2026-07-07T14:33:14.173Z" }, + { url = "https://files.pythonhosted.org/packages/70/4a/ecbd131485c07fcdfad54e28946d513e3da22ef3b4bd854dcafae54ec739/charset_normalizer-3.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:45b0cc4e3556cd875e09102988d1ab8356c998b596c9fced84547c8138b487a0", size = 319300, upload-time = "2026-07-07T14:33:15.666Z" }, + { url = "https://files.pythonhosted.org/packages/ec/96/5d9364e3342d69f3a045e1777bc47c85c383e6e9466d561b33fdb419d1f9/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b2aff1c7b3884512b9512c3eaadd9bab39fb45042ffaaa1dd08ff2b9f8109d9", size = 215802, upload-time = "2026-07-07T14:33:17.031Z" }, + { url = "https://files.pythonhosted.org/packages/4b/4c/5361f9aa7f2cb58d94f2ab831b3d493f69efb1d239654b4744e3c09527cb/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9104ed0bd76a429d46f9ec0dbc9b08ad1d2dcdf2b00a5a0daa1c145329b35b44", size = 237171, upload-time = "2026-07-07T14:33:18.576Z" }, + { url = "https://files.pythonhosted.org/packages/50/78/ce342ca4ff30b2eb49fe6d9578df85974f90c67d294113e94efdd9664cbd/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b86a2b16095d250c6f58b3d9b2eee6f4147754344f3dab0922f7c9bf7d226c9", size = 233075, upload-time = "2026-07-07T14:33:20.084Z" }, + { url = "https://files.pythonhosted.org/packages/01/c4/4fa4c8b3097a11f3c5f09a35b72ed6855fb1d332469504962ab7bafcc702/charset_normalizer-3.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e226f6218febc71f6c1fc2fafb91c226f75bdc1d8fb12d66823716e891608fd", size = 224256, upload-time = "2026-07-07T14:33:21.747Z" }, + { url = "https://files.pythonhosted.org/packages/87/3a/ad914516df7e358a81aae018caa5e0470ba827fa6d763b1d2e87d920a5f6/charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:90c44bc373b7687f6948b693cceaea1348ae0975d7474746559494468e3c1d84", size = 208784, upload-time = "2026-07-07T14:33:23.313Z" }, + { url = "https://files.pythonhosted.org/packages/d7/74/3c12f9755717dfe5c5c87da63f35d765fa0c00382ec26bf23f7fae34f2ba/charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cdef90ae47919cae358d8ab15797a800ed41da7aba5d72419fb510729e2ed4b", size = 219928, upload-time = "2026-07-07T14:33:24.814Z" }, + { url = "https://files.pythonhosted.org/packages/33/9a/895095b83e7907abd6d3d99aad3a38ad0d9686cc186cb0c94c24320fe63e/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:60f44ade2cf573dad7a277e6f8ca9a51a21dda572b13bd7d8539bb3cd5dbedde", size = 218489, upload-time = "2026-07-07T14:33:26.42Z" }, + { url = "https://files.pythonhosted.org/packages/a1/34/ef5c05f412f42520d7709b7d3784d19640839eb7366ded1755511585429f/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a1786910334ed46ab1dd73222f2cd1e05c2c3bb39f6dddb4f8b36fc382058a39", size = 210267, upload-time = "2026-07-07T14:33:27.952Z" }, + { url = "https://files.pythonhosted.org/packages/83/dc/9b29fa4412b318bf3bfea985c35d67eb55e04b59a7c3f2237168b0e0be6f/charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03d07803992c6c7bbc976327f34b18b6160327fc81cb82c9d504720ac0be3b62", size = 226030, upload-time = "2026-07-07T14:33:29.397Z" }, + { url = "https://files.pythonhosted.org/packages/0e/42/6dbc00b8cd16011691203e33570fa42ed5746599a2e878112d16eab403a3/charset_normalizer-3.4.9-cp312-cp312-win32.whl", hash = "sha256:78841cccf1af7b40f6f716338d50c0902dbe88d9f800b3c973b7a9a0a693a642", size = 151185, upload-time = "2026-07-07T14:33:30.781Z" }, + { url = "https://files.pythonhosted.org/packages/80/cc/f920afd1a23c58ccd53c1d36085a71893a4737ff5e66e0371efab6809850/charset_normalizer-3.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:4b3dac63058cc36820b0dd072f89898604e2d39686fe05321729d00d8ac185a0", size = 162557, upload-time = "2026-07-07T14:33:32.176Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/0386d43a261ff4e4b30c5857af7df877254b46bec7b9d1b74b6bf969a90b/charset_normalizer-3.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:78fa18e436a1a0e58dbd7e02fc4473f3f32cceb12df9dfca542d075961c307d2", size = 152665, upload-time = "2026-07-07T14:33:33.711Z" }, + { url = "https://files.pythonhosted.org/packages/b2/06/97ec2aeae780b31d742b6352218b43841a6871e2564578ca522dce4a45c3/charset_normalizer-3.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:440eede837960000d74978f0eba527be106b5b9aee0daf779d395276ed0b0614", size = 317688, upload-time = "2026-07-07T14:33:35.408Z" }, + { url = "https://files.pythonhosted.org/packages/d0/39/8ff066c672434225f8d25f8b739f992af250944392173dcc88362681c9bf/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21e764fd1e70b6a3e205a0e46f3051701f98a8cb3fad66eeb80e48bb502f8698", size = 214982, upload-time = "2026-07-07T14:33:36.996Z" }, + { url = "https://files.pythonhosted.org/packages/92/8f/3a47a3667c83c2df9483d91644c6c107de3bf8874aa1793da9d3012eb986/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e4fd89cc178bced6ad29cb3e6dd4aa63fa5017c3524dbd0b25998fb64a87cc8b", size = 236460, upload-time = "2026-07-07T14:33:38.536Z" }, + { url = "https://files.pythonhosted.org/packages/f1/60/b22cdbee7e4013dab8b0d7647fc6181120fbbbc8f7025c226d15bd5a47fc/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bd47ba7fc3ca94896759ea0109775132d3e7ab921fbf54038e1bab2e46c313c9", size = 232003, upload-time = "2026-07-07T14:33:40.059Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f8/72eb13dcabe7257035cea8aefd922caad2f110d252bf9f67c4c2ca763aee/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:84fd18bcc17526fc2b3c1af7d2b9217d32c9c04448c16ec693b9b4f1985c3d33", size = 223149, upload-time = "2026-07-07T14:33:41.631Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3e/faee8f9de92b14ee1198e9163252bb15efee7301b31256a3b6d9ebfdd0dd/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:5b10cd92fc5c498b35a8635df6d5a100207f88b63a4dc1de7ef9a548e1e2cd63", size = 207901, upload-time = "2026-07-07T14:33:43.209Z" }, + { url = "https://files.pythonhosted.org/packages/3a/25/45f30093ae27dd7b92a793b61882a38685f993700113ca36e0c9c14965e1/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4fbdde9dd4a9ce5fd52c2b3a347bb50cc89483ef783f1cb00d408c13f7a96c0", size = 219176, upload-time = "2026-07-07T14:33:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/48/18/c8f397329c35e32f6a837e488986f4ae03bd2abebc453b48714991630c2f/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:416c229f77e5ea25b3dfd4b582f8d73d7e43c22320302b9ab128a2d3a0b38efe", size = 217356, upload-time = "2026-07-07T14:33:46.192Z" }, + { url = "https://files.pythonhosted.org/packages/86/7e/5ce0bba863470fd1902d5e5843968951bddf38abe4742fc97116ef4598b3/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:75286256590a6320cf106a0d28970d3560aad9ee09aa7b34fb40524792436d35", size = 209614, upload-time = "2026-07-07T14:33:47.705Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ef/2473d3c4d869155be4af1191111d59c4d5c4e0173026f7e85b176e23bf65/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69b157c5d3292bcd443faca052f3096f637f1e074b98212a933c074ae23dc3b8", size = 224991, upload-time = "2026-07-07T14:33:49.238Z" }, + { url = "https://files.pythonhosted.org/packages/d0/a3/53ddae3db108a088156aa8ddfafd411ebbc1340f48c5573f697b27f69a39/charset_normalizer-3.4.9-cp313-cp313-win32.whl", hash = "sha256:51307f5c71007673a2bf8232ad973483d281e74cb99c8c5a990af1eefa6277d9", size = 150622, upload-time = "2026-07-07T14:33:50.711Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ef/6953a77c7cf2c2ff9998e6f575ab3e380119f100223381565a4f94c1f836/charset_normalizer-3.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:fe2c7201c642b7c308f1675355ad7ff7b66acfe3541625efe5a3ad38f29d6115", size = 161947, upload-time = "2026-07-07T14:33:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/6e/fb/d560d1d1555debbfe7849d9cac6145c1b537709d79576bf22557ed803b82/charset_normalizer-3.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:611057cc5d5c0afc743ba8be6bd828c17e0aaa8643f9d0a9b9bb7dea80eb8012", size = 152594, upload-time = "2026-07-07T14:33:53.486Z" }, + { url = "https://files.pythonhosted.org/packages/7e/8d/496817fa0944239ecae662dd57ea765cfeaec6a735f9f025d4b7b72e7143/charset_normalizer-3.4.9-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0327fcd59a935777d83410750c50600ee9571af2846f71ce40f25b13da1ef380", size = 317253, upload-time = "2026-07-07T14:33:54.994Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f9/ef4a69ea338ad3c0deceea0f5f7d2380ae8b52132b06d652cb0d2cd86706/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a79d9f4d8001473a30c163556b3c3bfebec837495a412dde78b51672f6134f9", size = 215898, upload-time = "2026-07-07T14:33:56.334Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e7/5ddfd76fc061eb52de219658a4aa431cbacadf0a0219c8854f00da50d289/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33bdcc2a32c0a0e861f60841a512c8acc658c87c2ac59d89e3a46dacf7d866e4", size = 236718, upload-time = "2026-07-07T14:33:57.9Z" }, + { url = "https://files.pythonhosted.org/packages/49/ba/768fa3f36048d81c477a0ce61f813bc1454d80917ccfe550abd9f44f5e24/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f840ed6d8ecba8255df8c42b87fadeda98ddfc6eeec05e2dc66e26d46dd6f58a", size = 232519, upload-time = "2026-07-07T14:33:59.811Z" }, + { url = "https://files.pythonhosted.org/packages/f4/c4/b3e049d2aa3766180c78507110543d9d50894cc97f57de543f1be521dcdc/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c25fe15c70c59eb7c5ce8c06a1f3fa1da0ecc5ea1e7a5922c40fd2fa9b0d5046", size = 223143, upload-time = "2026-07-07T14:34:01.517Z" }, + { url = "https://files.pythonhosted.org/packages/19/79/55c32d06d76ae4feafe053f061f3e3ab70bcf19f4007797ce8c3efda7830/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:f7fb7d750cfa0a070d2c24e831fd3481019a60dd317ea2b39acbcebc08b6ed81", size = 206742, upload-time = "2026-07-07T14:34:03.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/e0/47c079dd82d217c807479cd59ffd30af56307ea31c108b75758970459ad3/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1c96a7a18b9690a4d46df09e3e3382406ae3213727cd1019ebade1c4a81917", size = 219191, upload-time = "2026-07-07T14:34:04.657Z" }, + { url = "https://files.pythonhosted.org/packages/42/ab/b9bc2e77d6b44a7e46ef62ec5cac1c9a6ba7b9135a5d560f002696ec9995/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a4cfde78a9f2880208d16a93b795726a3017d5977e08d1e162a7a31322479c41", size = 218328, upload-time = "2026-07-07T14:34:06.115Z" }, + { url = "https://files.pythonhosted.org/packages/f1/78/c9c71d599f5aa2d42bcdd35cbbd46d7f535351a57e40ff7d8e5a7e219401/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4d6fcde76f94f5cb9e43e9e9a61f16dacefd228cbbf6f1a09bd9b219a92f1a1", size = 207406, upload-time = "2026-07-07T14:34:07.554Z" }, + { url = "https://files.pythonhosted.org/packages/f6/39/c914445c321a845097ce4f6ac7de9a18228a77b766272125a1ce00d851eb/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:898f0e9068ca27d37f8e83a5b962821df851532e6c4a7d615c1c033f9da6eedf", size = 225157, upload-time = "2026-07-07T14:34:09.061Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f2/c0d4b8508565a36bc5c624e88ed297f5b0b1095011034d7f5b83a69908b5/charset_normalizer-3.4.9-cp314-cp314-win32.whl", hash = "sha256:c1c948747b03be832dceed96ca815cef7360de9aa19d37c730f8e3f6101aca48", size = 151095, upload-time = "2026-07-07T14:34:10.901Z" }, + { url = "https://files.pythonhosted.org/packages/49/fd/a1d26144398c67486422a72bf5812cda22cb4ccfcd95a290fb41ceb4b8e2/charset_normalizer-3.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:16b65ea0f2465b6fb52aa22de5eca612aa964ddfec00a912e26f4656cbef890b", size = 162796, upload-time = "2026-07-07T14:34:12.47Z" }, + { url = "https://files.pythonhosted.org/packages/20/95/d75e82f8ce9fd323ebf059c16c9aadefb22a1ecde13b7840b35835e4886c/charset_normalizer-3.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:40a126142a56b2dfc0aacbad1de8310cbf60da7656db0e6b16eebd48e3e93519", size = 153334, upload-time = "2026-07-07T14:34:14.044Z" }, + { url = "https://files.pythonhosted.org/packages/00/5e/17398df3a139985ba9d11ed072531986f408c8fca952835ef1ab1820c02b/charset_normalizer-3.4.9-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:609b3ba8fcc0fb5ab7af00719d0fb6ad0cb518e48e7712d12fd68f1327951198", size = 338848, upload-time = "2026-07-07T14:34:15.688Z" }, + { url = "https://files.pythonhosted.org/packages/cd/91/7253a32e86b7e1d1239b1b36ba6dd0f021a21107ab33054b53119cc083b9/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51447e9aa2684679af07ca5021c3db526e0284347ebf4ffcec1154c3350cfe32", size = 223022, upload-time = "2026-07-07T14:34:17.248Z" }, + { url = "https://files.pythonhosted.org/packages/cb/32/2e64bd2be10e89c61e57ebe6a93fd98ae88eb7ebe414b5121f22c96c69eb/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cc1b0fff8ead343dae06305f954eb8468ba0ec1a97881f42489d198e4ce3c632", size = 241590, upload-time = "2026-07-07T14:34:18.813Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ef/d96ec496cfea0c21db43b0ad03891308b02388d054cc902cf0e5a1ad6a88/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa36ec09ef71d158186bc79e359ff5fdd6e7996fe8ab638f00d6b93139ba4fcf", size = 239584, upload-time = "2026-07-07T14:34:20.52Z" }, + { url = "https://files.pythonhosted.org/packages/d4/ce/9af95f7876194bd7a14e3dfe4a4de2e0bff02666a3910d72beafd06cc297/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df115d4d83168fdf2cae48ef1ff6d1cb4c466364e30861b37121de0f3bf1b990", size = 230224, upload-time = "2026-07-07T14:34:22.189Z" }, + { url = "https://files.pythonhosted.org/packages/52/94/af74dde74a3996bd959c350709bfe50e297823d70a8c1cbd54b838880863/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f86c6358749bd4fda175388691e3ba8c46e24c5347d0afd20f9b7edfc9faf07d", size = 212667, upload-time = "2026-07-07T14:34:23.857Z" }, + { url = "https://files.pythonhosted.org/packages/ee/f0/f1c4fe746c395922961b5916ed1d7d6e7d4c84851d19ed43cc89980ec953/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:32286a2c8d167e897177b673176c1e3e00d4057caf5d2b64eef9a3666b03018e", size = 227179, upload-time = "2026-07-07T14:34:25.586Z" }, + { url = "https://files.pythonhosted.org/packages/e4/56/6c745619ac397e8871e2bcd3cea1eec86b877488f33888b3aef5c3ed506e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:83aed2c10721ddd90f68140685391b50811a880af20654c59af6b6c66c40513c", size = 225372, upload-time = "2026-07-07T14:34:27.212Z" }, + { url = "https://files.pythonhosted.org/packages/78/ad/98aae8630ac71f16711968e38a5acfecce41b778bf2f0312851020f565a8/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cd6c3d4b783c556fa00bf540854e42f135e2f256abd29669fcd0da0f2dec79c2", size = 215222, upload-time = "2026-07-07T14:34:28.774Z" }, + { url = "https://files.pythonhosted.org/packages/f7/40/9593d54209765207a7f11073c06494c1721e4ca4a0a426c597679bf7f91e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ee2f2a527e3c1a6e6411eb4209642e138b544a2d72fe5d0d76daf77b24063534", size = 231958, upload-time = "2026-07-07T14:34:30.345Z" }, + { url = "https://files.pythonhosted.org/packages/b1/27/693ee5e8a18191eb38647360c51cd505013e2bd3b366aa43fd5344c21e3c/charset_normalizer-3.4.9-cp314-cp314t-win32.whl", hash = "sha256:0d861473f743244d349b50f850d10eb87aeb22bbdcc8e64f79273c94af5a8226", size = 155580, upload-time = "2026-07-07T14:34:31.884Z" }, + { url = "https://files.pythonhosted.org/packages/80/3f/bd97d3d9c613013d07cb7733d299385b41df37f0471310f5a73dc359f0b8/charset_normalizer-3.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:9b8e0f3107e2200b76f6054de99016eac3ee6762713587b36baaa7e4bd2ae177", size = 167620, upload-time = "2026-07-07T14:34:33.438Z" }, + { url = "https://files.pythonhosted.org/packages/3d/c6/eee9dca4439b1061f76373f06ea855678cc4a64c1c3c90b50e479edbb8eb/charset_normalizer-3.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:19ac87f93086ce37b86e098888555c4b4bc48102279bae3350098c0ed664b501", size = 158037, upload-time = "2026-07-07T14:34:35.018Z" }, + { url = "https://files.pythonhosted.org/packages/98/2b/f97f1c193fb855c345d678f5077d6926034db0722df74c8f057020e05a25/charset_normalizer-3.4.9-py3-none-any.whl", hash = "sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5", size = 64538, upload-time = "2026-07-07T14:34:56.993Z" }, ] [[package]] @@ -357,14 +362,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.3" +version = "8.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } +sdist = { url = "https://files.pythonhosted.org/packages/76/d4/81420972a676e8ffea40450d8c8c92943e7218a78fe9b64359836cc9876b/click-8.4.2.tar.gz", hash = "sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6", size = 338000, upload-time = "2026-06-24T17:45:15.148Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, ] [[package]] @@ -389,55 +394,61 @@ dev = [ [package.metadata.requires-dev] dev = [ { name = "nostr-sdk", specifier = ">=0.44" }, - { name = "pyln-client", specifier = ">=25.5" }, - { name = "pyln-proto", specifier = ">=25.5" }, - { name = "pyln-testing", specifier = ">=25.5" }, - { name = "pytest", specifier = ">=7.4,<9" }, + { name = "pyln-client", specifier = ">=25.9" }, + { name = "pyln-proto", specifier = ">=25.9" }, + { name = "pyln-testing", specifier = ">=25.9" }, + { name = "pytest", specifier = ">=8,<10" }, { name = "pytest-asyncio", specifier = ">=0.23.8,<2" }, { name = "pytest-timeout", specifier = ">=2.4,<3" }, { name = "pytest-xdist", specifier = ">=3.7,<4" }, ] [[package]] -name = "coincurve" -version = "20.0.0" +name = "coincurve-cp314-fix" +version = "22.0.1" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asn1crypto" }, - { name = "cffi" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d9/4c/9e5e51e6c12cec6444c86697992f9c6ccffa19f84d042ff939c8b89206ff/coincurve-20.0.0.tar.gz", hash = "sha256:872419e404300302e938849b6b92a196fabdad651060b559dc310e52f8392829", size = 122865, upload-time = "2024-06-02T18:15:50.787Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/f9/e9d3c502369933915d3e72d596a1ddf81b3d5ae986f5bd65bc5ada52e6b9/coincurve_cp314_fix-22.0.1.tar.gz", hash = "sha256:b68397f902a73ba15ab5652fae712f6ba85625e382026be2a5c5e698845a79f4", size = 129346, upload-time = "2026-07-17T14:49:10.202Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/0c/f6a8b06f461089aeab441824134ea5d5824dba3acaac0a9dbf8444cbe1d6/coincurve-20.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d559b22828638390118cae9372a1bb6f6594f5584c311deb1de6a83163a0919b", size = 1255634, upload-time = "2024-06-02T18:14:26.381Z" }, - { url = "https://files.pythonhosted.org/packages/62/c2/0dbabd2c6648f49f730fdcbba84c53b5ffaf452fca85c750633141fe049c/coincurve-20.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33d7f6ebd90fcc550f819f7f2cce2af525c342aac07f0ccda46ad8956ad9d99b", size = 1255532, upload-time = "2024-06-02T18:14:28.792Z" }, - { url = "https://files.pythonhosted.org/packages/f5/77/c4fa50f8cb5d050a9bcab806503acdd1705b0dfb5c554eed15cc18bc12e8/coincurve-20.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22d70dd55d13fd427418eb41c20fde0a20a5e5f016e2b1bb94710701e759e7e0", size = 1191928, upload-time = "2024-06-02T18:14:30.874Z" }, - { url = "https://files.pythonhosted.org/packages/a3/11/6254ea354a32a3a1d70722daf58f2ebf0f689f0940eaced5127233416553/coincurve-20.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46f18d481eaae72c169f334cde1fd22011a884e0c9c6adc3fdc1fd13df8236a3", size = 1194364, upload-time = "2024-06-02T18:14:32.684Z" }, - { url = "https://files.pythonhosted.org/packages/a7/a9/d8717d41eb02688691adc30d7348f7c5fdc78e977f4cea83ee84622050b5/coincurve-20.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9de1ec57f43c3526bc462be58fb97910dc1fdd5acab6c71eda9f9719a5bd7489", size = 1204658, upload-time = "2024-06-02T18:14:33.919Z" }, - { url = "https://files.pythonhosted.org/packages/55/13/40923832d99c18fb01a00f83e5f6f702156e71cc0eb5d6281535eee662af/coincurve-20.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a6f007c44c726b5c0b3724093c0d4fb8e294f6b6869beb02d7473b21777473a3", size = 1215298, upload-time = "2024-06-02T18:14:35.318Z" }, - { url = "https://files.pythonhosted.org/packages/4d/87/646462a7a7810c7a3dcadae8969e1b78d535bcff072c26b17588e93a39b8/coincurve-20.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0ff1f3b81330db5092c24da2102e4fcba5094f14945b3eb40746456ceabdd6d9", size = 1204504, upload-time = "2024-06-02T18:14:36.567Z" }, - { url = "https://files.pythonhosted.org/packages/01/58/fbb9a312d559aee701491435b691e409fb0efa12eabf269ff651d537fed4/coincurve-20.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82f7de97694d9343f26bd1c8e081b168e5f525894c12445548ce458af227f536", size = 1209298, upload-time = "2024-06-02T18:14:38.384Z" }, - { url = "https://files.pythonhosted.org/packages/ee/d0/1d5679c000b31f3b32512632d98571f2bb752cd25c127d6f5bf3711b6eae/coincurve-20.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:e905b4b084b4f3b61e5a5d58ac2632fd1d07b7b13b4c6d778335a6ca1dafd7a3", size = 1198934, upload-time = "2024-06-02T18:14:39.788Z" }, - { url = "https://files.pythonhosted.org/packages/a3/f6/8c1499f730fac49ec13740fb1c015ce8082fa6b917790056988559f22212/coincurve-20.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:3657bb5ed0baf1cf8cf356e7d44aa90a7902cc3dd4a435c6d4d0bed0553ad4f7", size = 1193319, upload-time = "2024-06-02T18:14:41.165Z" }, - { url = "https://files.pythonhosted.org/packages/24/a7/d60a41b3f0a546854c9b7ca65ab99a5fdf1c9e158ae264a580de8f23fd1c/coincurve-20.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:44087d1126d43925bf9a2391ce5601bf30ce0dba4466c239172dc43226696018", size = 1255635, upload-time = "2024-06-02T18:14:42.483Z" }, - { url = "https://files.pythonhosted.org/packages/b7/4a/727fab66c0fbecfd7beeb38467910bd3652a77df649565e30160a9d2bae2/coincurve-20.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ccf0ba38b0f307a9b3ce28933f6c71dc12ef3a0985712ca09f48591afd597c8", size = 1255536, upload-time = "2024-06-02T18:14:44.077Z" }, - { url = "https://files.pythonhosted.org/packages/0f/8b/25d4ae5bb60665023e6d71681fada88ee95b5010dae6fc0b44d8b23b8df1/coincurve-20.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:566bc5986debdf8572b6be824fd4de03d533c49f3de778e29f69017ae3fe82d8", size = 1191928, upload-time = "2024-06-02T18:14:45.739Z" }, - { url = "https://files.pythonhosted.org/packages/0d/86/8c32c512fa27bfe7cfe70329fd43ebac23c0c8cec202cf6e4f52854e7ce3/coincurve-20.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4d70283168e146f025005c15406086513d5d35e89a60cf4326025930d45013a", size = 1194365, upload-time = "2024-06-02T18:14:47.008Z" }, - { url = "https://files.pythonhosted.org/packages/fe/74/fefbe512f54df7d02a7ea4821b87cf199a91b3565cdf0c94448b3f6b1af1/coincurve-20.0.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:763c6122dd7d5e7a81c86414ce360dbe9a2d4afa1ca6c853ee03d63820b3d0c5", size = 1204658, upload-time = "2024-06-02T18:14:48.348Z" }, - { url = "https://files.pythonhosted.org/packages/09/68/05b29f881f628ce8e8468f5f7420f6c4d7c129f43964e81d15bf388ae67a/coincurve-20.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f00c361c356bcea386d47a191bb8ac60429f4b51c188966a201bfecaf306ff7f", size = 1215301, upload-time = "2024-06-02T18:14:49.84Z" }, - { url = "https://files.pythonhosted.org/packages/ee/5d/d91549cf5a163797b0724dc2dcd551b908b6beddb6598b37743df7f6f3ec/coincurve-20.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4af57bdadd2e64d117dd0b33cfefe76e90c7a6c496a7b034fc65fd01ec249b15", size = 1204505, upload-time = "2024-06-02T18:14:51.816Z" }, - { url = "https://files.pythonhosted.org/packages/37/0f/898022e08760fb57d281f3695576e859b0f8a8ac629670223d9066c3f60d/coincurve-20.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a26437b7cbde13fb6e09261610b788ca2a0ca2195c62030afd1e1e0d1a62e035", size = 1209305, upload-time = "2024-06-02T18:14:53.39Z" }, - { url = "https://files.pythonhosted.org/packages/57/b9/643567d3f680ddf8d1bf10a56112ae7755296500d8eaaef498be637a8533/coincurve-20.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:ed51f8bba35e6c7676ad65539c3dbc35acf014fc402101fa24f6b0a15a74ab9e", size = 1198932, upload-time = "2024-06-02T18:14:54.751Z" }, - { url = "https://files.pythonhosted.org/packages/b3/3a/898f5c12469b292042608dd0702bcb0420ec32bac6b1ca2a0dd790f922bd/coincurve-20.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:594b840fc25d74118407edbbbc754b815f1bba9759dbf4f67f1c2b78396df2d3", size = 1193318, upload-time = "2024-06-02T18:14:55.981Z" }, - { url = "https://files.pythonhosted.org/packages/8f/24/e1bf259dd57186fbdc7cec51909db320884162cfad5ec72cbaa63573ff9d/coincurve-20.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4df4416a6c0370d777aa725a25b14b04e45aa228da1251c258ff91444643f688", size = 1255671, upload-time = "2024-06-02T18:14:57.863Z" }, - { url = "https://files.pythonhosted.org/packages/0a/c5/1817f87d1cd5ff50d8537fe60fb96f66b76dd02da885d970952e6189a801/coincurve-20.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1ccc3e4db55abf3fc0e604a187fdb05f0702bc5952e503d9a75f4ae6eeb4cb3a", size = 1255565, upload-time = "2024-06-02T18:14:59.128Z" }, - { url = "https://files.pythonhosted.org/packages/90/9f/35e15f993717ed1dcc4c26d9771f073a1054af26808a0f421783bb4cd7e0/coincurve-20.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac8335b1658a2ef5b3eb66d52647742fe8c6f413ad5b9d5310d7ea6d8060d40f", size = 1191953, upload-time = "2024-06-02T18:15:01.047Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3d/6a9bc32e69b738b5e05f5027bace1da6722352a4a447e495d3c03a601d99/coincurve-20.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ac025e485a0229fd5394e0bf6b4a75f8a4f6cee0dcf6f0b01a2ef05c5210ff", size = 1194425, upload-time = "2024-06-02T18:15:02.919Z" }, - { url = "https://files.pythonhosted.org/packages/1a/a6/15424973dc47fc7c87e3c0f8859f6f1b1032582ee9f1b85fdd5d1e33d630/coincurve-20.0.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e46e3f1c21b3330857bcb1a3a5b942f645c8bce912a8a2b252216f34acfe4195", size = 1204678, upload-time = "2024-06-02T18:15:04.308Z" }, - { url = "https://files.pythonhosted.org/packages/6a/e7/71ddb4d66c11c4ad13e729362f8852e048ae452eba3dfcf57751842bb292/coincurve-20.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:df9ff9b17a1d27271bf476cf3fa92df4c151663b11a55d8cea838b8f88d83624", size = 1215395, upload-time = "2024-06-02T18:15:05.701Z" }, - { url = "https://files.pythonhosted.org/packages/b9/7d/03e0a19cfff1d86f5d019afc69cfbff02caada701ed5a4a50abc63d4261c/coincurve-20.0.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4155759f071375699282e03b3d95fb473ee05c022641c077533e0d906311e57a", size = 1204552, upload-time = "2024-06-02T18:15:07.107Z" }, - { url = "https://files.pythonhosted.org/packages/07/cd/e9bd4ca7d931653a35c74194da04191a9aecc54b8f48a554cd538dc810e4/coincurve-20.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0530b9dd02fc6f6c2916716974b79bdab874227f560c422801ade290e3fc5013", size = 1209392, upload-time = "2024-06-02T18:15:08.663Z" }, - { url = "https://files.pythonhosted.org/packages/99/54/260053f14f74b99b645084231e1c76994134ded49407a3bba23a8ffc0ff6/coincurve-20.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:eacf9c0ce8739c84549a89c083b1f3526c8780b84517ee75d6b43d276e55f8a0", size = 1198932, upload-time = "2024-06-02T18:15:10.786Z" }, - { url = "https://files.pythonhosted.org/packages/b4/b5/c465e09345dd38b9415f5d47ae7683b3f461db02fcc03e699b6b5687ab2b/coincurve-20.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:52a67bfddbd6224dfa42085c88ad176559801b57d6a8bd30d92ee040de88b7b3", size = 1193324, upload-time = "2024-06-02T18:15:12.511Z" }, + { url = "https://files.pythonhosted.org/packages/af/ca/ec5e2c4ba1df02844e1087e58636ecc78a096fa7e7dfd85cab286c0c1c31/coincurve_cp314_fix-22.0.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:9fec35946bfe7784e92aaa656b298b3b67f0a093f52c7970496dde4621bbe6c0", size = 1388687, upload-time = "2026-07-17T14:48:04.925Z" }, + { url = "https://files.pythonhosted.org/packages/a2/01/5c0435325516a6d7ce2958e90e4d050652d105465a187ff8e983a1271370/coincurve_cp314_fix-22.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f5f4abc2e639a4007ca212b7029ea9fc549dc1e1207348f1c8729e34b6e0a94c", size = 1383962, upload-time = "2026-07-17T14:48:06.636Z" }, + { url = "https://files.pythonhosted.org/packages/4c/93/d9ea4c7955a1f67740e492adca7d8ffdb7b9434c2ddc9628e578d37552bb/coincurve_cp314_fix-22.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2dc1deb3cec48571fa05064fa8dad3488a0d63e3f16fa731c7815f3f204a2cb", size = 1371893, upload-time = "2026-07-17T14:48:08.247Z" }, + { url = "https://files.pythonhosted.org/packages/d6/0c/eec3262ad43b0f40203656d1a1e52377d603a47f76de6352dd15659285bc/coincurve_cp314_fix-22.0.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b60d06533f4820023a4e91679c2181ccc29a23193d55fc51e6d55874186b67", size = 1370994, upload-time = "2026-07-17T14:48:09.9Z" }, + { url = "https://files.pythonhosted.org/packages/36/9c/733843a2423ec759ccdd78338dba53f9bb9093f011ea8b31feab07e510cc/coincurve_cp314_fix-22.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:01428e65cfac5bedbeb7a6c1e146970d8aa8a384a7dc39f0c45c38350c6960cc", size = 1377492, upload-time = "2026-07-17T14:48:11.756Z" }, + { url = "https://files.pythonhosted.org/packages/33/e1/6c8bca371a395b32ddd86b2bcbae146789984b0bdf329524f6e85a73846e/coincurve_cp314_fix-22.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b787b23c80501e1af903bfe4f89a07c51ccc02e2333913e152f4e4c2cdbf644f", size = 1374123, upload-time = "2026-07-17T14:48:13.33Z" }, + { url = "https://files.pythonhosted.org/packages/d7/52/e9a7a1c82d615e9f4060ac77509bae9d95e1c9a4ad24941abca9f356d091/coincurve_cp314_fix-22.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:6b02bca3564e795a945c0a2a7f575d3af7d6e3c0e84133c610ca84cf0b824d21", size = 1329759, upload-time = "2026-07-17T14:48:14.845Z" }, + { url = "https://files.pythonhosted.org/packages/5f/55/c858d0ce0a2a3ec590463838af052492680debae92ca38b071ee459f351c/coincurve_cp314_fix-22.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:c2a0afd16f410f0b9932cc2123ba1adceca8676cffd649c4a5c1fe344d3a55df", size = 1320063, upload-time = "2026-07-17T14:48:16.294Z" }, + { url = "https://files.pythonhosted.org/packages/74/d9/6e191269f241861ed25ce193bb1d24e8758eb83276a0bc28d7f929704d45/coincurve_cp314_fix-22.0.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c7beead9480fc1e47b3727172af2279f1043bacc68f1a7226af7687bd893058e", size = 1388699, upload-time = "2026-07-17T14:48:18.13Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1e/a330eba734c262182bd7d35595c1e4b72a32cced388b649ab826b8b93d47/coincurve_cp314_fix-22.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3a710296ef735bb96434a152101bcdef9bdabbec291305eb0e6cc864935e7db", size = 1383919, upload-time = "2026-07-17T14:48:19.759Z" }, + { url = "https://files.pythonhosted.org/packages/52/ef/fa17567e5044fdbe958534191b31e461affc2019df6e21f6644981bc2cb1/coincurve_cp314_fix-22.0.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8bb83a3ea92906d5fe1623c3554c4973c0b51cb7f3a5e10dae549eec9b1b1eb", size = 1371952, upload-time = "2026-07-17T14:48:21.412Z" }, + { url = "https://files.pythonhosted.org/packages/5c/d1/b6cbf61dcbb11b053891d593d82fb976e5cc44092fef09d0787c8edb7071/coincurve_cp314_fix-22.0.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9d88f8faf7156fddad08dbd032671d6bb2427a262fc57e81be5fc4f9d1357aa", size = 1370118, upload-time = "2026-07-17T14:48:23.358Z" }, + { url = "https://files.pythonhosted.org/packages/99/f5/8d629287c7c2e7b9efca2cab3cfa95a02c2312881551b39875cb81671edc/coincurve_cp314_fix-22.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8a41bd1c4012d8ff480a3972cc9300400530edb8e7f0d16617e0f241fe0b72d2", size = 1377513, upload-time = "2026-07-17T14:48:24.923Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ca/8aac5adcd9616dd73095a2c4a707b30bc31674fae8522de44992309d34df/coincurve_cp314_fix-22.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b9083d1e5042866a1e28e91e9d5c8fdcf51629dd2a273f6b405059e7c95945fb", size = 1373226, upload-time = "2026-07-17T14:48:26.512Z" }, + { url = "https://files.pythonhosted.org/packages/dd/98/1b4f2f26d6980bdd17a8f2864832f9dff895b032a49234b51389ab58596d/coincurve_cp314_fix-22.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:918b4ad4d1e4f4cf38c11aa39b9d842418987f898caccc6114b45aa081ae2c90", size = 1329815, upload-time = "2026-07-17T14:48:27.899Z" }, + { url = "https://files.pythonhosted.org/packages/4a/e1/21f17125e8b9160dd5f6db3f92da6e18b1f4cdececfc3a154f023a7f9ad7/coincurve_cp314_fix-22.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:ab926906a2718de26a3e23955103e41e39cb316e5cbb68489405d4a2bc2011c3", size = 1320212, upload-time = "2026-07-17T14:48:29.473Z" }, + { url = "https://files.pythonhosted.org/packages/4a/32/24fef91a512c51b57b5980207bdf4974ab6e998aefde3c50abb565b4f346/coincurve_cp314_fix-22.0.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:b077081c058338aaab7fe634275da0248fe59b469be9ec2bfa598e6e328fc5d1", size = 1389725, upload-time = "2026-07-17T14:48:31.19Z" }, + { url = "https://files.pythonhosted.org/packages/a6/7a/24b3079bee4bd2f081865e28a08eb13b909470c7d8ce3a8c4ac9aae80727/coincurve_cp314_fix-22.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0ed7e6c3434333ecc17b00834f17effa5f48fc89b3e130d19157e3b18e0bd149", size = 1384501, upload-time = "2026-07-17T14:48:32.942Z" }, + { url = "https://files.pythonhosted.org/packages/41/26/7a4fbaff523784b9339f247a315056f99439fe671705d92bd9df01612e59/coincurve_cp314_fix-22.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b72535b8303d45bfc47bb27e7b1b898cfba11ddb9ca6db9e528f0e23360da5f3", size = 1375639, upload-time = "2026-07-17T14:48:34.777Z" }, + { url = "https://files.pythonhosted.org/packages/82/2e/c6f93f5bbbe6c050f5425b9e1ebb1fc8a28e3074b4ebe6542c91be186e27/coincurve_cp314_fix-22.0.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9a56ec57a1ce4116db6281703c5c64d35a9fbcb4ed2ab65ac38109c412afb3b6", size = 1374117, upload-time = "2026-07-17T14:48:37.179Z" }, + { url = "https://files.pythonhosted.org/packages/a8/f0/911d0295ed3a199c7a4eb43d2f8ffff1d1dcca39f2128fe5fc7f1fe52c17/coincurve_cp314_fix-22.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0501e49a206b5ab4105f65daeb338dcf3559bd7d5392a03afc3f31db83f661fc", size = 1381644, upload-time = "2026-07-17T14:48:39.018Z" }, + { url = "https://files.pythonhosted.org/packages/be/68/b3ecefd5edccd3202f24c0cfb696aff0756e6095db9f7c67da78aff57f27/coincurve_cp314_fix-22.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7a5d93127fea898e53882ac106535fbc39ff4f2a26e9c5f23e1efae716bcf6a6", size = 1377529, upload-time = "2026-07-17T14:48:40.608Z" }, + { url = "https://files.pythonhosted.org/packages/57/54/be427f7e8b037b14b94eb97e22fab08b8d41535075dd1b1d7a668fc1d4dc/coincurve_cp314_fix-22.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:07fcc0a4b6c86566462c9918f6c68303e3699820c4df595daef9a8d131584f29", size = 1330618, upload-time = "2026-07-17T14:48:42.12Z" }, + { url = "https://files.pythonhosted.org/packages/c4/0b/1833d9b75a8d0dabba52db6a4e665cd0bf24401c513a062f82c8fc4f56a2/coincurve_cp314_fix-22.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:f7b282d27d234b96e064e6bf061d56a4148a4e4fb14f0309fdcc555206892ab6", size = 1320184, upload-time = "2026-07-17T14:48:43.588Z" }, + { url = "https://files.pythonhosted.org/packages/93/9f/2d2576282a16fcddd4bacaae9767852203bc6fe6a851a2410b0dffb0bc87/coincurve_cp314_fix-22.0.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:227158f4cbd329f0b75780198ab312ac72d697b2ec28daade45d0c3d8aa7ae3a", size = 1389695, upload-time = "2026-07-17T14:48:45.298Z" }, + { url = "https://files.pythonhosted.org/packages/9b/6a/fc8e76c2f6d62af177960a719b5987e644412b13fd673175dea4f2696370/coincurve_cp314_fix-22.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6cde6eda5348115cd46555aae8e5fc8a1329cdd34560293a371a3e8394390c16", size = 1384517, upload-time = "2026-07-17T14:48:46.86Z" }, + { url = "https://files.pythonhosted.org/packages/63/f1/f3a62079cb2d4205712545d70e7bd221b276b0b379329d844ed825076014/coincurve_cp314_fix-22.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:155c9364dcea3b4028fc03d02b5ac4c1d791c7989c54ea8d73ff52600fa9bab1", size = 1375601, upload-time = "2026-07-17T14:48:48.388Z" }, + { url = "https://files.pythonhosted.org/packages/ac/1c/d4769d938a000d14ce99f08c42148e8fda85b5b243943e804a5f27e4d951/coincurve_cp314_fix-22.0.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ccf73b280220759fa02251f09300d85af54ff23631da3e95c14eef30cba7408", size = 1374082, upload-time = "2026-07-17T14:48:49.852Z" }, + { url = "https://files.pythonhosted.org/packages/fd/88/246bad1924267d0064a7a6092d10851f66765caefe9a0010be8c25c94a6c/coincurve_cp314_fix-22.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5eb5a6aa08e8e07d2edb3a6b8e6602f0dc94a1b0fbf4d83138df749b921fe8b2", size = 1381599, upload-time = "2026-07-17T14:48:51.33Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e0/08dbec75aced7f23dba7dc52f1294f30e862de33cc9112bd01ccf3b3f50d/coincurve_cp314_fix-22.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0bde261973270d2313bf82d526a6a395d084faa74f514d98c2dfe96bcb3d331f", size = 1377435, upload-time = "2026-07-17T14:48:53.016Z" }, + { url = "https://files.pythonhosted.org/packages/28/ac/ff86fc78fa9dcf4792f57e0675030dd21701e0f121c165801a2434598e20/coincurve_cp314_fix-22.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:07ab4f37249dab54d3dc02e35dd82f0309c9995a1656f91ab1c8efebf7c35ed4", size = 1330553, upload-time = "2026-07-17T14:48:54.539Z" }, + { url = "https://files.pythonhosted.org/packages/1f/17/5f27565dd86fff04636b5a4a7a5450f6f35474b3c02ad6832852ce98e134/coincurve_cp314_fix-22.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:8d0d6057895da2d4fb7da49463c08ccbc3399a57df5b1ae18b296edcfe724cae", size = 1320215, upload-time = "2026-07-17T14:48:56.047Z" }, + { url = "https://files.pythonhosted.org/packages/5b/60/721383a5cc7079360afa5a235c8bbddbdf09505f39cc315d5eafba90f638/coincurve_cp314_fix-22.0.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6b18156408e60b12b87810ae323ef0b483251c33f05f172e344c325a034014e5", size = 1389892, upload-time = "2026-07-17T14:48:57.533Z" }, + { url = "https://files.pythonhosted.org/packages/fd/f2/9f8518e3b138256d80d40ccdb3cb8edb78dd01846ff221da3d212f6c44b9/coincurve_cp314_fix-22.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:15c9257f42a42c14e098e0f37b8b5fc32371854c84f3bebe2448a307ca674b3e", size = 1384743, upload-time = "2026-07-17T14:48:58.999Z" }, + { url = "https://files.pythonhosted.org/packages/44/40/913a7a3f02678bc1990e508fba9fa156d643a788cceb611d8c35b99a4436/coincurve_cp314_fix-22.0.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5c04475afd71244fb6e04af2d8f2e2ad98264f6b6f549826560ea96925425f33", size = 1375586, upload-time = "2026-07-17T14:49:00.759Z" }, + { url = "https://files.pythonhosted.org/packages/46/6d/21c2888ac918199cedd9b438a2bb5cd9e1746bff4cccbdf27f35c646b94d/coincurve_cp314_fix-22.0.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2e1066e72ac20310fb2359f0c7bdcc7d09f796eb3d316b3e03b3c5e59fab349", size = 1373843, upload-time = "2026-07-17T14:49:02.524Z" }, + { url = "https://files.pythonhosted.org/packages/61/64/d1ae34a2843dc76e69f65ef6c611fd3f09bc6a11924a56c555b6aea9339e/coincurve_cp314_fix-22.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0a629e282b8a9d531b882ee1735a6ce1cfc0c8c37531583131ade5c4ca5d08b1", size = 1381502, upload-time = "2026-07-17T14:49:03.993Z" }, + { url = "https://files.pythonhosted.org/packages/06/ad/1c0b90430feefd17594fda215880137a40bb7858dde62bb60c4b58cf0771/coincurve_cp314_fix-22.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f7697cc7d75de9b9e34e041cfe7dd903db2141a4c31c92ec420674f937c3af8", size = 1377090, upload-time = "2026-07-17T14:49:05.59Z" }, + { url = "https://files.pythonhosted.org/packages/7c/20/1267eb8492b9d2d7a904739476eebc7d4d3921d13e716621ca99128e25cb/coincurve_cp314_fix-22.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:56ddf3b2340c23c22fe16fbf171e18219de291cf2c26f75805c92640ec5ef8c8", size = 1334976, upload-time = "2026-07-17T14:49:07.092Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e7/9765c390984195f79c6b0d3ab299cc17749f57a9856651db30065cc66264/coincurve_cp314_fix-22.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:acfcd79081fc04e4f699731973c41165dfeee85b855dc352ee2d46250dd46f6e", size = 1327417, upload-time = "2026-07-17T14:49:08.695Z" }, ] [[package]] @@ -451,62 +462,59 @@ wheels = [ [[package]] name = "cryptography" -version = "48.0.0" +version = "49.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/99/d1c90d6041656cc6ee229dc99cd67fd0cd5aec3c5f7d72fffc27cc750054/cryptography-49.0.0.tar.gz", hash = "sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493", size = 854345, upload-time = "2026-06-12T20:02:30.512Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" }, - { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" }, - { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" }, - { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" }, - { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" }, - { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" }, - { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" }, - { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" }, - { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" }, - { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" }, - { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" }, - { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" }, - { url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" }, - { url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" }, - { url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" }, - { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" }, - { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" }, - { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" }, - { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" }, - { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" }, - { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" }, - { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" }, - { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" }, - { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" }, - { url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" }, - { url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" }, - { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" }, - { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" }, - { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" }, - { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" }, - { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" }, - { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" }, - { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" }, - { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" }, - { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" }, - { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" }, - { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" }, - { url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" }, - { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" }, - { url = "https://files.pythonhosted.org/packages/be/d2/024b5e06be9d44cb021fb0e1a03d34d63989cf56a0fe62f3dfbab695b9b4/cryptography-48.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:84cf79f0dc8b36ac5da873481716e87aef31fcfa0444f9e1d8b4b2cece142855", size = 3950391, upload-time = "2026-05-04T22:59:17.415Z" }, - { url = "https://files.pythonhosted.org/packages/bc/17/3861e17c56fa0fd37491a14a8673fdb77c57fc5693cafe745ea8b06dba75/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fdfef35d751d510fcef5252703621574364fec16418c4a1e5e1055248401054b", size = 4637126, upload-time = "2026-05-04T22:59:20.197Z" }, - { url = "https://files.pythonhosted.org/packages/f0/0a/7e226dbff530f21480727eb764973a7bff2b912f8e15cd4f129e71b56d1d/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:0890f502ddf7d9c6426129c3f49f5c0a39278ed7cd6322c8755ffca6ee675a13", size = 4667270, upload-time = "2026-05-04T22:59:22.647Z" }, - { url = "https://files.pythonhosted.org/packages/3b/f2/5a72274ca9f1b2a8b44a662ee0bf1b435909deb473d6f97bcd035bcdbc71/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:ecde28a596bead48b0cfd2a1b4416c3d43074c2d785e3a398d7ec1fc4d0f7fbb", size = 4636797, upload-time = "2026-05-04T22:59:24.912Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e1/48cedb2fe63626e91ded1edad159e2a4fb8b6906c4425eb7749673077ce7/cryptography-48.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:4defde8685ae324a9eb9d818717e93b4638ef67070ac9bc15b8ca85f63048355", size = 4666800, upload-time = "2026-05-04T22:59:27.474Z" }, - { url = "https://files.pythonhosted.org/packages/a2/ca/7e8365deec19afb2b2c7be7c1c0aa8f99633b54e90c570999acda93260fc/cryptography-48.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:db63bf618e5dea46c07de12e900fe1cdd2541e6dc9dbae772a70b7d4d4765f6a", size = 3739536, upload-time = "2026-05-04T22:59:29.61Z" }, + { url = "https://files.pythonhosted.org/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db", size = 4032100, upload-time = "2026-06-12T20:02:32.143Z" }, + { url = "https://files.pythonhosted.org/packages/09/41/3797cfaf69cae04a13ee78ebd83f0678d9c02b4779d21ce24445326f1a69/cryptography-49.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db", size = 4692978, upload-time = "2026-06-12T20:01:21.305Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8b/43011f7ebe515a8aa20d61f290a326cd890c2e738e16e59eaff8d9c3a412/cryptography-49.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325", size = 4716422, upload-time = "2026-06-12T20:01:48.566Z" }, + { url = "https://files.pythonhosted.org/packages/4a/91/01ce7303a4579e6d3a6abef01bd322848e9ea7a219adcabc5048b9033571/cryptography-49.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2", size = 4700503, upload-time = "2026-06-12T20:02:47.091Z" }, + { url = "https://files.pythonhosted.org/packages/62/99/a2c95cf8293f07491e9e27c20cc4dcd18176d944e674679adeb1d0173fd6/cryptography-49.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b", size = 5309779, upload-time = "2026-06-12T20:02:08.987Z" }, + { url = "https://files.pythonhosted.org/packages/20/2c/0622f20ff02b2ef32558733443805dc82fd4c275be01b2d19d14676f3a1b/cryptography-49.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6", size = 4749683, upload-time = "2026-06-12T20:02:03.335Z" }, + { url = "https://files.pythonhosted.org/packages/a3/5b/c5246635d5fd3b64e0d45ae10e99fd32fe9676a79915ccfe5a61ba9af1a5/cryptography-49.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c", size = 4337874, upload-time = "2026-06-12T20:02:54.323Z" }, + { url = "https://files.pythonhosted.org/packages/6d/88/05563c7fe2e914e87d1a536d06fe83e66b4e1d95cb593e05aea375531da8/cryptography-49.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7", size = 4700283, upload-time = "2026-06-12T20:01:34.822Z" }, + { url = "https://files.pythonhosted.org/packages/c4/b6/d7696e4e890d6ae1469935164c9e5215c557671cb78d6e3f458ccceaa632/cryptography-49.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68", size = 5265844, upload-time = "2026-06-12T20:01:24.09Z" }, + { url = "https://files.pythonhosted.org/packages/a9/3c/f3ad17eecc1a57b0ba236dc01f90e783c51f4a2f35f64777cc4f47a184b2/cryptography-49.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9", size = 4749290, upload-time = "2026-06-12T20:01:30.848Z" }, + { url = "https://files.pythonhosted.org/packages/4f/01/339573cf1023163a400b0b5d16f6d507de413b9f60be6fd1b77feeaf6737/cryptography-49.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f", size = 4834612, upload-time = "2026-06-12T20:01:29.246Z" }, + { url = "https://files.pythonhosted.org/packages/71/fd/577302e213a1be9468f92d1afef66fcf1ef83d516819d9992ca547f592bd/cryptography-49.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459", size = 4980804, upload-time = "2026-06-12T20:01:42.853Z" }, + { url = "https://files.pythonhosted.org/packages/1f/09/f42b1d190c5ba75f72062a387f8030d1d75f6ab035788f1d9c4b01de6525/cryptography-49.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:e5dfc1e64de5677cec922ffa8da89c546d0415bf6efdf081842e5d44c84e1f0e", size = 3810026, upload-time = "2026-06-12T20:02:39.262Z" }, + { url = "https://files.pythonhosted.org/packages/ec/9e/db72b3ae7fc9cfad53e630e56c6ae83b9b6ff0bf3718ffb8012d20b3aabf/cryptography-49.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:73a205dce83953d131a4aa1e0fd917a2fd1c5b1eef251e9d7152efefcbf5caf7", size = 4013892, upload-time = "2026-06-12T20:02:10.735Z" }, + { url = "https://files.pythonhosted.org/packages/86/12/c48a424f38db03027be9f7ed5c7dc5de9933dbee992865f98b13727a009d/cryptography-49.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:196ecd6a36e4e9aa10270393bb98d8df88fccee0bf1e5128b91ae4eb4375896d", size = 4678835, upload-time = "2026-06-12T20:02:48.743Z" }, + { url = "https://files.pythonhosted.org/packages/68/28/8a3ad4653662c93fc44dc4e5d8fd374c25c42e07b34bbfbadf49cf57a5a8/cryptography-49.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7abcee80084cda3f7691f3eb1ce480d8df49cec637b429aa35986c1de71738aa", size = 4697239, upload-time = "2026-06-12T20:02:56.03Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b2/2193fc74f81aee4f9b62733133b73b5176718932ed8f2e4b03fa040480a6/cryptography-49.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:4ae387c9cb68ea569ca17e490d66d8142b81c3cc814bf179974b7d146e490bbb", size = 4685593, upload-time = "2026-06-12T20:02:50.666Z" }, + { url = "https://files.pythonhosted.org/packages/47/f1/1d3eaa243bfc5de4a187b22aa8c048b3e4980bfbe830ac46e6bac2e66947/cryptography-49.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:f37d847238971164fdbc68ade6f6574aecc9c0af714190e2083429ff68f4ce9d", size = 5289961, upload-time = "2026-06-12T20:01:46.468Z" }, + { url = "https://files.pythonhosted.org/packages/58/39/2d51306721330c486495853eda1c567880ff036de15a14c4b74f399934af/cryptography-49.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:c2bc30226390d60ea19d9f82b19db005fe0452154a23c1c410c12ea801e43561", size = 4731145, upload-time = "2026-06-12T20:02:16.832Z" }, + { url = "https://files.pythonhosted.org/packages/17/50/983e838c7fd0d87fd8c969bcdd328edaf5f756e38df5281637424c155873/cryptography-49.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:07cab27cc7b7e0fd28e5e26bb9eeedde5c135c868b46de4a27845abe94af6122", size = 4321719, upload-time = "2026-06-12T20:02:52.611Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f5/8f571d7e27c55bce9f76f026143bcb1e040a4233149ecca0bea5fa5dd5f7/cryptography-49.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:b20133d204d2bb56ba047642199603876c872026ca53e79c35b83772ab2cc505", size = 4685209, upload-time = "2026-06-12T20:02:07.282Z" }, + { url = "https://files.pythonhosted.org/packages/e7/84/0e27016a6fc5a0886f797018b26aa42f40c09a82332bff77822a451deaaa/cryptography-49.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b970c6da94d5bb18629db453d14f2a1300f6bf59b61e9b82377931ef95504866", size = 5246285, upload-time = "2026-06-12T20:01:32.439Z" }, + { url = "https://files.pythonhosted.org/packages/11/2d/5e1fb307cb5931881516b464c98774b3f2c36b5d4bb9a2830253cf553cad/cryptography-49.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d8ecde755e2e91bf773fc94e8c9d730cd7f2007004cb492263a794ec3899a1c8", size = 4730441, upload-time = "2026-06-12T20:02:01.469Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c0/bff5a02ee731d207d6a1ed51732549d8c53d2bc8da1d10ec6f2844201d68/cryptography-49.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e3fb64c420688e5319ae25113a354015abbd8dffbfbc41781a1ea66fc7622ac3", size = 4815869, upload-time = "2026-06-12T20:01:36.574Z" }, + { url = "https://files.pythonhosted.org/packages/b9/26/814681d14248d95d73d5c3eea0c39a94eb8302df966f670a2c60de90974b/cryptography-49.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32703d93296f5c1f4b53349ad3a250c2cae0fdecd3a3dd5d47e616d8d616af27", size = 4960948, upload-time = "2026-06-12T20:02:18.688Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fe/93ecac273d3738939d023612ad12cca9a3740a5345d69fda04134c43fd96/cryptography-49.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:33cd0565932807baddb67b96dbee92f2c374b5c89dee09fd74079aeb8c8dba61", size = 3799153, upload-time = "2026-06-12T20:01:39.059Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/5bb823f5bedcf80718cea7fbc95ec5515cca3769633c4b01a32be7f30e7c/cryptography-49.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a", size = 4025947, upload-time = "2026-06-12T20:01:25.745Z" }, + { url = "https://files.pythonhosted.org/packages/3d/df/40577043ca124e17012f408ddddaeb213b856336ac82ddb3bc915f39e29f/cryptography-49.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4", size = 4692429, upload-time = "2026-06-12T20:01:53.628Z" }, + { url = "https://files.pythonhosted.org/packages/2c/99/2d13299eb3dd27b02dcfaafcc91d6b5cb3329f7cbd6d8f51921acd566c1a/cryptography-49.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18", size = 4700968, upload-time = "2026-06-12T20:02:45.383Z" }, + { url = "https://files.pythonhosted.org/packages/a5/4d/9c0cd02f95e2602dd5e563da149ee0830abef3537be8b34dc56281ebe27a/cryptography-49.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69", size = 4697758, upload-time = "2026-06-12T20:01:41.13Z" }, + { url = "https://files.pythonhosted.org/packages/24/01/186c825898477d77e2324d5360fefe622ff1d8d1963ec0554e2cada8ec77/cryptography-49.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64", size = 5298863, upload-time = "2026-06-12T20:02:24.579Z" }, + { url = "https://files.pythonhosted.org/packages/b8/7b/62cbbab75d0659865bf0273790031544a0b16c8072d258f9428dcd8190dc/cryptography-49.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21", size = 4735983, upload-time = "2026-06-12T20:01:50.14Z" }, + { url = "https://files.pythonhosted.org/packages/6c/72/3e798c064bc39e471008075d0f9bc9daf77a80879c092e4a8e170c585ed4/cryptography-49.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9", size = 4334173, upload-time = "2026-06-12T20:01:44.743Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ee/6fca21d1ac73e06f8bef71940abfd4d2f6472b4bca284d770f32bd4086f6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc", size = 4697298, upload-time = "2026-06-12T20:02:20.918Z" }, + { url = "https://files.pythonhosted.org/packages/67/d0/a5fcd3515f0bae49a7b6d0413cc1bdccdcc1fc0047037a0d480642cdc5d6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8", size = 5254338, upload-time = "2026-06-12T20:02:22.737Z" }, + { url = "https://files.pythonhosted.org/packages/a0/84/84fe36f19caf857d61cb7fc9c63035a47ffabd84ea12d1d393148efa3615/cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36", size = 4735650, upload-time = "2026-06-12T20:02:41.389Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a0/db537264e234f7273a73ec020873d6d6b39dfd8a53db78b550ca8320440e/cryptography-49.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e", size = 4834820, upload-time = "2026-06-12T20:01:51.847Z" }, + { url = "https://files.pythonhosted.org/packages/93/77/8df9eb486495979bccecd1062e2eaf435250e84437040295b57d09048b0b/cryptography-49.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b", size = 4967968, upload-time = "2026-06-12T20:02:12.524Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e6/f60198ea8d9dfa15fff9ed4ca02ce362f6eadd9ba757dcc50634c4257b63/cryptography-49.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001", size = 3785547, upload-time = "2026-06-12T20:02:26.847Z" }, + { url = "https://files.pythonhosted.org/packages/63/d3/4a83af35d65e3fad632c926fad684c193ea4398569ccb0bbbc7fe8f5dc9a/cryptography-49.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc1e275c2f1d97b1a6450b8b0ea3ebfa6e087a611c2b26cb2404d48588abab7b", size = 3993685, upload-time = "2026-06-12T20:02:14.883Z" }, + { url = "https://files.pythonhosted.org/packages/d6/a7/f9dac0ab7f80368c56993a7bf638ef9935f825c91902798481fac0898138/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83782480a4a9da4d0feb51950131ba32e12e70813848b3343f6e18c28a66838", size = 4676239, upload-time = "2026-06-12T20:02:28.793Z" }, + { url = "https://files.pythonhosted.org/packages/d7/70/2ba3769dd0ae167e2f33dfa9592d45db6ff9a61d62ca1a5b3d1bdd09068f/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b39efa323140595abd3ecca8529d321ae50f55f3aa3ba9cc81ea56a6011953d5", size = 4715584, upload-time = "2026-06-12T20:01:27.495Z" }, + { url = "https://files.pythonhosted.org/packages/94/64/2923570ac1c0bd3a737aa366ac3abbbbde273042308b8cde95e2364a6e6a/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:b47db11c2c3525083296069b98ac5221907455e989ae0c2e3008bde851921615", size = 4675885, upload-time = "2026-06-12T20:01:55.49Z" }, + { url = "https://files.pythonhosted.org/packages/ab/f8/614dc7e051418cfe53d55173c1e24c6b0085e89996fe90508c2fdf769aef/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:084ef1af862eb07ec46d25f68689f2102a9fc0e05ce7b80f14f5fe51e4eef0f6", size = 4715449, upload-time = "2026-06-12T20:02:05.469Z" }, + { url = "https://files.pythonhosted.org/packages/aa/50/a9caea39ad19c431c1a3f8a31114df65b260cdfe67786b6c7e7c040c4c44/cryptography-49.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be9fcb48a55f023493482827d4f459bd263cc20efde64f204b97c123201850c6", size = 3783731, upload-time = "2026-06-12T20:02:43.319Z" }, ] [[package]] @@ -523,7 +531,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -558,11 +566,11 @@ wheels = [ [[package]] name = "idna" -version = "3.14" +version = "3.18" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/b1/efac073e0c297ecf2fb33c346989a529d4e19164f1759102dee5953ee17e/idna-3.14.tar.gz", hash = "sha256:466d810d7a2cc1022bea9b037c39728d51ae7dad40d480fc9b7d7ecf98ba8ee3", size = 198272, upload-time = "2026-05-10T20:32:15.935Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/3c/3f62dee257eb3d6b2c1ef2a09d36d9793c7111156a73b5654d2c2305e5ce/idna-3.14-py3-none-any.whl", hash = "sha256:e677eaf072e290f7b725f9acf0b3a2bd55f9fd6f7c70abe5f0e34823d0accf69", size = 72184, upload-time = "2026-05-10T20:32:14.295Z" }, + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, ] [[package]] @@ -585,14 +593,14 @@ wheels = [ [[package]] name = "jaraco-functools" -version = "4.4.0" +version = "4.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "more-itertools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/1f/c23395957d41ccf27c4e535c3d334c4051e5395b3752057ba4cbaec35c56/jaraco_functools-4.6.0.tar.gz", hash = "sha256:880c577ec9720b3a052d5bc611fb9f2269b3d87902ef42440df443b88e443280", size = 20837, upload-time = "2026-07-14T01:28:02.544Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, + { url = "https://files.pythonhosted.org/packages/02/36/ecc85bc96c273dc8a11273ed4782272975e6338d4a3e9228621175edf0e3/jaraco_functools-4.6.0-py3-none-any.whl", hash = "sha256:99e3dc0060c5cbe8fcd1cdb36258e2a65ca40f1566b2033b12abb1bb44dd3c30", size = 11677, upload-time = "2026-07-14T01:28:01.59Z" }, ] [[package]] @@ -615,7 +623,8 @@ dependencies = [ { name = "attrs" }, { name = "jsonschema-specifications" }, { name = "referencing" }, - { name = "rpds-py" }, + { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "rpds-py", version = "2026.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } wheels = [ @@ -730,31 +739,31 @@ wheels = [ [[package]] name = "more-itertools" -version = "11.0.2" +version = "11.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/f7/139d22fef48ac78127d18e01d80cf1be40236ae489769d17f35c3d425293/more_itertools-11.0.2.tar.gz", hash = "sha256:392a9e1e362cbc106a2457d37cabf9b36e5e12efd4ebff1654630e76597df804", size = 144659, upload-time = "2026-04-09T15:01:33.297Z" } +sdist = { url = "https://files.pythonhosted.org/packages/de/1d/f4da6f02cdffe04d6362210b807146a26044c88d839208aec273bb0d9184/more_itertools-11.1.0.tar.gz", hash = "sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d", size = 145772, upload-time = "2026-05-22T14:14:29.909Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/98/6af411189d9413534c3eb691182bff1f5c6d44ed2f93f2edfe52a1bbceb8/more_itertools-11.0.2-py3-none-any.whl", hash = "sha256:6e35b35f818b01f691643c6c611bc0902f2e92b46c18fffa77ae1e7c46e912e4", size = 71939, upload-time = "2026-04-09T15:01:32.21Z" }, + { url = "https://files.pythonhosted.org/packages/e8/3d/1087453384dbde46a8c7f9356eead2c58be8a7bf156bca40243377c85715/more_itertools-11.1.0-py3-none-any.whl", hash = "sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192", size = 72226, upload-time = "2026-05-22T14:14:28.824Z" }, ] [[package]] name = "nostr-sdk" -version = "0.44.2" +version = "0.44.6" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/35/ea/cd40b12352a5cba810116641959a7054f83d5f48d16e5e47614992b9ba64/nostr_sdk-0.44.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:184075689531e34085bc1e71b62f3a964df2d4aeb15a25f94557e15a4294c584", size = 3360561, upload-time = "2026-01-29T10:56:37.285Z" }, - { url = "https://files.pythonhosted.org/packages/ed/66/81057ec283cb6bcc5df27c1d58a0a7996afa4fb96c0c9a4570ae50190f9a/nostr_sdk-0.44.2-cp39-abi3-macosx_11_0_x86_64.whl", hash = "sha256:65e85c79295f4e258e5276a82b52e36ffb3f5cf59c2c7c4a959970ed9303cf6f", size = 3475196, upload-time = "2026-01-29T10:56:39.296Z" }, - { url = "https://files.pythonhosted.org/packages/61/fd/567b67c832b6263817470885e92b059b7d19cd9967b83ef1f59b6e04e603/nostr_sdk-0.44.2-cp39-abi3-manylinux_2_17_aarch64.whl", hash = "sha256:d1d557aeb8a423dcd054f699bbf7371eb268a6cec5916bf147957cb3f9f7da02", size = 3613359, upload-time = "2026-01-29T10:56:41.079Z" }, - { url = "https://files.pythonhosted.org/packages/fc/93/cdf2cd98f8fbb045a1ca12f073ad747342961b0a3bbe9cd6a09760cb0132/nostr_sdk-0.44.2-cp39-abi3-manylinux_2_17_armv7l.whl", hash = "sha256:41b1bb050f890f81b4c4edaf4d6cc3054ae9e783911eb4ce7dce6b8e97dfa60e", size = 3364451, upload-time = "2026-01-29T10:56:42.987Z" }, - { url = "https://files.pythonhosted.org/packages/8d/ec/7e9e9630af0b49e931cff8f65a5c9ce4086e9f2c372213d355b53871253c/nostr_sdk-0.44.2-cp39-abi3-manylinux_2_17_i686.whl", hash = "sha256:db584ba95de5abbb74da77036c3316e9a166bc22d9d2ea3ca38fc09325d9c6a9", size = 3603547, upload-time = "2026-01-29T10:56:44.404Z" }, - { url = "https://files.pythonhosted.org/packages/6a/03/c7ed0230f84615e2930259788b1fc1c485b159093f2070125ae9ddae327e/nostr_sdk-0.44.2-cp39-abi3-manylinux_2_17_x86_64.whl", hash = "sha256:4e1dccf29b7ad6eeb44945175a45334d4e45d82881ae5f3a28433899f155831c", size = 3726609, upload-time = "2026-01-29T10:56:45.709Z" }, - { url = "https://files.pythonhosted.org/packages/d1/8d/bfb2172743a99a21bbba0a4f77572be50cb8b10dbcf837b70be0e0b8f4b7/nostr_sdk-0.44.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:aa347c6437dc33ae45cc2ed56177f5aeded0fda42285b1b325a840de44e1f708", size = 3607268, upload-time = "2026-01-29T10:56:47.312Z" }, - { url = "https://files.pythonhosted.org/packages/a3/af/cb61a46d91bf61f15b628d188a2338b9e76e9fee4078728d4a7ded876381/nostr_sdk-0.44.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:a7a03b35ddbc69f31d79bea17ac60b8d250ef62bcf1d52fc33b9e36e0ecf772b", size = 3360529, upload-time = "2026-01-29T10:56:49.248Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dc/5405965ef11cc784be2dd473297caf997f8fa00b5f2e92884a029eae6eee/nostr_sdk-0.44.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:e53682dbf192acd137a92ae5d91903fbb22728d55452a8fa2315581f1d8413f2", size = 3494705, upload-time = "2026-01-29T10:56:51.158Z" }, - { url = "https://files.pythonhosted.org/packages/03/89/febbbec3d20cb63ab30eee84f59cb06317f24b8e2ab15c2d99212d71d622/nostr_sdk-0.44.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b3e6dd4191997546f157aba4532a995d06ad8b0cf9c17e77c0015553f06a9cf2", size = 3727113, upload-time = "2026-01-29T10:56:52.513Z" }, - { url = "https://files.pythonhosted.org/packages/67/d5/718b0428af9a3c9e33bd6a21b2b32920559db356e0369d0013b6421286d9/nostr_sdk-0.44.2-cp39-abi3-win32.whl", hash = "sha256:aa94a18eb8f4d77559c0290c0f17d6816c00f4fdddba5f1fcf96ff01b4690b42", size = 3164430, upload-time = "2026-01-29T10:56:54.334Z" }, - { url = "https://files.pythonhosted.org/packages/96/67/3f19800f6a52c2e1e7ba2b2b04ed0fca7a11a1819a2d02cdd961c4f4a54b/nostr_sdk-0.44.2-cp39-abi3-win_amd64.whl", hash = "sha256:31e609a864c3857cddde70d8a91181e2b2cadf923e562b6a9d82e655edded117", size = 3384420, upload-time = "2026-01-29T10:56:56.19Z" }, - { url = "https://files.pythonhosted.org/packages/71/bb/b2c2bb390fb309477bff63507c9d6058bea55d01f4938a749269dd5dd8bc/nostr_sdk-0.44.2-cp39-abi3-win_arm64.whl", hash = "sha256:3a9d3284af0547e224bf705d90cd6d9ca5a1b48f5becea1021da915de4408310", size = 3233797, upload-time = "2026-01-29T10:56:57.538Z" }, + { url = "https://files.pythonhosted.org/packages/61/c3/1fbcb47aff23b368b817e5f6e90f3284b4b299e9bcf08e9a4cd414b80020/nostr_sdk-0.44.6-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:41dabb2539513db0b354c4fd2a3391307e50eb913a60c9325b1565e80e98530f", size = 3365132, upload-time = "2026-07-26T09:02:22.234Z" }, + { url = "https://files.pythonhosted.org/packages/43/08/16525328d477e1fb427d47c0ccc14ce5b25f120871947374457a3267ae99/nostr_sdk-0.44.6-cp39-abi3-macosx_11_0_x86_64.whl", hash = "sha256:326526e6fd736c4dd6ad73b5cf7653d1a0ae46a3fb8d8ed4b4cb221ee64f7feb", size = 3482251, upload-time = "2026-07-26T09:02:23.623Z" }, + { url = "https://files.pythonhosted.org/packages/65/a4/0f210f8490faf4c24dbc4bfbdc1943afc632d907c6fae48f88f0f3b55605/nostr_sdk-0.44.6-cp39-abi3-manylinux_2_17_aarch64.whl", hash = "sha256:811637b021cfa338a38e37dda8df5dbeebe447458d567ad9b700a65199f6874c", size = 3611794, upload-time = "2026-07-26T09:02:24.758Z" }, + { url = "https://files.pythonhosted.org/packages/f2/73/ce525eed5f93969c62984f9397c80acf99883eab52bc055259e189eb1375/nostr_sdk-0.44.6-cp39-abi3-manylinux_2_17_armv7l.whl", hash = "sha256:44cbec5c99a8a8c8e8beeb80ae7427a3ff20b943ca459bd0ba556e41c20dd246", size = 3359056, upload-time = "2026-07-26T09:02:26.101Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ad/f700ddbbfcf2d0ff2285dec9b87b8b290d1f4359add21cb09b568bf52c0f/nostr_sdk-0.44.6-cp39-abi3-manylinux_2_17_i686.whl", hash = "sha256:64e0304313bbbaa05226d9cac98618f94ab20e8e3f5e3a297179454e657d3446", size = 3607461, upload-time = "2026-07-26T09:02:27.393Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ec/947f9fd4161a69da12bdbb8502a377c5ada2ab8e640ce5515c4a001cf81f/nostr_sdk-0.44.6-cp39-abi3-manylinux_2_17_x86_64.whl", hash = "sha256:0234eae16b0fc11e3d72eb08c4c14b0c333a992e700d8fb88f191a42ef017a07", size = 3726683, upload-time = "2026-07-26T09:02:28.925Z" }, + { url = "https://files.pythonhosted.org/packages/01/fe/448443ef9e23433f202c0362c2eb9e1b98dc0efffb5c3f9cae6f6252a5a8/nostr_sdk-0.44.6-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d16adc26a42cab37ae4ae8e3a8bed03dcedbe88c7862133c48cf9d99cb9e0cf3", size = 3605361, upload-time = "2026-07-26T09:02:30.308Z" }, + { url = "https://files.pythonhosted.org/packages/df/02/b0e83f3f5066e525e5c1fd44c81f6711479cc6942aee298ebcd545fb3e59/nostr_sdk-0.44.6-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:dccebbc33500718ace5cf8e7ac6ed7fa41ec07f65f157896b392ac5f0b9ab2ed", size = 3360640, upload-time = "2026-07-26T09:02:31.615Z" }, + { url = "https://files.pythonhosted.org/packages/9e/01/7104eacda8609d52a148171354bca1095227a651860022984bf518ead1f2/nostr_sdk-0.44.6-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:e73a55e1f52dd4cb006f502725e65ab12b68609cb1b18d96b2ba74a3b245d264", size = 3493797, upload-time = "2026-07-26T09:02:32.973Z" }, + { url = "https://files.pythonhosted.org/packages/00/80/e8de31cf0f1cf4c65ca5691a6c8baabbed19daccdccb427d73968e0335b9/nostr_sdk-0.44.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:512229272a68cc651bbe96b1d0ec78d12b899708d871ef92b49ce96eb33ba1fd", size = 3724890, upload-time = "2026-07-26T09:02:34.219Z" }, + { url = "https://files.pythonhosted.org/packages/83/0f/6d4c364bd4f1ad1c76030aeddc43c5f6edd63d79cec289229de6afa5a72d/nostr_sdk-0.44.6-cp39-abi3-win32.whl", hash = "sha256:5eee3919224b653007e71a8e8a43465ee36156132839dfd15c120ee9b54d075c", size = 3164542, upload-time = "2026-07-26T09:02:35.291Z" }, + { url = "https://files.pythonhosted.org/packages/90/f5/11141f5612f8fb361d39dbb95d6ec916059213a8d5625f8f98b58a7cfc70/nostr_sdk-0.44.6-cp39-abi3-win_amd64.whl", hash = "sha256:f89a13a4cec623283cd66218b8efedc8ec618b1b3e33a0e398240469f7b317e6", size = 3383380, upload-time = "2026-07-26T09:02:36.804Z" }, + { url = "https://files.pythonhosted.org/packages/d0/3e/552318b5c8307ab2f725de59b9bedbd06df3f031c252d11b15dcea302511/nostr_sdk-0.44.6-cp39-abi3-win_arm64.whl", hash = "sha256:c36845d218118dc0330f554fdea5c6e0c4a73bc54cb74dcf628750bf9260e01f", size = 3236001, upload-time = "2026-07-26T09:02:37.906Z" }, ] [[package]] @@ -895,36 +904,36 @@ wheels = [ [[package]] name = "pyln-client" -version = "26.4.1" +version = "26.6.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyln-bolt7" }, { name = "pyln-proto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/fe/e1e19c59dd7520dec2de992f0a3da6292db24162c9830be279616a7b8810/pyln_client-26.4.1.tar.gz", hash = "sha256:26dfb21ac39c61b7ab7a40db18798b002f3f19a179ff2ec40374a9419d032050", size = 92170, upload-time = "2026-04-24T04:53:08.46Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/5f/7e52fbe6b6d7d65651d2c908e1f68449256b3007a8259fa589282e9a4c0f/pyln_client-26.6.6.tar.gz", hash = "sha256:01c02021a62347d4d7dbaaa97bb627e9c2b60212a2e146107a782c1c156588ba", size = 92480, upload-time = "2026-07-20T12:39:51.278Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/65/90/d3b5e11f85cf076b1011a1ea6031ced3745abf451e47a1697216e1ea7c29/pyln_client-26.4.1-py3-none-any.whl", hash = "sha256:a9afba9b117818023bf4567a94c34633e9f2e89f0e1ca11e433329a2c678a685", size = 37673, upload-time = "2026-04-24T04:53:07.187Z" }, + { url = "https://files.pythonhosted.org/packages/a6/c0/2efddeec3dda9c915b02fc406bcc1f179d8c45cc9a2e8089608cbdde1a0e/pyln_client-26.6.6-py3-none-any.whl", hash = "sha256:6afa9019aef1e54fa01e185be160773ac4db2fdc7cf864a319e7374b71cd6a6d", size = 37975, upload-time = "2026-07-20T12:39:50.024Z" }, ] [[package]] name = "pyln-proto" -version = "26.4.1" +version = "26.6.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "base58" }, { name = "bitstring" }, - { name = "coincurve" }, + { name = "coincurve-cp314-fix" }, { name = "cryptography" }, { name = "pysocks" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8c/4b/c8d676ad84d6066553b203668c0e36f45cfae0a4d7140eb88aed6d65b38b/pyln_proto-26.4.1.tar.gz", hash = "sha256:d73f261bacc06a6a613dcece05fc429699b740a4673f24dffb7c41bd3542d589", size = 44864, upload-time = "2026-04-24T04:53:10.196Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a8/97/219900f708391678165461e6b519c6272edb5966b2e20c62856e9c1f736a/pyln_proto-26.6.6.tar.gz", hash = "sha256:b6d424eb22d33de9fc21e4216502759e34a62eaf460a47cc45dbbb208096db8a", size = 44879, upload-time = "2026-07-20T12:40:07.68Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/f1/6541b6cd8a017d3505fcb49885d3f01b8e6bb09e40db18e686edb9edeb99/pyln_proto-26.4.1-py3-none-any.whl", hash = "sha256:b5d10d5e69cf3debd9b289e2ffa49713fca6c349a71cb0eaf9e733024f15b3da", size = 31833, upload-time = "2026-04-24T04:53:08.686Z" }, + { url = "https://files.pythonhosted.org/packages/2c/18/ed5d323f55350b175e5f8bbed7b0c8acb988eb48ee37dd5b44d9e7a41799/pyln_proto-26.6.6-py3-none-any.whl", hash = "sha256:b7b12769e2c37e6d1ce7c2063bbab67f5959e411459ff089d0dbc14109778e03", size = 31833, upload-time = "2026-07-20T12:40:06.677Z" }, ] [[package]] name = "pyln-testing" -version = "26.4.1" +version = "26.6.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cheroot" }, @@ -939,9 +948,9 @@ dependencies = [ { name = "python-bitcoinlib" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/64/73/d969847564591fe16633b11bf8be9f8184ccd885e367acee171e9fd6d2d8/pyln_testing-26.4.1.tar.gz", hash = "sha256:0a40d66ec2f0840df95bdbc4845c214aede01e9d7a89a422f5b6b00a89e63690", size = 52617, upload-time = "2026-04-24T04:53:10.904Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/86/49b10e9317142f989dc03c7d42d512789a83000da7660177393a9d7e681b/pyln_testing-26.6.6.tar.gz", hash = "sha256:31caa503724d02bb736d8b1e11cd344cff15e0229bc6f709bd1571df5066b432", size = 53213, upload-time = "2026-07-20T12:39:37.337Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/ad/603ff35f7b60bcc6cf364a101c58a73887132063f0773767108946361ed2/pyln_testing-26.4.1-py3-none-any.whl", hash = "sha256:dd0e9d8a865593ddc9a14ef43e9aaec0fe0d348ce9ec2fcd23807a35c98c9e8c", size = 54381, upload-time = "2026-04-24T04:53:09.599Z" }, + { url = "https://files.pythonhosted.org/packages/3c/28/58c3524d0d1c1e244580bd82730f7c0e72b60e5a52b0d34c4395e7f46a1d/pyln_testing-26.6.6-py3-none-any.whl", hash = "sha256:24b814f6977faa1e66f98a6a447b397d148ba683c53a73e595a429c53a88af78", size = 54976, upload-time = "2026-07-20T12:39:36.32Z" }, ] [[package]] @@ -955,7 +964,7 @@ wheels = [ [[package]] name = "pytest" -version = "8.4.2" +version = "9.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -966,23 +975,23 @@ dependencies = [ { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, ] [[package]] name = "pytest-asyncio" -version = "1.3.0" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, { name = "pytest" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, + { url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" }, ] [[package]] @@ -1025,7 +1034,8 @@ version = "0.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, - { name = "rpds-py" }, + { name = "rpds-py", version = "0.30.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "rpds-py", version = "2026.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } @@ -1035,7 +1045,7 @@ wheels = [ [[package]] name = "requests" -version = "2.34.0" +version = "2.34.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -1043,15 +1053,18 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/b8/7a707d60fea4c49094e40262cc0e2ca6c768cca21587e34d3f705afec47e/requests-2.34.0.tar.gz", hash = "sha256:7d62fe92f50eb82c529b0916bb445afa1531a566fc8f35ffdc64446e771b856a", size = 142436, upload-time = "2026-05-11T19:29:51.717Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/e6/e300fce5fe83c30520607a015dabd985df3251e188d234bfe9492e17a389/requests-2.34.0-py3-none-any.whl", hash = "sha256:917520a21b767485ce7c588f4ebb917c436b24a31231b44228715eaeb5a52c60", size = 73021, upload-time = "2026-05-11T19:29:49.923Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, ] [[package]] name = "rpds-py" version = "0.30.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, @@ -1170,6 +1183,132 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, ] +[[package]] +name = "rpds-py" +version = "2026.6.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/2a/9618a122aeb2a169a28b03889a2995fe297588964333d4a7d67bdf46e147/rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4", size = 64051, upload-time = "2026-06-30T07:17:53.009Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/1f/a2dca5ffdbf1d475ffc4e80e4d5d720ff3a00f691795910116960ee12511/rpds_py-2026.6.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7b689145a1485c335569bd056464f3243a29af7ed3871c7be31ad624ba239bc7", size = 342174, upload-time = "2026-06-30T07:14:54.821Z" }, + { url = "https://files.pythonhosted.org/packages/4d/dc/323d08583c0832911768663d1944f0107fcd4088704858d84b5e06d105a0/rpds_py-2026.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:db08f45aecde626498fb3df07bcf6d2ec040af42e859a4f5040d79c200342911", size = 345513, upload-time = "2026-06-30T07:14:56.515Z" }, + { url = "https://files.pythonhosted.org/packages/0b/2a/e31989834d18d2f26ec1d2774c5b1eb3331df4ea8ada525175294c94b48a/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc992ab27b15f852c76755eb2ab7dce86585ddadba6fa5946e58556088845b4", size = 373783, upload-time = "2026-06-30T07:14:57.736Z" }, + { url = "https://files.pythonhosted.org/packages/87/fe/e80107ee3639585c9941c17d6a42cd65325022f656c023191fce78c324c8/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f88d653e7b3b779d71ae7454e20dcc9b6bae903f33c269db9f2be41bda3f261", size = 378316, upload-time = "2026-06-30T07:14:59.077Z" }, + { url = "https://files.pythonhosted.org/packages/22/6f/81e3adf81acfb6fa694de2a6e4e7d8863121e3e0799e0a7725e6cf5679c4/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e52655eaf81e32593abedaa4bfe33170c8cfedf3365ed9be6e11e07f148f0278", size = 499423, upload-time = "2026-06-30T07:15:00.488Z" }, + { url = "https://files.pythonhosted.org/packages/2d/9a/41263969df0ce3d9af2a96d5005a288200af1989aed3354bfceb5fc0b21f/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dfcc8b909769d19db55c7cc9541eb64b9b774b1057ffffb4f1048070475bb9f9", size = 386077, upload-time = "2026-06-30T07:15:01.911Z" }, + { url = "https://files.pythonhosted.org/packages/5e/19/7e98f468bd50346faff5b10e5297374b443bfdddacc8e9fbc65984539597/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c1255b302953c86a486b81d330d5ee1d5bd937691ce271b6be0ef0e299eaab7", size = 371315, upload-time = "2026-06-30T07:15:03.317Z" }, + { url = "https://files.pythonhosted.org/packages/99/3c/2b973b4d371906a134b03decfea7f5d9835a2c6d263454392e15b64b5b18/rpds_py-2026.6.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:8d2294a31386bfa251d8c8a39472beee17db67d4f1a6eabea665d35c9a4461c3", size = 383502, upload-time = "2026-06-30T07:15:04.627Z" }, + { url = "https://files.pythonhosted.org/packages/98/2a/12e2799500af0a307bca76b63361c51f9fe479223561489c29eea1f2ee41/rpds_py-2026.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8f23ead891a3b762f35ab3b04623da7056545b48aa60d59957e6789914545da", size = 402673, upload-time = "2026-06-30T07:15:05.856Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e3/21e5872d165fe08be4f229e3d5ee9d90019c0bf0e5538de60dbd54009450/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:421aba32367055614287a4292b6a17f1939c9452299f7a0209c117e990b646d4", size = 549964, upload-time = "2026-06-30T07:15:07.159Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d0/5ee0fe36844297de8123bee27bc12078c1a7416ad9f1b8a8ca18d6b0c0ac/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1e5822dfc2f0d4ab7e745eaa6d85945069329beeccef965af3f3bb26058fcab6", size = 615446, upload-time = "2026-06-30T07:15:08.531Z" }, + { url = "https://files.pythonhosted.org/packages/b1/80/1ea5873cb683f2fbe5f21b23ea1f6d179ead19f3c5b249b7eb5dca568ef2/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83e35b57523816c8613fd0776b40cd8bb9f596b37ddd2692eb4a6bb5ab2f8c93", size = 576975, upload-time = "2026-06-30T07:15:09.97Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e1/90ef639217a5ddb15b7f4f61b1c33911fd044ad03c311bafdd2bcab85582/rpds_py-2026.6.3-cp311-cp311-win32.whl", hash = "sha256:de3eceba0b683bcbb1ab93da016d0270df1f9ae7be716b40214c5dafac6ea45a", size = 204453, upload-time = "2026-06-30T07:15:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/f2/b7/b7a1695d7af36f521fb11e80d6d3adbd744f73b921859bd3c2a2c0dc706f/rpds_py-2026.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:2c54a076ca4d370980ab57bc0e31df57bbe8d41340436a90ef8b1219a3cbb127", size = 223219, upload-time = "2026-06-30T07:15:12.476Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a2/145afacf796e4506062825941176ad9445c2dcf2b3b6a1f13d3030a15e19/rpds_py-2026.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:168c733a7112e071bb7a66460e667edfcff06c017a3c523f7a8a8e08d0140804", size = 219137, upload-time = "2026-06-30T07:15:13.631Z" }, + { url = "https://files.pythonhosted.org/packages/5c/be/2e8974163072e7bab7df1a5acd54c4498e75e35d6d18b864d3a9d5dadc92/rpds_py-2026.6.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0811d33247c3d6128a3001d763f2aa056bb3425204335400ac54f89eec3a0d0", size = 343691, upload-time = "2026-06-30T07:15:14.96Z" }, + { url = "https://files.pythonhosted.org/packages/a4/73/319dfa745dd668efe89309141ded489126461fcecd2b8f3a3cda185129b6/rpds_py-2026.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:538949e262e46caa31ac01bdb3c1e8f642622922cacbabbae6a8445d9dc33eaf", size = 338542, upload-time = "2026-06-30T07:15:16.267Z" }, + { url = "https://files.pythonhosted.org/packages/21/63/4239893be1c4d09b709b1a8f6be4188f0870084ff547f46606b8a75f1b03/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55927d532399c2c646100ff7feb48eaa940ad70f42cd68e1328f3ded9f81ca24", size = 368180, upload-time = "2026-06-30T07:15:17.62Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ca/9c5de382225234ceb37b1844ebdb140db12b2a278bb9efe2fcd19f6c82ce/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f56f1695bc5c0871cbc33dc0130fcf503aab0c57dcc5a6700a4f49eba4f2652e", size = 375067, upload-time = "2026-06-30T07:15:18.952Z" }, + { url = "https://files.pythonhosted.org/packages/87/dc/863f69d1bf04ade34b7fe0d59b9fdf6f0135fe2d7cbca74f1d665589559d/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:270b293dae9058fc9fcedab50f13cebf46fb8ed1d1d54e0521a9da5d6b211975", size = 490509, upload-time = "2026-06-30T07:15:20.434Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ef/eac16a12048b45ec7c7fa94f2be3438a5f26bf9cc8580b18a1cfd609b7f6/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:127565fead0a10943b282957bd5447804ff3160ad79f2ad2635e6d249e380680", size = 382754, upload-time = "2026-06-30T07:15:21.831Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/d2f3f532616be4d06c316ef119683e832bd3d41e112bf3a88f4151c95b17/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6", size = 366189, upload-time = "2026-06-30T07:15:23.371Z" }, + { url = "https://files.pythonhosted.org/packages/e3/29/41a7b0e98a4b44cd676ab7598419623373eb43b20be68c084935c1a8cf88/rpds_py-2026.6.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:58eadac9cd119677b60e1cf8ac4052f35949d71b8a9e5556efccbe82533cf22a", size = 377750, upload-time = "2026-06-30T07:15:24.659Z" }, + { url = "https://files.pythonhosted.org/packages/2e/05/ecda0bec46f9a1565090bcdc941d023f6a25aff85fda28f89f8d19878152/rpds_py-2026.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7491ee23305ac3eb59e492b6945881f5cd77a6f731061a3f25b77fd40f9e99a4", size = 395576, upload-time = "2026-06-30T07:15:25.987Z" }, + { url = "https://files.pythonhosted.org/packages/68/a8/6ed52f03ee6cb854ce78785cc9a9a672eb880e83fd7224d471f667d151f1/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2c99f7e8ccb3dd6e3e4bfeac657a7b208c9bac8075f4b078c02d7404c34107fa", size = 543807, upload-time = "2026-06-30T07:15:27.356Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d6/156c0d3eea27ba09b92562ba2364ba124c0a061b199e17eac637cd25a5e2/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62698275682bf121181861295c9181e789030a2d516071f5b8f3c23c170cd0fc", size = 611187, upload-time = "2026-06-30T07:15:28.931Z" }, + { url = "https://files.pythonhosted.org/packages/f1/31/774212ed989c62f7f310220089f9b0a3fb8f40f5443d1727abd5d9f52bc9/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a214c993455f99a89aaeadc9b21241900037adc9d97203e374d75513c5911822", size = 573030, upload-time = "2026-06-30T07:15:30.553Z" }, + { url = "https://files.pythonhosted.org/packages/c9/50/22f73127a41f1ce4f87fe39aadfb9a126345801c274aa93ae88456249327/rpds_py-2026.6.3-cp312-cp312-win32.whl", hash = "sha256:501f9f04a588d6a09179368c57071301445191767c64e4b52a6aa9871f1ef5ed", size = 202185, upload-time = "2026-06-30T07:15:32.027Z" }, + { url = "https://files.pythonhosted.org/packages/04/3a/f0ee4d4dde9d3b69dedf1b5f74e7a40017046d55052d173e418c6a94f960/rpds_py-2026.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:2c958bf94822e9290a40aaf2a822d4bc5c88099093e3948ad6c571eca9272e5f", size = 220394, upload-time = "2026-06-30T07:15:33.359Z" }, + { url = "https://files.pythonhosted.org/packages/f3/83/3382fe37f809b59f02aac04dbc4e765b480b46ee0227ed516e3bdc4d3dfc/rpds_py-2026.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:22bffe6042b9bcb0822bcd1955ec00e245daf17b4344e4ed8e9551b976b63e96", size = 215753, upload-time = "2026-06-30T07:15:34.778Z" }, + { url = "https://files.pythonhosted.org/packages/a4/9e/b818ee580026ec578138e961027a68820c40afeb1ec8f6819b54fb99e196/rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223", size = 343012, upload-time = "2026-06-30T07:15:36.005Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6b/686d9dc4359a8f163cfbbf89ee0b4e586431de22fe8248edb63a8cf50d49/rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f", size = 338203, upload-time = "2026-06-30T07:15:37.462Z" }, + { url = "https://files.pythonhosted.org/packages/9e/9b/069aa329940f8207615e091f5eedbbd40e1e15eac68a0790fd05ccdf796c/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f", size = 367984, upload-time = "2026-06-30T07:15:39.008Z" }, + { url = "https://files.pythonhosted.org/packages/14/db/34c203e4becff3703e4d3bc121842c00b8689197f398161203a880052f4e/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7", size = 374815, upload-time = "2026-06-30T07:15:40.253Z" }, + { url = "https://files.pythonhosted.org/packages/ee/7d/8071067d2cc453d916ad836e828c943f575e8a44612537759002a1e07381/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6", size = 490545, upload-time = "2026-06-30T07:15:41.729Z" }, + { url = "https://files.pythonhosted.org/packages/a3/42/da06c5aa8f0484ff07f270787434204d9f4535e2f8c3b51ed402267e63c3/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af", size = 382828, upload-time = "2026-06-30T07:15:43.327Z" }, + { url = "https://files.pythonhosted.org/packages/57/d7/fe978efc2ae50abe48eb7464668ea99f53c010c60aeebb7b35ad27f23661/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf", size = 365678, upload-time = "2026-06-30T07:15:44.992Z" }, + { url = "https://files.pythonhosted.org/packages/69/9d/1d8922e1990b2a6eb532b6ff53d3e73d2b3bbffc84116c75826bee73dfc6/rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885", size = 377811, upload-time = "2026-06-30T07:15:46.523Z" }, + { url = "https://files.pythonhosted.org/packages/b1/3d/198dceafb4fb034a6a47347e1b0735d34e0bd4a50be4e898d408ee66cb14/rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4", size = 395382, upload-time = "2026-06-30T07:15:47.955Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f1/13968e49655d40b6b19d8b9140296bbc6f1d86b3f0f6c346cf9f1adddf4b/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7", size = 543832, upload-time = "2026-06-30T07:15:49.33Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ab/289bcb1b90bd3e40a2900c561fa0e2087345ecbb094f0b870f2345142b7c/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d", size = 611011, upload-time = "2026-06-30T07:15:50.847Z" }, + { url = "https://files.pythonhosted.org/packages/1e/16/5043105e679436ccfbc8e5e0dd2d663ed18a8b8113515fd06a5e5d77c83e/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97", size = 572431, upload-time = "2026-06-30T07:15:52.394Z" }, + { url = "https://files.pythonhosted.org/packages/85/ed/adab103321c0a6565d5ae1c2998349bc3ee175b82ccc5ae8fc04cc413075/rpds_py-2026.6.3-cp313-cp313-win32.whl", hash = "sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0", size = 201710, upload-time = "2026-06-30T07:15:53.894Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ed/a03b09668e74e5dabbf2e211f6468e1820c0552f7b0500082da31841bf7b/rpds_py-2026.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80", size = 219454, upload-time = "2026-06-30T07:15:55.25Z" }, + { url = "https://files.pythonhosted.org/packages/27/17/b8642c12930b71bc2b25831f6708ccf0f75abcd11883932ec9ce54ba3a78/rpds_py-2026.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb", size = 215063, upload-time = "2026-06-30T07:15:56.573Z" }, + { url = "https://files.pythonhosted.org/packages/b6/36/7fbe9dcdaf857fb3f63c2a2284b62492d95f5e8334e947e5fb6e7f68c9be/rpds_py-2026.6.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:931908d9fc855d8f74783377822be318edb6dcb19e47169dc038f9a1bf60b06e", size = 344510, upload-time = "2026-06-30T07:15:57.921Z" }, + { url = "https://files.pythonhosted.org/packages/ba/54/f785cc3d3f60839ca57a5af4927a9f347b07b2799c373fc20f7949f87c7e/rpds_py-2026.6.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd", size = 339495, upload-time = "2026-06-30T07:15:59.238Z" }, + { url = "https://files.pythonhosted.org/packages/63/ef/d4cdaf309e6b095b43597103cf8c0b951d6cca2acce68c474f75ec12e0c7/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d", size = 369454, upload-time = "2026-06-30T07:16:01.021Z" }, + { url = "https://files.pythonhosted.org/packages/96/4a/9559a68b7ee15db09d7981212e8c2e219d2a1d6d4faa0391d813c3496a36/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847927daf4cffbd4e90e42bc890069897101edd015f956cb8721b3473372edda", size = 374583, upload-time = "2026-06-30T07:16:02.287Z" }, + { url = "https://files.pythonhosted.org/packages/ef/75/8964aa7d2c6e8ac43eba8eb6e6b0fdda1f46d39f2fc3e6aa9f2cb17f485d/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aca6c1ef08a82bfe327cc156da694660f599923e2e6665b6d81c9c2d0ac9ffc8", size = 492919, upload-time = "2026-06-30T07:16:03.723Z" }, + { url = "https://files.pythonhosted.org/packages/8f/97/6908094ac804115e65aedfd90f1b5fee4eebebd3f6c4cfc5419939267565/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae50181a047c871561212bb97f7932a2d45fb53e947bd9b57ebad85b529cbc53", size = 383725, upload-time = "2026-06-30T07:16:05.305Z" }, + { url = "https://files.pythonhosted.org/packages/d1/9c/0d1fdc2e7aba23e290d603bc494e97bd205bae262ce33c6b32a69768ed5e/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504", size = 367255, upload-time = "2026-06-30T07:16:07.086Z" }, + { url = "https://files.pythonhosted.org/packages/c4/fe/f0209ca4a9ed074bc8acb44dfd0e81c3122e94c9689f5645b7973a866719/rpds_py-2026.6.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e4316bf32babbed84e691e352faf967ce2f0f024174a8643c37c94a1080374fc", size = 379060, upload-time = "2026-06-30T07:16:08.525Z" }, + { url = "https://files.pythonhosted.org/packages/c6/8d/f1cc54c616b9d8897de8738aac148d20afca93f68187475fe194d09a71b9/rpds_py-2026.6.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8c6e5a2f750cc71c3e3b11d71661f21d6f9bc6cebc6564b1466417a1ec03ec77", size = 395960, upload-time = "2026-06-30T07:16:09.989Z" }, + { url = "https://files.pythonhosted.org/packages/fb/04/aafff00f73aeca2945f734f1d483c64ab8f472d0864ab02377fd8e89c3b2/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4470ce197d4090875cf6affbf1f853338387428df97c4fb7b7106317b8214698", size = 545356, upload-time = "2026-06-30T07:16:11.816Z" }, + { url = "https://files.pythonhosted.org/packages/fd/cc/e229663b9e4ddac5a4acbe9085dd80a71af2a5d356b8b39d6bff233f24b0/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea964164cc9afa72d4d9b23cc28dafae93693c0a53e0b42acbff15b22c3f9ddd", size = 612319, upload-time = "2026-06-30T07:16:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7a/8a0e6d3e6cd066af108b71b43122c3fe158dd9eb86acac626593a2582eb1/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:639c8929aa0afe81be836b04de888460d6bed38b9c54cfc18da8f6bfabf5af5d", size = 573508, upload-time = "2026-06-30T07:16:15.23Z" }, + { url = "https://files.pythonhosted.org/packages/87/03/2a69ab618a789cf6cf85c86bb844c62d090e700ab1a2aa676b3741b6c516/rpds_py-2026.6.3-cp314-cp314-win32.whl", hash = "sha256:882076c00c0a608b131187055ddc5ae29f2e7eaf870d6168980420d58528a5c8", size = 202504, upload-time = "2026-06-30T07:16:16.893Z" }, + { url = "https://files.pythonhosted.org/packages/85/62/a3892ba945f4e24c78f352e5de3c7620d8479f73f211406a97263d13c7d2/rpds_py-2026.6.3-cp314-cp314-win_amd64.whl", hash = "sha256:0be972be84cfcaf46c8c6edf690ca0f154ac17babf1f6a955a51579b34ad2dc5", size = 220380, upload-time = "2026-06-30T07:16:18.108Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e7/c2bd44dc831931815ad11ebb5f430b5a0a4d3caa9de837107876c30c3432/rpds_py-2026.6.3-cp314-cp314-win_arm64.whl", hash = "sha256:2a9c6f195058cb45335e8cc3802745c603d716eb96bc9625950c1aac71c0c703", size = 215976, upload-time = "2026-06-30T07:16:19.654Z" }, + { url = "https://files.pythonhosted.org/packages/79/9c/fff7b74bce9a091ec9a012a03f9ff5f69364eaf9451060dfc4486da2ffdd/rpds_py-2026.6.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:f90938e92afda60266da758ee7d363447f7f0138c9559f9e1811629580582d90", size = 346840, upload-time = "2026-06-30T07:16:21.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/77bcb1168b33704908295533d27f10eb811e9e3e193e8993dc99572211d3/rpds_py-2026.6.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec829541c45bca16e61c7ae50c20501f213605beb75d1aba91a6ee37fbbb56a4", size = 340282, upload-time = "2026-06-30T07:16:22.875Z" }, + { url = "https://files.pythonhosted.org/packages/87/3c/7a9081c7c9e645b39efe19e4ffbeccd80add246327cd9b888aecffd72317/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd70d95892096cdb26f15a00c45907b17817577aa8d1c76b2dcc2788391f9e9", size = 370403, upload-time = "2026-06-30T07:16:24.415Z" }, + { url = "https://files.pythonhosted.org/packages/f7/69/af47021eb7dad6ff3396cb001c08f0f3c4d06c20253f75be6421a59fe6b7/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29dfa0533a5d4c94d4dfa1b694fcb56c9c63aad8330ffdd816fd225d0a7a162f", size = 376055, upload-time = "2026-06-30T07:16:26.111Z" }, + { url = "https://files.pythonhosted.org/packages/81/fc/a3bcf517084396a6dd258c592567a3c011ba4557f2fde23dceaf26e74f2e/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af05d726809bff6b141be124d4c7ce998f9c9c7f30edb1f46c07aa103d540b41", size = 494419, upload-time = "2026-06-30T07:16:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/c9/eb/13d529d1788135425c7bf207f8463458ca5d92e43f3f701365b83e9dffc1/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9826217f048f620d9a712672818bf231442c1b35d96b227a07eabd11b4bb6945", size = 384848, upload-time = "2026-06-30T07:16:29.183Z" }, + { url = "https://files.pythonhosted.org/packages/8e/f4/b7ac49f30013aba8f7b9566b1dd07e81de95e708c1374b7bacc5b9bc5c9c/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:536bceea4fa4acf7e1c61da2b5786304367c816c8895be71b8f537c480b0ea1f", size = 371369, upload-time = "2026-06-30T07:16:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/31/86/6260bafa622f788b07ddec0e52d810305c8b9b0b8c27f58a2ab04bf62b4f/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:bc0011654b91cc4fb2ae701bec0a0ba1e552c0714247fa7af6c59e0ccfa3a4e1", size = 379673, upload-time = "2026-06-30T07:16:32.486Z" }, + { url = "https://files.pythonhosted.org/packages/19/c3/03f1ee79a047b48daeca157c89a18509cde22b6b951d642b9b0af1be660a/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:539d75de9e0d536c84ff18dfeb805398e58227001ce09231a26a08b9aed1ee0e", size = 397500, upload-time = "2026-06-30T07:16:34.471Z" }, + { url = "https://files.pythonhosted.org/packages/f0/95/8ed0cd8c377dca12aea498f119fe639fc474d1461545c39d2b5872eb1c0f/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:166cf54d9f44fc6ceb53c7860258dde44a81406646de79f8ed3234fca3b6e538", size = 545978, upload-time = "2026-06-30T07:16:36.45Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f2/0eb57f0eaa83f8fc152a7e03de968ab77e1f00732bebc892b190c6eebde7/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:d34c20167764fbcf927194d532dd7e0c56772f0a5f943fa5ef9e9afbba8fb9db", size = 613350, upload-time = "2026-06-30T07:16:38.213Z" }, + { url = "https://files.pythonhosted.org/packages/5b/de/e0674bdbc3ef7634989b3f854c3f34bc1f587d36e5bfdc5c378d57034619/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea7bb13b7c9a29791f87a0387ba7d3ad3a6d783d827e4d3f27b40a0ff44495e2", size = 576486, upload-time = "2026-06-30T07:16:39.797Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f6/21101359743cd136ada781e8210a85769578422ba460672eea0e29739200/rpds_py-2026.6.3-cp314-cp314t-win32.whl", hash = "sha256:6de4744d05bd1aa1be4ed7ea1189e3979196808008113bbbf899a460966b925e", size = 201068, upload-time = "2026-06-30T07:16:41.316Z" }, + { url = "https://files.pythonhosted.org/packages/a6/b2/9574d4d44f7760c2aa32d92a0a4f41698e33f5b204a0bf5c9758f52c79d5/rpds_py-2026.6.3-cp314-cp314t-win_amd64.whl", hash = "sha256:c7b9a2f8f4d8e90af72571d3d495deebdd7e3c75451f5b41719aee166e940fc2", size = 220600, upload-time = "2026-06-30T07:16:43.091Z" }, + { url = "https://files.pythonhosted.org/packages/08/ae/f23a2697e6ee6340a578b0f136be6483657bef0c6f9497b752bb5c0964bb/rpds_py-2026.6.3-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:e059c5dde6452b44424bd1834557556c226b57781dee1227af23518459722b13", size = 344726, upload-time = "2026-06-30T07:16:44.5Z" }, + { url = "https://files.pythonhosted.org/packages/c3/63/e7b3a1a5358dd32c930a1062d8e15b67fd6e8922e81df9e91706d66ee5c8/rpds_py-2026.6.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2f7c26fbc5acd2522b95d4177fe4710ffd8e9b20529e703ffbf8db4d93903f05", size = 339587, upload-time = "2026-06-30T07:16:46.255Z" }, + { url = "https://files.pythonhosted.org/packages/ec/64/10a85681916ca55fffb91b0a211f84e34297c109243484dd6394660a8a7c/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3086b538543802f84c843911242db20447de00d8752dd0efc936dbcf02218ba", size = 369585, upload-time = "2026-06-30T07:16:48.101Z" }, + { url = "https://files.pythonhosted.org/packages/76/c2/baf95c7c38823e12ba34407c5f5767a89e5cf2233895e56f608167ae9493/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2e5c5ee828d42cb11760761c0af6507927bec42d0ad5458f97c9203b054617", size = 375479, upload-time = "2026-06-30T07:16:49.93Z" }, + { url = "https://files.pythonhosted.org/packages/6a/94/0aad06c72d65101e11d33528d438cda99a39ce0da99466e156158f2541d3/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed0c1e5d10cdc7135537988c74a0188da68e2f3c30813ba3744ab1e42e0480f9", size = 492418, upload-time = "2026-06-30T07:16:51.641Z" }, + { url = "https://files.pythonhosted.org/packages/b5/17/de3f5a479a1f056535d7489819639d8cd591ea6281d700390b43b1abd745/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c2642a7603ec0b16ed77da4555db3b4b472341904873788327c0b0d7b95f1bb", size = 384123, upload-time = "2026-06-30T07:16:53.622Z" }, + { url = "https://files.pythonhosted.org/packages/46/7d/bf09bd1b145bb2671c03e1e6d1ab8651858d90d8c7dfeadd85a37a934fd8/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4320744c1ffdd95a603def63344bfab2d33edeab301c5007e7de9f9f5b3885", size = 367351, upload-time = "2026-06-30T07:16:55.241Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ea/1bb734f314b8be319149ddee80b18bd41372bdcfbdf88d28131c0cd37719/rpds_py-2026.6.3-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:a9f4645593036b81bbdb36b9c8e0ea0d1c3fee968c4d59db0344c14087ef143a", size = 378827, upload-time = "2026-06-30T07:16:56.841Z" }, + { url = "https://files.pythonhosted.org/packages/4b/93/d9611e5b25e26df9a3649813ed66193ace9347a7c7fc4ab7cf70e94851c0/rpds_py-2026.6.3-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e55d236be29255554da47abe5c577637db7c24a02b8b46f0ca9524c855801868", size = 395966, upload-time = "2026-06-30T07:16:58.557Z" }, + { url = "https://files.pythonhosted.org/packages/c3/cb/99d77e16e5534ae1d90629bbe419ba6ee170833a6a85e3aa1cc41726fbbc/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:24e9c5386e16669b674a69c156c8eeefcb578f3b3397b713b08e6d60f3c7b187", size = 545680, upload-time = "2026-06-30T07:17:00.164Z" }, + { url = "https://files.pythonhosted.org/packages/59/15/11a29755f790cef7a2f755e8e14f4f0c33f39489e1893a632a2eee59672b/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c60924535c75f1566b6eb75b5c31a48a43fef04fa2d0d201acbad8a9969c6107", size = 611853, upload-time = "2026-06-30T07:17:01.962Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/0c27547e21644da938fb530f7e1a8148dd24d02db07e7a5f2567a17ce710/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:38a2fea2787428f811719ceb9114cb78964a3138838320c29ac39526c79c16ba", size = 573715, upload-time = "2026-06-30T07:17:03.693Z" }, + { url = "https://files.pythonhosted.org/packages/29/71/4d8fcf700931815594bce892255bbd973b94efaf0fc1932b0590df18d886/rpds_py-2026.6.3-cp315-cp315-win32.whl", hash = "sha256:d483fe17f01ad64b7bf7cc38fcefff1ca9fb83f8c2b2542b68f97ffe0611b369", size = 202864, upload-time = "2026-06-30T07:17:05.746Z" }, + { url = "https://files.pythonhosted.org/packages/eb/62/b577562de0edbb55b2be85ce5fd09c33e386b9b13eee09833af4240fd5c4/rpds_py-2026.6.3-cp315-cp315-win_amd64.whl", hash = "sha256:67e3a721ffc5d8d2210d3671872298c4a84e4b8035cfe42ffd7cde35d772b146", size = 220430, upload-time = "2026-06-30T07:17:07.471Z" }, + { url = "https://files.pythonhosted.org/packages/c8/95/d6d0b2509825141eef60669a5739eec88dbc6a48053d6c92993a5704defe/rpds_py-2026.6.3-cp315-cp315-win_arm64.whl", hash = "sha256:6e84adbcf4bf841aed8116a8264b9f50b4cb3e7bd89b516122e616ac56ca269e", size = 215877, upload-time = "2026-06-30T07:17:09.008Z" }, + { url = "https://files.pythonhosted.org/packages/b7/bf/f3ea278f0afd615c1d0f19cb69043a41526e2bb600c2b536eb192218eb27/rpds_py-2026.6.3-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ae6dd8f10bd17aad820876d24caec9efdafd80a318d16c0a48edb5e136902c6b", size = 346933, upload-time = "2026-06-30T07:17:10.762Z" }, + { url = "https://files.pythonhosted.org/packages/9d/29/9907bdf1c5346763cf10b7f6852aad86652168c259def904cbe0082c5864/rpds_py-2026.6.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:bdbd97738551fca3917c1bd7188bec1920bb520104f28e7e1007f9ceb17b7690", size = 340274, upload-time = "2026-06-30T07:17:12.266Z" }, + { url = "https://files.pythonhosted.org/packages/6f/2c/8e03767b5778ef25cebf74a7a91a2c3806f8eced4c92cb7406bbe060756d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b95977e7211527ab0ba576e286d023389fbeeb32a6b7b771665d333c60e5342", size = 370763, upload-time = "2026-06-30T07:17:14.107Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e1/df2a7e1ba2efd796af26194250b8d42c821b46592311595162af9ef0528d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15fde0e6fb0d88a60d221204873743e5d9f0b7d29165e62cd86d0413ad74ba6", size = 376467, upload-time = "2026-06-30T07:17:15.76Z" }, + { url = "https://files.pythonhosted.org/packages/6b/de/8a0814d1946af29cb068fb259aa8622f856df1d0bab58429448726b537f5/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a136d453475ac0fcbda502ef1e6504bd28d6d904700915d278deeab0d00fe140", size = 496689, upload-time = "2026-06-30T07:17:17.308Z" }, + { url = "https://files.pythonhosted.org/packages/df/f3/f19e0c852ba13694f5a79f3b719331051573cb5693feacf8a88ffffc3a71/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f826877d462181e5eb1c26a0026b8d0cab05d99844ecb6d8bf3627a2ca0c0442", size = 385340, upload-time = "2026-06-30T07:17:18.928Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ae/7ec3a9d2d4351f99e37bcb06b6b6f954512646bfdbf9742e1de727865daf/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79486287de1730dbaff3dbd124d0ca4d2ef7f9d29bf2544f1f93c09b5bcbbd12", size = 372179, upload-time = "2026-06-30T07:17:20.539Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ac/9cee911dff2aaa9a5a8354f6610bf2e6a616de9197c5fff4f54f82585f1e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:808345f53cb952433ca2816f1604ff3515608a81784954f38d4452acfe8e61d5", size = 379993, upload-time = "2026-06-30T07:17:22.212Z" }, + { url = "https://files.pythonhosted.org/packages/83/6b/7c2a07ba88d1e9a936612f7a5d067467ed03d971d5a06f7d309dff044a7e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1967debc37f64f2c4dc90a7f563aec558b471966e12adcac4e1c4240496b6ebf", size = 398909, upload-time = "2026-06-30T07:17:23.66Z" }, + { url = "https://files.pythonhosted.org/packages/97/0b/776ffcb66783637b0031f6d58d6fb55913c8b5abf00aeecd46bf933fb477/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:f0840b5b17057f7fd918b76183a4b5a0635f43e14eb2ce60dce1d4ee4707ea00", size = 546584, upload-time = "2026-06-30T07:17:25.264Z" }, + { url = "https://files.pythonhosted.org/packages/55/33/ba3bc04d7092bd553c9b2b195624992d2cc4f3de1f380b7b93cbee67bd79/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:faa679d19a6696fd54259ad321251ad77a13e70e03dd834daa762a44fb6196ef", size = 614357, upload-time = "2026-06-30T07:17:26.888Z" }, + { url = "https://files.pythonhosted.org/packages/8b/71/14edf065f04630b1a8472f7653cad03f6c478bcf95ea0e6aed55451e33ea/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:23a439f31ccbeff1574e24889128821d1f7917470e830cf6544dced1c662262a", size = 576533, upload-time = "2026-06-30T07:17:28.546Z" }, + { url = "https://files.pythonhosted.org/packages/ba/76/65002b08596c389105720a8c0d22298b8dc25a4baf89b2ce431343c8b1de/rpds_py-2026.6.3-cp315-cp315t-win32.whl", hash = "sha256:913ca42ccad3f8cc6e292b587ae8ae49c8c823e5dce51a736252fc7c7cdfa577", size = 201204, upload-time = "2026-06-30T07:17:30.193Z" }, + { url = "https://files.pythonhosted.org/packages/8c/97/d855d6b3c322d1f27e26f5241c42016b56cf01377ea8ed348285f54652f0/rpds_py-2026.6.3-cp315-cp315t-win_amd64.whl", hash = "sha256:ae3d4fe8c0b9213624fdce7279d70e3b148b682ca20719ebd193a23ebfa47324", size = 220719, upload-time = "2026-06-30T07:17:31.788Z" }, + { url = "https://files.pythonhosted.org/packages/b4/9c/f0d19ac587fd0e4ab6b72cda355e9c5a6166b01ef7e064e437aef8eb9fef/rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4cf2d36a2357e4d07bb5a4f98801265327b48256867816cfd2ceb001e9754a8f", size = 349791, upload-time = "2026-06-30T07:17:33.315Z" }, + { url = "https://files.pythonhosted.org/packages/38/c7/1d49d204c9fd2ee6c537601dc4c1ba921e03363ca576bfab94a00254ac9a/rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:30c6dc199b24a5e3e81d50da0f00858c5bbdb2617a750395687f4339c5818171", size = 352842, upload-time = "2026-06-30T07:17:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e5/c0b5dc93cd0d4c06ce1f438907649514e2ea077bcd911e3154a51e96c38e/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9891e594296ab9dada6551c8e7b387b2721f27a67eecd528412e8906247a7b90", size = 382094, upload-time = "2026-06-30T07:17:36.514Z" }, + { url = "https://files.pythonhosted.org/packages/0d/54/ec0e907b4ca8d541112db352409bd15f871c9b243e0c92c9b5a46ae96f01/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5c2dc92304aa48a4a60443b548bb12f12e119d4b72f314015e67b9e1be97fca", size = 388662, upload-time = "2026-06-30T07:17:38.235Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f4/921c22a4fd0f1c1ac13a3996ffbf0aa67951e2c8ad0d1d9574938a2932e8/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:127e08c0642d880cf32ca47ec2a4a77b901f7e2dd1ad9762adb13955d72ffcc9", size = 504896, upload-time = "2026-06-30T07:17:39.689Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1b/a114b972cefa1ab1cdb3c7bb177cd3844a12826c507c722d3a73516dbbaf/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bb68f03f395eb793220b45c097bd4d8c32944393da0fad8b999efac0868fc8c", size = 391545, upload-time = "2026-06-30T07:17:41.336Z" }, + { url = "https://files.pythonhosted.org/packages/4e/98/af9b3db77d47fcbe6c8c1f36e2c2147ec70292819e99c325f871584a1c11/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3450b693fde92133e9f51060568a4c31fcca76d5e53bbd611e689ca446517e9", size = 380059, upload-time = "2026-06-30T07:17:42.857Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ba/0efd8668b97c1d26a61566386c636a7a7a09829e474fdf807caa15a2c844/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:5e8d07bddee435a2ff6f1920e18feff28d0bc4533e42f4bf6927fbd073312c41", size = 393235, upload-time = "2026-06-30T07:17:44.637Z" }, + { url = "https://files.pythonhosted.org/packages/62/90/8c139ee9690f73b0829f32647de6f40d826f8f443af6fa72644f96351aac/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3a83ae6c67b7676b9878378547ca8e93ed77a580037bcbcd1d32f739e1e6089c", size = 413008, upload-time = "2026-06-30T07:17:46.225Z" }, + { url = "https://files.pythonhosted.org/packages/9c/97/0043896fdd7828ce09a1d9a8b06433714d0960fc4ff3fc4aa72b666b764e/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2bfd04c19ddbd6640de0b51894d764bd2758854d5b75bd102d2ef10cb9c293a9", size = 558118, upload-time = "2026-06-30T07:17:47.759Z" }, + { url = "https://files.pythonhosted.org/packages/f6/40/02355f0e134f783a8f9814c4680a1bd311d37671577a5964ea838573ff37/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:ca6546b66be9dc4738b1b043d5ebd5488c66c578c5ff0fd0e8065313fe3afb76", size = 623138, upload-time = "2026-06-30T07:17:49.355Z" }, + { url = "https://files.pythonhosted.org/packages/10/85/48f0abdcef5cce4e034c7a5b0ceeceba0b01bf0d942824f4bb720afe2dec/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8e65860d238379ed982fd9ba690579b5e95af2f4840f99c772816dbe573cb826", size = 586486, upload-time = "2026-06-30T07:17:51.141Z" }, +] + [[package]] name = "tibs" version = "0.5.7" @@ -1264,11 +1403,11 @@ wheels = [ [[package]] name = "typing-extensions" -version = "4.15.0" +version = "4.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, ] [[package]] From 6d984a090094c8634aba96117e44f0cb8f792093 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 26 Jul 2026 15:02:51 +0200 Subject: [PATCH 24/56] CI: download hold binary --- .github/workflows/ci.yml | 7 + Cargo.lock | 346 +++++++++++++++++---------------------- 2 files changed, 159 insertions(+), 194 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b15911b..cac47ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -153,6 +153,13 @@ jobs: uv run python -m grpc_tools.protoc --proto_path="proto" --python_out="tests" --grpc_python_out="tests" proto/*.proto fi + url=$(curl -f --retry 3 --retry-delay 3 -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/repos/BoltzExchange/hold/releases/latest \ + | jq '.assets[] | select(.name | endswith("linux-amd64.tar.gz")) | .browser_download_url' \ + | tr -d '\"') + wget $url + tar -xvf ${url##*/} -C tests + mv "tests/build/hold-linux-amd64" "tests/hold" + cargo build cargo test fi diff --git a/Cargo.lock b/Cargo.lock index c693f75..7eb17b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,25 +34,25 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.104" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arrayvec" -version = "0.7.8" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" +checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" [[package]] name = "async-trait" -version = "0.1.91" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn", ] [[package]] @@ -95,9 +95,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "base58ck" -version = "0.1.101" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "365c0acd5b2e8dd0111a46c4faea83fb3cfb6e39a49a7c73a06e090db7b2eff0" +checksum = "8f1cba749a07c1efb1f4d87518f39cea4aec25d0991fb97e80459c057238f0d2" dependencies = [ "bitcoin_hashes", ] @@ -127,73 +127,52 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.32.102" +version = "0.32.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0ce8bd5baaa0d303a19915a6d93afed161f528654e42da2a7a97d05c59499a" +checksum = "a1a121ccf1177a09084bd6ce97c52119919aac08ea3649967958eae41beceebf" dependencies = [ "base58ck", "bech32", "bitcoin-io", "bitcoin-units", "bitcoin_hashes", - "hex-conservative 0.2.2", + "hex-conservative", "hex_lit", "secp256k1", "serde", ] -[[package]] -name = "bitcoin-consensus-encoding" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "207311705279250ba465076a1bac4b1ac982855fff73fc5f67e22158ac58cdc9" -dependencies = [ - "bitcoin-internals", - "hex-conservative 1.2.0", - "serde", -] - -[[package]] -name = "bitcoin-internals" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" - [[package]] name = "bitcoin-io" -version = "0.1.101" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78" -dependencies = [ - "bitcoin-consensus-encoding", -] +checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" [[package]] name = "bitcoin-units" -version = "0.1.101" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cb95693f371d089a4b5b6fc41c6f3ea6e01ee8c15388335dfac8ea685173b51" +checksum = "c202276cab20ab02cf6cc9d4df0d4284f7c784031619d3842236de8bc2bd5c6c" dependencies = [ - "bitcoin-consensus-encoding", "serde", ] [[package]] name = "bitcoin_hashes" -version = "0.14.101" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" +checksum = "4ed83caece3afc59919481b33b472e1432d1abc4641ed9100be142ef5110b406" dependencies = [ "bitcoin-io", - "hex-conservative 0.2.2", + "hex-conservative", "serde", ] [[package]] name = "bitflags" -version = "2.13.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "block-buffer" @@ -233,9 +212,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.12.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" [[package]] name = "cbc" @@ -248,9 +227,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.4.0" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", "shlex", @@ -403,14 +382,14 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] name = "either" -version = "1.17.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "equivalent" @@ -440,9 +419,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.5.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "find-msvc-tools" @@ -479,9 +458,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.33" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -494,9 +473,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.33" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -504,15 +483,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.33" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.33" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -521,38 +500,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.33" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" -version = "0.3.33" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] name = "futures-sink" -version = "0.3.33" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.33" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.33" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -696,15 +675,6 @@ dependencies = [ "arrayvec", ] -[[package]] -name = "hex-conservative" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" -dependencies = [ - "arrayvec", -] - [[package]] name = "hex_lit" version = "0.1.1" @@ -723,9 +693,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.1.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", @@ -733,9 +703,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.4" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", @@ -758,9 +728,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.11.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" dependencies = [ "atomic-waker", "bytes", @@ -950,9 +920,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" dependencies = [ "cfg-if", "futures-util", @@ -967,9 +937,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.189" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "linux-raw-sys" @@ -1024,15 +994,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.3" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "mio" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "wasi", @@ -1203,7 +1173,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -1248,14 +1218,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.119", + "syn", ] [[package]] name = "proc-macro2" -version = "1.0.107" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -1287,7 +1257,7 @@ dependencies = [ "pulldown-cmark", "pulldown-cmark-to-cmark", "regex", - "syn 2.0.119", + "syn", "tempfile", ] @@ -1301,7 +1271,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -1399,9 +1369,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.47" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -1420,9 +1390,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.9.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha", "rand_core 0.9.5", @@ -1474,9 +1444,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.13.1" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -1486,9 +1456,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.16" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1530,9 +1500,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.42" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "log", "once_cell", @@ -1545,9 +1515,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.1" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "zeroize", ] @@ -1565,9 +1535,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.23" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "scopeguard" @@ -1597,9 +1567,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.229" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -1607,29 +1577,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.229" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.229" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn", ] [[package]] name = "serde_json" -version = "1.0.151" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -1640,9 +1610,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.7" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -1678,9 +1648,9 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "socket2" -version = "0.6.5" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", "windows-sys 0.61.2", @@ -1700,20 +1670,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.119" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -1734,7 +1693,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -1761,11 +1720,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.19" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.19", + "thiserror-impl 2.0.18", ] [[package]] @@ -1776,25 +1735,25 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] name = "thiserror-impl" -version = "2.0.19" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn", ] [[package]] name = "thread_local" -version = "1.1.10" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", ] @@ -1811,9 +1770,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.12.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" dependencies = [ "tinyvec_macros", ] @@ -1826,9 +1785,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.53.1" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -1850,13 +1809,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -1883,9 +1842,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.19" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" dependencies = [ "futures-core", "pin-project-lite", @@ -1911,14 +1870,13 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.19" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", "futures-sink", - "libc", "pin-project-lite", "tokio", ] @@ -1961,7 +1919,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -1986,7 +1944,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.119", + "syn", "tempfile", "tonic-build", ] @@ -2042,7 +2000,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -2101,11 +2059,11 @@ dependencies = [ "http", "httparse", "log", - "rand 0.9.5", + "rand 0.9.4", "rustls", "rustls-pki-types", "sha1", - "thiserror 2.0.19", + "thiserror 2.0.18", "utf-8", ] @@ -2185,9 +2143,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.24.0" +version = "1.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -2232,9 +2190,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" dependencies = [ "cfg-if", "once_cell", @@ -2245,9 +2203,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.76" +version = "0.4.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" dependencies = [ "js-sys", "wasm-bindgen", @@ -2255,9 +2213,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2265,31 +2223,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.119", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.103" +version = "0.3.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" dependencies = [ "js-sys", "wasm-bindgen", @@ -2301,14 +2259,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.9", + "webpki-roots 1.0.8", ] [[package]] name = "webpki-roots" -version = "1.0.9" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" dependencies = [ "rustls-pki-types", ] @@ -2432,28 +2390,28 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.55" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.55" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -2473,7 +2431,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", "synstructure", ] @@ -2513,11 +2471,11 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] name = "zmij" -version = "1.0.23" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" From edd95056abc975e39e553afffb5baaf2e8eae196 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sat, 8 Aug 2026 12:55:49 +0200 Subject: [PATCH 25/56] update for nostr 0.45 release version --- Cargo.lock | 492 +++++++++++++++++++++++---------------- Cargo.toml | 4 +- src/nwc.rs | 15 +- src/nwc_hold.rs | 12 +- src/nwc_lookups.rs | 2 +- src/nwc_notifications.rs | 7 +- src/rpc.rs | 6 +- src/structs.rs | 14 +- 8 files changed, 323 insertions(+), 229 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7eb17b6..20ea17f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,41 +25,41 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "arrayvec" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "async-utility" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34a3b57207a7a1007832416c3e4862378c8451b4e8e093e436f48c2d3d2c151" +checksum = "188f83b9a198af8c336e505611edb00d6d2ac5c694241c5a4f9a12316938cfe9" dependencies = [ "futures-util", "gloo-timers", @@ -69,9 +69,9 @@ dependencies = [ [[package]] name = "async-wsocket" -version = "0.15.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c69f24cf7a798027c6b0801de5bfd05d01fe343e87a8329f7c8b46a6d3bc4a79" +checksum = "2c713e1f14c7b82e32ea159af1c6e2f070cfadbdf23fb2512acce9af0a26f1a2" dependencies = [ "futures", "futures-util", @@ -95,11 +95,11 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "base58ck" -version = "0.1.1" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1cba749a07c1efb1f4d87518f39cea4aec25d0991fb97e80459c057238f0d2" +checksum = "365c0acd5b2e8dd0111a46c4faea83fb3cfb6e39a49a7c73a06e090db7b2eff0" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.101", ] [[package]] @@ -114,65 +114,104 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" +[[package]] +name = "bech32" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbd3e1070bbdf4cd88a75264e18e8a26f7cb5c6949eadf0ceb85fb159cf08f8" + [[package]] name = "bip39" version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.101", "serde", "unicode-normalization", ] [[package]] name = "bitcoin" -version = "0.32.10" +version = "0.32.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a121ccf1177a09084bd6ce97c52119919aac08ea3649967958eae41beceebf" +checksum = "bb0ce8bd5baaa0d303a19915a6d93afed161f528654e42da2a7a97d05c59499a" dependencies = [ "base58ck", - "bech32", + "bech32 0.11.1", "bitcoin-io", "bitcoin-units", - "bitcoin_hashes", - "hex-conservative", + "bitcoin_hashes 0.14.101", + "hex-conservative 0.2.2", "hex_lit", - "secp256k1", + "secp256k1 0.29.1", "serde", ] [[package]] -name = "bitcoin-io" -version = "0.1.4" +name = "bitcoin-consensus-encoding" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" +checksum = "207311705279250ba465076a1bac4b1ac982855fff73fc5f67e22158ac58cdc9" +dependencies = [ + "bitcoin-internals", + "hex-conservative 1.2.0", + "serde", +] + +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + +[[package]] +name = "bitcoin-io" +version = "0.1.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78" +dependencies = [ + "bitcoin-consensus-encoding", +] [[package]] name = "bitcoin-units" -version = "0.1.4" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c202276cab20ab02cf6cc9d4df0d4284f7c784031619d3842236de8bc2bd5c6c" +checksum = "9cb95693f371d089a4b5b6fc41c6f3ea6e01ee8c15388335dfac8ea685173b51" dependencies = [ + "bitcoin-consensus-encoding", "serde", ] [[package]] name = "bitcoin_hashes" -version = "0.14.2" +version = "0.14.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ed83caece3afc59919481b33b472e1432d1abc4641ed9100be142ef5110b406" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" dependencies = [ "bitcoin-io", - "hex-conservative", + "hex-conservative 0.2.2", + "serde", +] + +[[package]] +name = "bitcoin_hashes" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5304e53726dbe5f93141535e102ed97b5bf4714fbecefdda8f9fb98d7fdaff0e" +dependencies = [ + "bitcoin-consensus-encoding", + "bitcoin-internals", + "hex-conservative 1.2.0", "serde", ] [[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" [[package]] name = "block-buffer" @@ -192,12 +231,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "btreecap" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6160c957d8aa33d0a8ba1dbab98e3cb57023ad9374c501441e88559f99e6c4c9" - [[package]] name = "bumpalo" version = "3.20.3" @@ -212,9 +245,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[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 = "cbc" @@ -227,9 +260,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.65" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e" dependencies = [ "find-msvc-tools", "shlex", @@ -360,9 +393,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" [[package]] name = "digest" @@ -376,20 +409,20 @@ 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", "quote", - "syn", + "syn 3.0.3", ] [[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 = "equivalent" @@ -419,15 +452,15 @@ dependencies = [ [[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 = "find-msvc-tools" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" [[package]] name = "fixedbitset" @@ -458,9 +491,9 @@ dependencies = [ [[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", @@ -473,9 +506,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", @@ -483,15 +516,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", @@ -500,38 +533,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", "quote", - "syn", + "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", @@ -675,6 +708,15 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hex-conservative" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex_lit" version = "0.1.1" @@ -683,9 +725,9 @@ checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" [[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", "itoa", @@ -693,9 +735,9 @@ dependencies = [ [[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", "http", @@ -703,9 +745,9 @@ dependencies = [ [[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", "futures-core", @@ -728,9 +770,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[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", @@ -920,9 +962,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.102" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", @@ -937,9 +979,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[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 = "linux-raw-sys" @@ -979,9 +1021,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.18.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6180140927ee907000b0aa540091f6ea512ead4447c92b8fc35bc72788a5a6" +checksum = "5d2f2f9b4ba7e6b24d95e7e899329d35be83bcded72c8540cdd5368932d1d90a" [[package]] name = "matchers" @@ -994,15 +1036,15 @@ 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 = "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", "wasi", @@ -1023,45 +1065,45 @@ checksum = "f0efe882e02d206d8d279c20eb40e03baf7cb5136a1476dc084a324fbc3ec42d" [[package]] name = "nostr" -version = "0.45.0-alpha.6" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e90ba68fc04b87254ef574e10aef1fe92d44839043f42e37a7c4c4a1e7e4c82" +checksum = "5dde8c76076d334409d86c2e1db3e97abe5deb8cb92744f939cbc1fa45bd69e7" dependencies = [ "aes", "base64", - "bech32", + "bech32 0.12.0", "bip39", - "bitcoin_hashes", + "bitcoin_hashes 1.2.0", "cbc", "chacha20", "chacha20poly1305", "faster-hex", "opaquerr", "rand 0.10.2", - "secp256k1", + "secp256k1 0.30.0", "serde", "serde_json", "unicode-normalization", "universal-time", "url", + "zeroize", ] [[package]] name = "nostr-database" -version = "0.45.0-alpha.6" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c37167c3f289fea43e82f8bb24bb2006a2fb803abaaa908b25f1d3ca0b2bcc" +checksum = "4b1fdb9fcba732e32719662afad1b267e50322dbe89e506017ec13f24361bddf" dependencies = [ - "btreecap", "nostr", "opaquerr", ] [[package]] name = "nostr-gossip" -version = "0.45.0-alpha.6" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb24a1f82e3fe9e3e9b4549e916ec168d4c21c2e55c61fa6be9f98218872cd37" +checksum = "fa07539e52a71cb91fe0d693facaa298f03fcf9edcd66a521094e18e286e2336" dependencies = [ "nostr", "opaquerr", @@ -1069,9 +1111,9 @@ dependencies = [ [[package]] name = "nostr-sdk" -version = "0.45.0-alpha.6" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b197627792d5f02ab257c1bff30d4475e0ebc85229a82515b05d6717c6a4fbc" +checksum = "26c86342f367bd9b173ec4a697e936e3a82d6dad5b4aa06c0d35d9b4f88a8e72" dependencies = [ "async-utility", "async-wsocket", @@ -1083,6 +1125,7 @@ dependencies = [ "nostr-database", "nostr-gossip", "opaquerr", + "rand 0.10.2", "tokio", "tokio-stream", "tracing", @@ -1112,9 +1155,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "opaquerr" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a7b7f0accc9fdd8e514f122a697adf824d55de481ca26c31ff8f9a8e813c5" +checksum = "4f933a4265d5cdad61d19bbdfc972ea5726d56cd8d3d57b8f2d3c365dd42bee9" [[package]] name = "parking_lot" @@ -1173,7 +1216,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1218,14 +1261,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.119", ] [[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", ] @@ -1257,7 +1300,7 @@ dependencies = [ "pulldown-cmark", "pulldown-cmark-to-cmark", "regex", - "syn", + "syn 2.0.119", "tempfile", ] @@ -1271,7 +1314,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1369,9 +1412,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -1390,11 +1433,22 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.9.4" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" dependencies = [ - "rand_chacha", + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha 0.9.0", "rand_core 0.9.5", ] @@ -1408,6 +1462,16 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -1418,6 +1482,15 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + [[package]] name = "rand_core" version = "0.9.5" @@ -1444,9 +1517,9 @@ dependencies = [ [[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", @@ -1456,9 +1529,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -1500,9 +1573,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.40" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "log", "once_cell", @@ -1515,9 +1588,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] @@ -1535,9 +1608,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 = "scopeguard" @@ -1551,11 +1624,22 @@ version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.14.101", "secp256k1-sys", "serde", ] +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes 0.14.101", + "rand 0.8.7", + "secp256k1-sys", +] + [[package]] name = "secp256k1-sys" version = "0.10.1" @@ -1567,9 +1651,9 @@ dependencies = [ [[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", @@ -1577,29 +1661,29 @@ 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", "quote", - "syn", + "syn 3.0.3", ] [[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", @@ -1610,9 +1694,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", "cpufeatures", @@ -1648,9 +1732,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", @@ -1670,9 +1754,20 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.118" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" dependencies = [ "proc-macro2", "quote", @@ -1693,7 +1788,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1720,11 +1815,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.20", ] [[package]] @@ -1735,25 +1830,25 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[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", ] @@ -1770,9 +1865,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", ] @@ -1785,9 +1880,9 @@ 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", "libc", @@ -1809,13 +1904,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", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -1842,9 +1937,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" dependencies = [ "futures-core", "pin-project-lite", @@ -1870,13 +1965,14 @@ 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", "futures-core", "futures-sink", + "libc", "pin-project-lite", "tokio", ] @@ -1919,7 +2015,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1944,7 +2040,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn", + "syn 2.0.119", "tempfile", "tonic-build", ] @@ -2000,7 +2096,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2059,11 +2155,11 @@ dependencies = [ "http", "httparse", "log", - "rand 0.9.4", + "rand 0.9.5", "rustls", "rustls-pki-types", "sha1", - "thiserror 2.0.18", + "thiserror 2.0.20", "utf-8", ] @@ -2106,9 +2202,9 @@ dependencies = [ [[package]] name = "universal-time" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f942cacf37a673350e8f252dff4691fb8e525a5fff6e1f2c4b6c2f3b8349248d" +checksum = "47a939edecc3c5a7b83c02e5f6b3c31d2bc69eabcc9a87ab12c6d37ee6dbc856" [[package]] name = "untrusted" @@ -2143,9 +2239,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.23.3" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -2190,9 +2286,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.125" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -2203,9 +2299,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.75" +version = "0.4.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" dependencies = [ "js-sys", "wasm-bindgen", @@ -2213,9 +2309,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.125" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2223,31 +2319,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.125" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.125" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.102" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" dependencies = [ "js-sys", "wasm-bindgen", @@ -2259,14 +2355,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.8", + "webpki-roots 1.0.9", ] [[package]] name = "webpki-roots" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" dependencies = [ "rustls-pki-types", ] @@ -2390,28 +2486,28 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.52" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.52" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -2431,7 +2527,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] @@ -2471,11 +2567,11 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[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" diff --git a/Cargo.toml b/Cargo.toml index 484d61b..42419e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,8 +24,8 @@ parking_lot = "0.12" # "nip04", # "nip44", # ] } -nostr = { version = "0.45.0-alpha.4", features = ["nip47", "nip04", "nip44"] } -nostr-sdk = { version = "0.45.0-alpha.4" } +nostr = { version = "0.45.1", features = ["nip47", "nip04", "nip44"] } +nostr-sdk = { version = "0.45.1" } futures = "0.3" diff --git a/src/nwc.rs b/src/nwc.rs index c381f94..68a1597 100644 --- a/src/nwc.rs +++ b/src/nwc.rs @@ -4,18 +4,11 @@ use anyhow::anyhow; use cln_plugin::Plugin; use futures::StreamExt; use nostr::{ - Event, - EventBuilder, - EventId, - Filter, - Keys, - Kind, - PublicKey, - SecretKey, - Tag, - Timestamp, - event::FinalizeEventAsync, + event::{Event, EventBuilder, EventId, FinalizeEventAsync, Kind, Tag}, + filter::Filter, + key::{Keys, PublicKey, SecretKey}, nips::{nip04, nip44, nip47}, + types::Timestamp, }; use nostr_sdk::{ client::{self, Client, ClientNotification}, diff --git a/src/nwc_hold.rs b/src/nwc_hold.rs index d3b0d8f..c87ce50 100644 --- a/src/nwc_hold.rs +++ b/src/nwc_hold.rs @@ -2,10 +2,10 @@ use std::str::FromStr; use cln_plugin::Plugin; use cln_rpc::primitives::Sha256; -use nostr::{nips::nip47, Timestamp}; +use nostr::{nips::nip47, types::Timestamp}; use crate::{ - hold::{invoice_request::Description, CancelRequest, InvoiceRequest, SettleRequest}, + hold::{CancelRequest, InvoiceRequest, SettleRequest, invoice_request::Description}, nwc_notifications::holdinvoice_accepted_handler, structs::PluginState, }; @@ -66,7 +66,7 @@ async fn make_hold_invoice( return Err(nip47::NIP47Error { code: nip47::ErrorCode::Other, message: "Could not convert description hash to bytes".to_owned(), - }) + }); } }; Some(Description::Hash(desc_hash_bytes)) @@ -83,7 +83,7 @@ async fn make_hold_invoice( return Err(nip47::NIP47Error { code: nip47::ErrorCode::Other, message: "Invalid payment hash".to_owned(), - }) + }); } }; @@ -164,7 +164,7 @@ async fn cancel_hold_invoice( return Err(nip47::NIP47Error { code: nip47::ErrorCode::Other, message: "Invalid payment hash".to_owned(), - }) + }); } }; @@ -222,7 +222,7 @@ async fn settle_hold_invoice( return Err(nip47::NIP47Error { code: nip47::ErrorCode::Other, message: "Invalid preimage".to_owned(), - }) + }); } }; diff --git a/src/nwc_lookups.rs b/src/nwc_lookups.rs index c75c079..a9dc343 100644 --- a/src/nwc_lookups.rs +++ b/src/nwc_lookups.rs @@ -16,7 +16,7 @@ use cln_rpc::{ }, primitives::Sha256, }; -use nostr::{Timestamp, nips::nip47}; +use nostr::{nips::nip47, types::Timestamp}; use tonic::transport::Channel; use crate::{ diff --git a/src/nwc_notifications.rs b/src/nwc_notifications.rs index 164e246..04accbd 100644 --- a/src/nwc_notifications.rs +++ b/src/nwc_notifications.rs @@ -17,12 +17,9 @@ use cln_rpc::{ primitives::Sha256, }; use nostr::{ - EventBuilder, - Kind, - Tag, - Timestamp, - event::FinalizeEventAsync, + event::{EventBuilder, FinalizeEventAsync, Kind, Tag}, nips::{nip04, nip44, nip47}, + types::Timestamp, }; use crate::{ diff --git a/src/rpc.rs b/src/rpc.rs index 8b46ad0..fea29c7 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -6,7 +6,11 @@ use cln_rpc::model::requests::{ DeldatastoreRequest, ListdatastoreRequest, }; -use nostr::{Keys, SecretKey, Timestamp, nips::nip47::NostrWalletConnectUri}; +use nostr::{ + key::{Keys, SecretKey}, + nips::nip47::NostrWalletConnectUri, + types::Timestamp, +}; use serde_json::json; use crate::{ diff --git a/src/structs.rs b/src/structs.rs index 8f3b86f..ff0ff59 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,8 +1,12 @@ use std::{collections::HashMap, path::PathBuf, str::FromStr, sync::Arc}; use cln_rpc::ClnRpc; -use nostr::{nips::nip47::NostrWalletConnectUri, types::RelayUrl}; -use nostr_sdk::client; +use nostr::{ + key::{Keys, PublicKey}, + nips::nip47::NostrWalletConnectUri, + types::RelayUrl, +}; +use nostr_sdk::client::Client; use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use tokio::sync::oneshot; @@ -33,9 +37,9 @@ impl PluginState { } pub struct WalletService { - pub client: client::Client, - pub client_pubkey: nostr::PublicKey, - pub wallet_secret: nostr::Keys, + pub client: Client, + pub client_pubkey: PublicKey, + pub wallet_secret: Keys, } #[derive(Clone, Debug)] From f5b64586fc778027ef4f076c0ca9603656180bcc Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sat, 8 Aug 2026 14:36:42 +0200 Subject: [PATCH 26/56] tests: update to nostr sdk 0.45 --- tests/pyproject.toml | 2 +- tests/test_cln-nip47.py | 333 +++++++++++++++++++--------------------- tests/uv.lock | 30 ++-- 3 files changed, 176 insertions(+), 189 deletions(-) diff --git a/tests/pyproject.toml b/tests/pyproject.toml index 70483b9..bcde559 100644 --- a/tests/pyproject.toml +++ b/tests/pyproject.toml @@ -10,7 +10,7 @@ dev = [ "pytest-asyncio>=0.23.8,<2", "pytest-xdist>=3.7,<4", "pytest-timeout>=2.4,<3", - "nostr-sdk>=0.44", + "nostr-sdk>=0.45", "pyln-testing>=25.9", "pyln-client>=25.9", "pyln-proto>=25.9", diff --git a/tests/test_cln-nip47.py b/tests/test_cln-nip47.py index 9b46a41..fbf4742 100644 --- a/tests/test_cln-nip47.py +++ b/tests/test_cln-nip47.py @@ -18,7 +18,6 @@ from nostr_sdk import ( Event, EventBuilder, Filter, - HandleNotification, Keys, KeysendTlvRecord, Kind, @@ -28,14 +27,14 @@ from nostr_sdk import ( Method, NostrSdkError, NostrSigner, + NostrWalletConnect, NostrWalletConnectUri, - Nwc, PayInvoiceRequest, PayKeysendRequest, PublicKey, RelayUrl, + ReqTarget, Tag, - TagKind, TransactionType, ) from pyln.testing.fixtures import * @@ -45,23 +44,7 @@ from util import generate_random_label, get_hold, get_plugin # noqa: F401 LOGGER = logging.getLogger(__name__) -class NotificationHandler(HandleNotification): - def __init__(self, events_list, stop_after): - self.events_list = events_list - self.stop_after = stop_after - self._done = asyncio.Event() - - async def handle(self, relay_url, subscription_id, event: Event): - LOGGER.info(f"Received new event from {relay_url}: {event.as_json()}") - self.events_list.append(event) - if len(self.events_list) >= self.stop_after: - self._done.set() - - async def handle_msg(self, relay_url, msg): - _var = None - - -Action = Union[ +Action = Union[ # noqa: UP007 Callable[[], Awaitable[None]], Callable[[], None], Awaitable[None], @@ -78,15 +61,31 @@ async def fetch_event_responses( ) -> tuple[list[Event], Any]: events = [] response_filter = Filter().kind(Kind(event_kind)).pubkey(client_pubkey) + target = ReqTarget.auto([response_filter]) - id = uuid.uuid4().hex - LOGGER.info(f"Subscribing with id {id} to {response_filter}") - await client.subscribe_with_id(id, response_filter) + subscription_id = uuid.uuid4().hex + LOGGER.info(f"Subscribing with id {subscription_id} to {response_filter}") - handler = NotificationHandler(events, stop_after) - task = asyncio.create_task(client.handle_notifications(handler)) + await client.subscribe(target, subscription_id) + + async def collect_events(): + stream = client.notifications() + + while len(events) < stop_after: + notification = await stream.next() + + if notification.is_new_event(): + event = notification.event + relay_url = notification.relay_url + + LOGGER.info(f"Received new event from {relay_url}: {event.as_json()}") + + events.append(event) + + task = asyncio.create_task(collect_events()) await asyncio.sleep(1) + if inspect.iscoroutine(action): action_result = await action elif inspect.iscoroutinefunction(action): @@ -97,10 +96,10 @@ async def fetch_event_responses( raise TypeError("action must be a callable or an awaitable") try: - await asyncio.wait_for(handler._done.wait(), timeout=timeout) + await asyncio.wait_for(task, timeout=timeout) except asyncio.TimeoutError: print( - f"Timeout reached after {timeout} seconds, collected {len(events)} events" + f"Timeout reached after {timeout} seconds, collected {len(events)} events", ) finally: task.cancel() @@ -109,9 +108,10 @@ async def fetch_event_responses( except asyncio.CancelledError: pass - await client.unsubscribe_all() + await client.unsubscribe_all() + assert len(events) == stop_after - return (events, action_result) + return events, action_result async def fetch_info_event( @@ -119,20 +119,17 @@ async def fetch_info_event( uri: NostrWalletConnectUri, ) -> Event: response_filter = Filter().kind(Kind(13194)).author(uri.public_key()) - events = await client.fetch_events( - response_filter, timeout=timedelta(seconds=TIMEOUT) - ) + target = ReqTarget.auto([response_filter]) + events = await client.fetch_events(target, timeout=timedelta(seconds=TIMEOUT)) start_time = datetime.now() - while events.len() < 1 and (datetime.now() - start_time) < timedelta( + while len(events) < 1 and (datetime.now() - start_time) < timedelta( seconds=TIMEOUT ): await asyncio.sleep(1) - events = await client.fetch_events( - response_filter, timeout=timedelta(seconds=1) - ) - assert events.len() == 1 + events = await client.fetch_events(target, timeout=timedelta(seconds=1)) + assert len(events) == 1 - return events.first() + return events[0] @pytest.mark.asyncio @@ -155,38 +152,35 @@ async def test_get_balance(nostr_relay, node_factory, get_plugin): # noqa: F811 uri_str = l1.rpc.call("nip47-create", ["test1", 3000])["uri"] LOGGER.info(uri_str) uri = NostrWalletConnectUri.parse(uri_str) - signer = NostrSigner.keys(Keys(uri.secret())) - client = Client(signer) + client = Client() await client.add_relay(RelayUrl.parse(url)) await client.connect() await fetch_info_event(client, uri) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) balance = await nwc.get_balance() - assert balance == 3000 + assert balance.balance == 3000 uri_str = l1.rpc.call("nip47-create", ["test2"])["uri"] LOGGER.info(uri_str) uri = NostrWalletConnectUri.parse(uri_str) - signer = NostrSigner.keys(Keys(uri.secret())) - client = Client(signer) + client = Client() await client.add_relay(RelayUrl.parse(url)) await client.connect() await fetch_info_event(client, uri) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) balance = await nwc.get_balance() - assert balance == node_balance + assert balance.balance == node_balance uri_str = l1.rpc.call("nip47-create", ["test3", 0])["uri"] LOGGER.info(uri_str) uri = NostrWalletConnectUri.parse(uri_str) - signer = NostrSigner.keys(Keys(uri.secret())) - client = Client(signer) + client = Client() await client.add_relay(RelayUrl.parse(url)) await client.connect() await fetch_info_event(client, uri) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) balance = await nwc.get_balance() - assert balance == 0 + assert balance.balance == 0 with pytest.raises(RpcError, match="not an integer"): uri_str = l1.rpc.call("nip47-create", ["test3", -1])["uri"] @@ -207,24 +201,23 @@ async def test_get_info(nostr_relay, node_factory, get_plugin): # noqa: F811 uri_str = l1.rpc.call("nip47-create", ["test1", 3000])["uri"] LOGGER.info(uri_str) uri = NostrWalletConnectUri.parse(uri_str) - signer = NostrSigner.keys(Keys(uri.secret())) - client = Client(signer) + client = Client() await client.add_relay(RelayUrl.parse(url)) await client.connect() await fetch_info_event(client, uri) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) get_info = await nwc.get_info() assert get_info.alias == node_get_info["alias"] assert get_info.block_height == node_get_info["blockheight"] assert get_info.color == node_get_info["color"] assert get_info.methods == [ - Method.MAKE_INVOICE, - Method.LOOKUP_INVOICE, - Method.LIST_TRANSACTIONS, - Method.GET_BALANCE, - Method.GET_INFO, - Method.PAY_INVOICE, - Method.PAY_KEYSEND, + Method.MAKE_INVOICE(), + Method.LOOKUP_INVOICE(), + Method.LIST_TRANSACTIONS(), + Method.GET_BALANCE(), + Method.GET_INFO(), + Method.PAY_INVOICE(), + Method.PAY_KEYSEND(), ] assert get_info.network == "regtest" assert get_info.notifications == ["payment_received", "payment_sent"] @@ -248,13 +241,13 @@ async def test_get_info(nostr_relay, node_factory, get_plugin): # noqa: F811 assert get_info.block_height == node_get_info["blockheight"] assert get_info.color == node_get_info["color"] assert get_info.methods == [ - Method.MAKE_INVOICE, - Method.LOOKUP_INVOICE, - Method.LIST_TRANSACTIONS, - Method.GET_BALANCE, - Method.GET_INFO, - Method.PAY_INVOICE, - Method.PAY_KEYSEND, + Method.MAKE_INVOICE(), + Method.LOOKUP_INVOICE(), + Method.LIST_TRANSACTIONS(), + Method.GET_BALANCE(), + Method.GET_INFO(), + Method.PAY_INVOICE(), + Method.PAY_KEYSEND(), ] assert get_info.network == "regtest" assert get_info.notifications == [] @@ -264,28 +257,27 @@ async def test_get_info(nostr_relay, node_factory, get_plugin): # noqa: F811 info_event.content() == "make_invoice lookup_invoice list_transactions get_balance get_info pay_invoice pay_keysend" ) - assert ( - info_event.tags().find(TagKind.UNKNOWN("encryption")).content() - == "nip44_v2 nip04" + encryption_tag = next( + tag for tag in info_event.tags() if tag.kind() == "encryption" ) - assert info_event.tags().find(TagKind.UNKNOWN("notifications")) is None + assert encryption_tag.content() == "nip44_v2 nip04" + assert not any(tag.kind() == "notifications" for tag in info_event.tags()) uri_str = l1.rpc.call("nip47-create", ["test2", 0])["uri"] LOGGER.info(uri_str) uri = NostrWalletConnectUri.parse(uri_str) - signer = NostrSigner.keys(Keys(uri.secret())) - client = Client(signer) + client = Client() await client.add_relay(RelayUrl.parse(url)) await client.connect() await fetch_info_event(client, uri) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) get_info = await nwc.get_info() assert get_info.methods == [ - Method.MAKE_INVOICE, - Method.LOOKUP_INVOICE, - Method.LIST_TRANSACTIONS, - Method.GET_BALANCE, - Method.GET_INFO, + Method.MAKE_INVOICE(), + Method.LOOKUP_INVOICE(), + Method.LIST_TRANSACTIONS(), + Method.GET_BALANCE(), + Method.GET_INFO(), ] info_event = await fetch_info_event(client, uri) @@ -293,11 +285,11 @@ async def test_get_info(nostr_relay, node_factory, get_plugin): # noqa: F811 info_event.content() == "make_invoice lookup_invoice list_transactions get_balance get_info" ) - assert ( - info_event.tags().find(TagKind.UNKNOWN("encryption")).content() - == "nip44_v2 nip04" + encryption_tag = next( + tag for tag in info_event.tags() if tag.kind() == "encryption" ) - assert info_event.tags().find(TagKind.UNKNOWN("notifications")) is None + assert encryption_tag.content() == "nip44_v2 nip04" + assert not any(tag.kind() == "notifications" for tag in info_event.tags()) @pytest.mark.asyncio @@ -314,12 +306,11 @@ async def test_make_invoice(nostr_relay, node_factory, get_plugin): # noqa: F81 uri_str = l1.rpc.call("nip47-create", ["test1", 3000])["uri"] LOGGER.info(uri_str) uri = NostrWalletConnectUri.parse(uri_str) - signer = NostrSigner.keys(Keys(uri.secret())) - client = Client(signer) + client = Client() await client.add_relay(RelayUrl.parse(url)) await client.connect() await fetch_info_event(client, uri) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) timestamp = int(time.time()) invoice = await nwc.make_invoice( MakeInvoiceRequest( @@ -407,12 +398,11 @@ async def test_pay_keysend(nostr_relay, node_factory, get_plugin): # noqa: F811 uri_str = l1.rpc.call("nip47-create", ["test1", 3000])["uri"] LOGGER.info(uri_str) uri = NostrWalletConnectUri.parse(uri_str) - signer = NostrSigner.keys(Keys(uri.secret())) - client = Client(signer) + client = Client() await client.add_relay(RelayUrl.parse(url)) await client.connect() await fetch_info_event(client, uri) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) result = await nwc.pay_keysend( PayKeysendRequest( id="id123", amount=1000, pubkey=l3.info["id"], preimage=None, tlv_records=[] @@ -488,12 +478,11 @@ async def test_lookup_invoice(nostr_relay, node_factory, get_plugin): # noqa: F uri_str = l1.rpc.call("nip47-create", ["test1", 3000])["uri"] LOGGER.info(uri_str) uri = NostrWalletConnectUri.parse(uri_str) - signer = NostrSigner.keys(Keys(uri.secret())) - client = Client(signer) + client = Client() await client.add_relay(RelayUrl.parse(url)) await client.connect() await fetch_info_event(client, uri) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) invoice = await nwc.make_invoice( MakeInvoiceRequest( amount=3000, description="test1", description_hash=None, expiry=None @@ -715,12 +704,11 @@ async def test_list_transactions(nostr_relay, node_factory, get_plugin): # noqa uri_str = l1.rpc.call("nip47-create", ["test1"])["uri"] LOGGER.info(uri_str) uri = NostrWalletConnectUri.parse(uri_str) - signer = NostrSigner.keys(Keys(uri.secret())) - client = Client(signer) + client = Client() await client.add_relay(RelayUrl.parse(url)) await client.connect() await fetch_info_event(client, uri) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) for i in range(10): invoice = l2.rpc.call( "invoice", @@ -803,12 +791,12 @@ async def test_notifications(nostr_relay, node_factory, get_plugin): # noqa: F8 LOGGER.info(uri_str) uri = NostrWalletConnectUri.parse(uri_str) - signer = NostrSigner.keys(Keys(uri.secret())) - client = Client(signer) + keys = Keys(uri.secret()) + client = Client() await client.add_relay(RelayUrl.parse(url)) await client.connect() await fetch_info_event(client, uri) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) invoice = l3.rpc.call( "invoice", @@ -870,11 +858,10 @@ async def test_notifications(nostr_relay, node_factory, get_plugin): # noqa: F8 responses = responses1 + responses2 LOGGER.info(f"response1: {responses1} response2: {responses2}") assert len(responses) == 2 - signer = NostrSigner.keys(Keys(uri.secret())) received_events = [] sent_events = [] for event in responses: - content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = keys.nip04_decrypt(uri.public_key(), event.content()) content = json.loads(content) LOGGER.info(content) if content["notification_type"] == "payment_received": @@ -973,8 +960,7 @@ async def test_pay_invoice(nostr_relay, node_factory, get_plugin): # noqa: F811 ) uri_str = l1.rpc.call("nip47-create", ["test1", 3001])["uri"] uri = NostrWalletConnectUri.parse(uri_str) - signer = NostrSigner.keys(Keys(uri.secret())) - client = Client(signer) + client = Client() await client.add_relay(RelayUrl.parse(url)) await client.connect() await fetch_info_event(client, uri) @@ -983,7 +969,7 @@ async def test_pay_invoice(nostr_relay, node_factory, get_plugin): # noqa: F811 "invoice", {"label": generate_random_label(), "description": "test1", "amount_msat": 3000}, ) - nwc = Nwc(NostrWalletConnectUri.parse(uri_str)) + nwc = NostrWalletConnect(NostrWalletConnectUri.parse(uri_str)) result = await nwc.pay_invoice( PayInvoiceRequest(id=None, amount=None, invoice=invoice["bolt11"]) ) @@ -1041,12 +1027,11 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 l1.daemon.wait_for_log("All NWC's loaded") await asyncio.sleep(3) uri = NostrWalletConnectUri.parse(uri_str) - signer = NostrSigner.keys(Keys(uri.secret())) - client = Client(signer) + client = Client() await client.add_relay(RelayUrl.parse(url)) await client.connect() await fetch_info_event(client, uri) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) result = await nwc.pay_invoice( PayInvoiceRequest(id=None, amount=None, invoice=invoice["bolt11"]) ) @@ -1082,12 +1067,11 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 uri_str = l1.rpc.call("nip47-create", ["test1", 3000, "10sec"])["uri"] uri = NostrWalletConnectUri.parse(uri_str) - signer = NostrSigner.keys(Keys(uri.secret())) - client = Client(signer) + client = Client() await client.add_relay(RelayUrl.parse(url)) await client.connect() await fetch_info_event(client, uri) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) invoice = l2.rpc.call( "invoice", @@ -1179,14 +1163,13 @@ async def test_budget_command(nostr_relay, node_factory, get_plugin): # noqa: F {"label": generate_random_label(), "description": "test1", "amount_msat": 5000}, ) uri = NostrWalletConnectUri.parse(uri_str) - signer = NostrSigner.keys(Keys(uri.secret())) - client = Client(signer) + client = Client() await client.add_relay(RelayUrl.parse(url)) await client.connect() await fetch_info_event(client, uri) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) balance = await nwc.get_balance() - assert balance == 3000 + assert balance.balance == 3000 with pytest.raises(NostrSdkError.Generic, match="Payment exceeds budget"): await nwc.pay_invoice( @@ -1195,7 +1178,7 @@ async def test_budget_command(nostr_relay, node_factory, get_plugin): # noqa: F l1.rpc.call("nip47-budget", ["test1", 4000]) balance = await nwc.get_balance() - assert balance == 4000 + assert balance.balance == 4000 with pytest.raises(NostrSdkError.Generic, match="Payment exceeds budget"): await nwc.pay_invoice( @@ -1204,7 +1187,7 @@ async def test_budget_command(nostr_relay, node_factory, get_plugin): # noqa: F l1.rpc.call("nip47-budget", ["test1", 5000, "15s"]) balance = await nwc.get_balance() - assert balance == 5000 + assert balance.balance == 5000 with pytest.raises( RpcError, match="`budget_msat` must be greater than 0 if you use `interval`" @@ -1217,17 +1200,17 @@ async def test_budget_command(nostr_relay, node_factory, get_plugin): # noqa: F assert pay.preimage is not None balance = await nwc.get_balance() - assert balance == 0 + assert balance.balance == 0 get_info = await nwc.get_info() assert get_info.methods == [ - Method.MAKE_INVOICE, - Method.LOOKUP_INVOICE, - Method.LIST_TRANSACTIONS, - Method.GET_BALANCE, - Method.GET_INFO, - Method.PAY_INVOICE, - Method.PAY_KEYSEND, + Method.MAKE_INVOICE(), + Method.LOOKUP_INVOICE(), + Method.LIST_TRANSACTIONS(), + Method.GET_BALANCE(), + Method.GET_INFO(), + Method.PAY_INVOICE(), + Method.PAY_KEYSEND(), ] info_event = await fetch_info_event(client, uri) @@ -1236,31 +1219,31 @@ async def test_budget_command(nostr_relay, node_factory, get_plugin): # noqa: F info_event.content() == "make_invoice lookup_invoice list_transactions get_balance get_info pay_invoice pay_keysend notifications" ) - assert ( - info_event.tags().find(TagKind.UNKNOWN("encryption")).content() - == "nip44_v2 nip04" + encryption_tag = next( + tag for tag in info_event.tags() if tag.kind() == "encryption" ) - assert ( - info_event.tags().find(TagKind.UNKNOWN("notifications")).content() - == "payment_received payment_sent" + assert encryption_tag.content() == "nip44_v2 nip04" + notification_tag = next( + tag for tag in info_event.tags() if tag.kind() == "notifications" ) + assert notification_tag.content() == "payment_received payment_sent" await asyncio.sleep(18) balance = await nwc.get_balance() - assert balance == 5000 + assert balance.balance == 5000 l1.rpc.call("nip47-budget", ["test1", 0]) balance = await nwc.get_balance() - assert balance == 0 + assert balance.balance == 0 get_info = await nwc.get_info() assert get_info.methods == [ - Method.MAKE_INVOICE, - Method.LOOKUP_INVOICE, - Method.LIST_TRANSACTIONS, - Method.GET_BALANCE, - Method.GET_INFO, + Method.MAKE_INVOICE(), + Method.LOOKUP_INVOICE(), + Method.LIST_TRANSACTIONS(), + Method.GET_BALANCE(), + Method.GET_INFO(), ] info_event = await fetch_info_event(client, uri) @@ -1268,14 +1251,14 @@ async def test_budget_command(nostr_relay, node_factory, get_plugin): # noqa: F info_event.content() == "make_invoice lookup_invoice list_transactions get_balance get_info notifications" ) - assert ( - info_event.tags().find(TagKind.UNKNOWN("encryption")).content() - == "nip44_v2 nip04" + encryption_tag = next( + tag for tag in info_event.tags() if tag.kind() == "encryption" ) - assert ( - info_event.tags().find(TagKind.UNKNOWN("notifications")).content() - == "payment_received payment_sent" + assert encryption_tag.content() == "nip44_v2 nip04" + notification_tag = next( + tag for tag in info_event.tags() if tag.kind() == "notifications" ) + assert notification_tag.content() == "payment_received payment_sent" @pytest.mark.asyncio @@ -1311,7 +1294,7 @@ async def test_hold_invoice( LOGGER.info(uri_str) uri = NostrWalletConnectUri.parse(uri_str) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) preimage = secrets.token_hex(32) payment_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() @@ -1325,14 +1308,14 @@ async def test_hold_invoice( }, } content = json.dumps(content) - signer = NostrSigner.keys(Keys(uri.secret())) - encrypted_content = await signer.nip04_encrypt(uri.public_key(), content) + keys = Keys(uri.secret()) + encrypted_content = keys.nip04_encrypt(uri.public_key(), content) event = ( await EventBuilder(Kind(23194), encrypted_content) .tags([Tag.public_key(uri.public_key())]) - .sign(signer) + .finalize_async(keys) ) - client = Client(signer) + client = Client() relay_url = RelayUrl.parse(url) await client.add_relay(relay_url) await client.connect() @@ -1346,7 +1329,7 @@ async def test_hold_invoice( success_events = [] for event in responses1: LOGGER.info(event) - content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = keys.nip04_decrypt(uri.public_key(), event.content()) content = json.loads(content) LOGGER.info(content) if "result" in content and content["result"] is not None: @@ -1403,7 +1386,7 @@ async def test_hold_invoice( hold_events = [] for event in responses2: LOGGER.info(event) - content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = keys.nip04_decrypt(uri.public_key(), event.content()) content = json.loads(content) LOGGER.info(content) if content["notification_type"] == "hold_invoice_accepted": @@ -1427,7 +1410,7 @@ async def test_hold_invoice( assert lookup_hold.preimage is None assert lookup_hold.payment_hash == payment_hash assert lookup_hold.transaction_type.name == "INCOMING" - assert lookup_hold.state.name == "PENDING" # TODO ACCEPTED STATE + assert lookup_hold.state.name == "ACCEPTED" assert lookup_hold.settled_at is None content = { @@ -1437,11 +1420,11 @@ async def test_hold_invoice( }, } content = json.dumps(content) - encrypted_content = await signer.nip04_encrypt(uri.public_key(), content) + encrypted_content = keys.nip04_encrypt(uri.public_key(), content) event = ( await EventBuilder(Kind(23194), encrypted_content) .tags([Tag.public_key(uri.public_key())]) - .sign(signer) + .finalize_async(keys) ) (responses3, _res) = await fetch_event_responses( @@ -1451,7 +1434,7 @@ async def test_hold_invoice( success_events = [] for event in responses3: LOGGER.info(event) - content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = keys.nip04_decrypt(uri.public_key(), event.content()) content = json.loads(content) LOGGER.info(content) if ( @@ -1509,12 +1492,11 @@ async def test_hold_invoice( }, } content = json.dumps(content) - signer = NostrSigner.keys(Keys(uri.secret())) - encrypted_content = await signer.nip04_encrypt(uri.public_key(), content) + encrypted_content = keys.nip04_encrypt(uri.public_key(), content) event = ( await EventBuilder(Kind(23194), encrypted_content) .tags([Tag.public_key(uri.public_key())]) - .sign(signer) + .finalize_async(keys) ) (responses4, _res) = await fetch_event_responses( @@ -1524,7 +1506,7 @@ async def test_hold_invoice( success_events = [] for event in responses4: LOGGER.info(event) - content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = keys.nip04_decrypt(uri.public_key(), event.content()) content = json.loads(content) LOGGER.info(content) if ( @@ -1558,7 +1540,7 @@ async def test_hold_invoice( hold_events = [] for event in responses5: LOGGER.info(event) - content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = keys.nip04_decrypt(uri.public_key(), event.content()) content = json.loads(content) LOGGER.info(content) if ( @@ -1575,11 +1557,11 @@ async def test_hold_invoice( }, } content = json.dumps(content) - encrypted_content = await signer.nip04_encrypt(uri.public_key(), content) + encrypted_content = keys.nip04_encrypt(uri.public_key(), content) event = ( await EventBuilder(Kind(23194), encrypted_content) .tags([Tag.public_key(uri.public_key())]) - .sign(signer) + .finalize_async(keys) ) (responses6, _res) = await fetch_event_responses( @@ -1593,7 +1575,7 @@ async def test_hold_invoice( success_events = [] for event in responses6: LOGGER.info(event) - content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = keys.nip04_decrypt(uri.public_key(), event.content()) content = json.loads(content) LOGGER.info(content) if ( @@ -1639,7 +1621,7 @@ async def test_hold_invoice( ) ) - nwc = Nwc(uri) + nwc = NostrWalletConnect(uri) invoice_lookup1 = await nwc.lookup_invoice( LookupInvoiceRequest( @@ -1699,11 +1681,11 @@ async def test_hold_invoice( }, } content = json.dumps(content) - encrypted_content = await signer.nip04_encrypt(uri.public_key(), content) + encrypted_content = keys.nip04_encrypt(uri.public_key(), content) event = ( await EventBuilder(Kind(23194), encrypted_content) .tags([Tag.public_key(uri.public_key())]) - .sign(signer) + .finalize_async(keys) ) await client.send_event(event) @@ -1718,7 +1700,8 @@ async def test_hold_invoice( ) ) break - except Exception: + except Exception as e: # noqa: BLE001 + LOGGER.error(e) continue lookup_hold = await nwc.lookup_invoice( @@ -1750,11 +1733,11 @@ async def test_hold_invoice( }, } content = json.dumps(content) - encrypted_content = await signer.nip04_encrypt(uri.public_key(), content) + encrypted_content = keys.nip04_encrypt(uri.public_key(), content) event = ( await EventBuilder(Kind(23194), encrypted_content) .tags([Tag.public_key(uri.public_key())]) - .sign(signer) + .finalize_async(keys) ) await client.send_event(event) @@ -1769,7 +1752,8 @@ async def test_hold_invoice( ) ) break - except Exception: + except Exception as e: # noqa: BLE001 + LOGGER.error(e) continue lookup_hold = await nwc.lookup_invoice( @@ -1793,12 +1777,15 @@ async def test_hold_invoice( info_event.content() == "make_invoice lookup_invoice list_transactions get_balance get_info pay_invoice pay_keysend make_hold_invoice cancel_hold_invoice settle_hold_invoice notifications" ) - assert ( - info_event.tags().find(TagKind.UNKNOWN("encryption")).content() - == "nip44_v2 nip04" + encryption_tag = next( + tag for tag in info_event.tags() if tag.kind() == "encryption" + ) + assert encryption_tag.content() == "nip44_v2 nip04" + notification_tag = next( + tag for tag in info_event.tags() if tag.kind() == "notifications" ) assert ( - info_event.tags().find(TagKind.UNKNOWN("notifications")).content() + notification_tag.content() == "payment_received payment_sent hold_invoice_accepted" ) @@ -1816,7 +1803,7 @@ async def test_hold_invoice( hold_events = [] for event in responses7: LOGGER.info(event) - content = await signer.nip04_decrypt(uri.public_key(), event.content()) + content = keys.nip04_decrypt(uri.public_key(), event.content()) content = json.loads(content) LOGGER.info(content) if ( diff --git a/tests/uv.lock b/tests/uv.lock index d90c9ac..c2c260e 100644 --- a/tests/uv.lock +++ b/tests/uv.lock @@ -393,7 +393,7 @@ dev = [ [package.metadata.requires-dev] dev = [ - { name = "nostr-sdk", specifier = ">=0.44" }, + { name = "nostr-sdk", specifier = ">=0.45" }, { name = "pyln-client", specifier = ">=25.9" }, { name = "pyln-proto", specifier = ">=25.9" }, { name = "pyln-testing", specifier = ">=25.9" }, @@ -748,22 +748,22 @@ wheels = [ [[package]] name = "nostr-sdk" -version = "0.44.6" +version = "0.45.0" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/61/c3/1fbcb47aff23b368b817e5f6e90f3284b4b299e9bcf08e9a4cd414b80020/nostr_sdk-0.44.6-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:41dabb2539513db0b354c4fd2a3391307e50eb913a60c9325b1565e80e98530f", size = 3365132, upload-time = "2026-07-26T09:02:22.234Z" }, - { url = "https://files.pythonhosted.org/packages/43/08/16525328d477e1fb427d47c0ccc14ce5b25f120871947374457a3267ae99/nostr_sdk-0.44.6-cp39-abi3-macosx_11_0_x86_64.whl", hash = "sha256:326526e6fd736c4dd6ad73b5cf7653d1a0ae46a3fb8d8ed4b4cb221ee64f7feb", size = 3482251, upload-time = "2026-07-26T09:02:23.623Z" }, - { url = "https://files.pythonhosted.org/packages/65/a4/0f210f8490faf4c24dbc4bfbdc1943afc632d907c6fae48f88f0f3b55605/nostr_sdk-0.44.6-cp39-abi3-manylinux_2_17_aarch64.whl", hash = "sha256:811637b021cfa338a38e37dda8df5dbeebe447458d567ad9b700a65199f6874c", size = 3611794, upload-time = "2026-07-26T09:02:24.758Z" }, - { url = "https://files.pythonhosted.org/packages/f2/73/ce525eed5f93969c62984f9397c80acf99883eab52bc055259e189eb1375/nostr_sdk-0.44.6-cp39-abi3-manylinux_2_17_armv7l.whl", hash = "sha256:44cbec5c99a8a8c8e8beeb80ae7427a3ff20b943ca459bd0ba556e41c20dd246", size = 3359056, upload-time = "2026-07-26T09:02:26.101Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ad/f700ddbbfcf2d0ff2285dec9b87b8b290d1f4359add21cb09b568bf52c0f/nostr_sdk-0.44.6-cp39-abi3-manylinux_2_17_i686.whl", hash = "sha256:64e0304313bbbaa05226d9cac98618f94ab20e8e3f5e3a297179454e657d3446", size = 3607461, upload-time = "2026-07-26T09:02:27.393Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ec/947f9fd4161a69da12bdbb8502a377c5ada2ab8e640ce5515c4a001cf81f/nostr_sdk-0.44.6-cp39-abi3-manylinux_2_17_x86_64.whl", hash = "sha256:0234eae16b0fc11e3d72eb08c4c14b0c333a992e700d8fb88f191a42ef017a07", size = 3726683, upload-time = "2026-07-26T09:02:28.925Z" }, - { url = "https://files.pythonhosted.org/packages/01/fe/448443ef9e23433f202c0362c2eb9e1b98dc0efffb5c3f9cae6f6252a5a8/nostr_sdk-0.44.6-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d16adc26a42cab37ae4ae8e3a8bed03dcedbe88c7862133c48cf9d99cb9e0cf3", size = 3605361, upload-time = "2026-07-26T09:02:30.308Z" }, - { url = "https://files.pythonhosted.org/packages/df/02/b0e83f3f5066e525e5c1fd44c81f6711479cc6942aee298ebcd545fb3e59/nostr_sdk-0.44.6-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:dccebbc33500718ace5cf8e7ac6ed7fa41ec07f65f157896b392ac5f0b9ab2ed", size = 3360640, upload-time = "2026-07-26T09:02:31.615Z" }, - { url = "https://files.pythonhosted.org/packages/9e/01/7104eacda8609d52a148171354bca1095227a651860022984bf518ead1f2/nostr_sdk-0.44.6-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:e73a55e1f52dd4cb006f502725e65ab12b68609cb1b18d96b2ba74a3b245d264", size = 3493797, upload-time = "2026-07-26T09:02:32.973Z" }, - { url = "https://files.pythonhosted.org/packages/00/80/e8de31cf0f1cf4c65ca5691a6c8baabbed19daccdccb427d73968e0335b9/nostr_sdk-0.44.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:512229272a68cc651bbe96b1d0ec78d12b899708d871ef92b49ce96eb33ba1fd", size = 3724890, upload-time = "2026-07-26T09:02:34.219Z" }, - { url = "https://files.pythonhosted.org/packages/83/0f/6d4c364bd4f1ad1c76030aeddc43c5f6edd63d79cec289229de6afa5a72d/nostr_sdk-0.44.6-cp39-abi3-win32.whl", hash = "sha256:5eee3919224b653007e71a8e8a43465ee36156132839dfd15c120ee9b54d075c", size = 3164542, upload-time = "2026-07-26T09:02:35.291Z" }, - { url = "https://files.pythonhosted.org/packages/90/f5/11141f5612f8fb361d39dbb95d6ec916059213a8d5625f8f98b58a7cfc70/nostr_sdk-0.44.6-cp39-abi3-win_amd64.whl", hash = "sha256:f89a13a4cec623283cd66218b8efedc8ec618b1b3e33a0e398240469f7b317e6", size = 3383380, upload-time = "2026-07-26T09:02:36.804Z" }, - { url = "https://files.pythonhosted.org/packages/d0/3e/552318b5c8307ab2f725de59b9bedbd06df3f031c252d11b15dcea302511/nostr_sdk-0.44.6-cp39-abi3-win_arm64.whl", hash = "sha256:c36845d218118dc0330f554fdea5c6e0c4a73bc54cb74dcf628750bf9260e01f", size = 3236001, upload-time = "2026-07-26T09:02:37.906Z" }, + { url = "https://files.pythonhosted.org/packages/b9/73/9401a17b0a43bf22542f31e5c5624db7f838f7690977ec786edd72b0880b/nostr_sdk-0.45.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:5bd1a6b972e9f5ae8c4f4448a148f668f124a3389b72eafb9de00f714e829059", size = 3835872, upload-time = "2026-08-05T13:14:21.43Z" }, + { url = "https://files.pythonhosted.org/packages/e2/5d/355adf2a676493101357080857cf7896880c6556fe84795281bc283513c5/nostr_sdk-0.45.0-cp39-abi3-macosx_11_0_x86_64.whl", hash = "sha256:a20cb90f4fa225e57118431fc05d1d567a78a047dab54d34897365fcc5b82498", size = 3901388, upload-time = "2026-08-05T13:14:22.92Z" }, + { url = "https://files.pythonhosted.org/packages/a1/72/489cd53d491afe180f71d24c4f4f334f6da38d1bf683e8907771f436bffd/nostr_sdk-0.45.0-cp39-abi3-manylinux_2_17_aarch64.whl", hash = "sha256:00da5e7e387515d1e12779eccfb2c794856f774b4534694200434aa0146a03ed", size = 4323888, upload-time = "2026-08-05T13:14:24.348Z" }, + { url = "https://files.pythonhosted.org/packages/7e/cb/9a5edbea9db9995704ebc44ec4fca153c5f7d9ea07d668cf7837f01ddf1e/nostr_sdk-0.45.0-cp39-abi3-manylinux_2_17_armv7l.whl", hash = "sha256:e5cae5ef56411d6665de78c39822a4e80382231d44b4a6fc3db096b8b203ec26", size = 3867750, upload-time = "2026-08-05T13:14:26.066Z" }, + { url = "https://files.pythonhosted.org/packages/c1/d3/1a464004596e9ea0366a62e1671fc41adefde97b477bf60e69615dedec64/nostr_sdk-0.45.0-cp39-abi3-manylinux_2_17_i686.whl", hash = "sha256:9d91d519a93c48ab4d689f975f406d8ee96e39d6876d2aeaaee58de7e66ded93", size = 4393993, upload-time = "2026-08-05T13:14:27.766Z" }, + { url = "https://files.pythonhosted.org/packages/c1/57/1684e67051af3540666e6aeba4ed5bc9ea027cbe311789dce79863c85b48/nostr_sdk-0.45.0-cp39-abi3-manylinux_2_17_x86_64.whl", hash = "sha256:8152945cb27b48bbf6f89d73d8560bfbadd202b8e5c165bf5b5532e91feed417", size = 4426295, upload-time = "2026-08-05T13:14:29.397Z" }, + { url = "https://files.pythonhosted.org/packages/34/b0/526e134d23e2bdbac523bb38351d940a478cc2d7499c0ee9d1ee499ecfb4/nostr_sdk-0.45.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a7e0a886b427356dc94cd0b1bc8421b01eeb5aa9ea26e1b69c21f0a3cc1bc2ad", size = 4336015, upload-time = "2026-08-05T13:14:30.836Z" }, + { url = "https://files.pythonhosted.org/packages/70/27/764af3a4bc1ec8542401616f11c41bbf9c42fbaef8659bf26bab4097ed6f/nostr_sdk-0.45.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:3b314b5160014bf70b39e95b3cf5cb059050e2fc2f782a0d9916c7ae1559e34f", size = 3864414, upload-time = "2026-08-05T13:14:32.352Z" }, + { url = "https://files.pythonhosted.org/packages/bd/31/1289150239dfce59c1c2afb8313dfd21411040103c68e6121f1ba4515fb7/nostr_sdk-0.45.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:15346b498f4df4bb42ef2987cecd9e97ba60294b9bc7bb486e1c4b6bee4f528e", size = 4205457, upload-time = "2026-08-05T13:14:33.858Z" }, + { url = "https://files.pythonhosted.org/packages/8b/63/3964d86ed2f36f691374813751fb342de0c169ea0d926609379e50fd761c/nostr_sdk-0.45.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:78ccdb4766ae627ac9e2c10a7bb224d1e00da48fc7b61c7f48935ae23241fd64", size = 4410510, upload-time = "2026-08-05T13:14:35.224Z" }, + { url = "https://files.pythonhosted.org/packages/0d/15/a9c8da6d37bbbbfe07cba889a3d149f030985e524758ba51b44ca7a29ca0/nostr_sdk-0.45.0-cp39-abi3-win32.whl", hash = "sha256:45ff1ae2f19fedfa9f91b0c81c1be755b706e2fd1e7865ea46612c2ad1d98a3e", size = 3549588, upload-time = "2026-08-05T13:14:36.642Z" }, + { url = "https://files.pythonhosted.org/packages/99/f0/e03b7abb4dc016ea19dc2f311f367e472df47c187fbb69cee6df3ed2f323/nostr_sdk-0.45.0-cp39-abi3-win_amd64.whl", hash = "sha256:b3904abc4d7e4ac46ed66c8aa528e89748f3f25023708e4bbab58bc81d3a99f7", size = 3870530, upload-time = "2026-08-05T13:14:38.079Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d0/f80080bd2aa73ed25c7a9d8d21020fc73eaa10591b39e13ec2b88228a8ce/nostr_sdk-0.45.0-cp39-abi3-win_arm64.whl", hash = "sha256:5a0fc63b69995bf2dac44e479aaec1ce1af71e687c9f4258891e429ed3db25e5", size = 3704095, upload-time = "2026-08-05T13:14:39.494Z" }, ] [[package]] From 5c65a94c138265cb107ecf46bacdd77f8d4141bf Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sat, 8 Aug 2026 15:01:17 +0200 Subject: [PATCH 27/56] add retry logic for hold server connect --- src/main.rs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index f80d21a..c8136fa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -285,11 +285,30 @@ async fn check_hold_support(plugin: Plugin) -> Result<(), anyhow::E .identity(identity) .domain_name("hold"); - let hold_channel = Endpoint::from_shared(format!("https://{hold_grpc_host}:{hold_grpc_port}"))? + let endpoint = Endpoint::from_shared(format!("https://{hold_grpc_host}:{hold_grpc_port}"))? .tls_config(tls_config)? .keep_alive_while_idle(true) - .connect_lazy(); - *plugin.state().hold_client.lock() = Some(HoldClient::new(hold_channel)); + .connect_timeout(Duration::from_secs(5)); + + let max_retries = 20; + + let channel = loop { + match endpoint.connect().await { + Ok(channel) => break channel, + Err(e) if retries < max_retries => { + retries += 1; + + log::debug!("Hold gRPC server not ready, retrying ({retries}/{max_retries}): {e}"); + + time::sleep(Duration::from_millis(500)).await; + } + Err(e) => { + return Err(anyhow!("Hold gRPC server did not become ready: {e}")); + } + } + }; + + *plugin.state().hold_client.lock() = Some(HoldClient::new(channel)); Ok(()) } From 18c00d3442ac58184c1cfef526553f7e5054f5cf Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sat, 8 Aug 2026 15:08:16 +0200 Subject: [PATCH 28/56] SECURITY.md: init --- SECURITY.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..9b5fbd8 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,27 @@ +# Security Policy + +## Supported Versions + +Only the latest version is supported. + +## Reporting a Vulnerability + +To report security vulnerabilities, please send an email to: +- `daywalker990@gmx.de` + +Note: This email address is exclusively for vulnerability reporting. + +For all other inquiries/communication, please use the Github issues. + +## Signatures For Releases + +The following keys may be used to communicate sensitive information to +developers: + +| Name | Email | Fingerprint | +|------|-------|-------------| +| daywalker90 | `daywalker990@gmx.de` | 8A07 9421 A871 D0B1 0835 1193 7AB4 802E D5A6 39F3 | + +You can import a key by running the following command with that individual’s fingerprint: +`gpg --keyserver hkps://keys.openpgp.org --recv-keys ""`. +Ensure that you put quotes around fingerprints containing spaces. From 07bf8288f5ab2e72ad56d4f1e278eabcb34c76b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 8 Aug 2026 13:10:06 +0000 Subject: [PATCH 29/56] Bump actions/checkout from 6 to 7 Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cac47ea..82b3448 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Create cache paths run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6fef945..39f2ae2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: rust-version: ${{ steps.versions.outputs.rust-version }} steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Install Rust uses: dtolnay/rust-toolchain@stable @@ -57,7 +57,7 @@ jobs: macos-deployment-target: ${{ steps.build.outputs.macos-deployment-target }} steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Install Rust uses: dtolnay/rust-toolchain@stable @@ -135,7 +135,7 @@ jobs: id: tag_name run: echo "current_version=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT" - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Get Changelog Entry id: changelog_reader uses: mindsers/changelog-reader-action@v2 From 0a078e0b76e5c7aa9c4816133688a381ff8af17e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 8 Aug 2026 13:10:20 +0000 Subject: [PATCH 30/56] Bump actions/cache from 5 to 6 Bumps [actions/cache](https://github.com/actions/cache) from 5 to 6. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82b3448..b4b6a60 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,7 +66,7 @@ jobs: - name: Restore CLN cache id: cache-cln - uses: actions/cache/restore@v5 + uses: actions/cache/restore@v6 with: path: | /usr/local/bin/lightning* @@ -75,7 +75,7 @@ jobs: - name: Restore bitcoind cache id: cache-bitcoind - uses: actions/cache/restore@v5 + uses: actions/cache/restore@v6 with: path: /usr/local/bin/bitcoin* key: cache-bitcoind-${{ matrix.bitcoind-version }}-${{ steps.exact_versions.outputs.os_version }} @@ -99,7 +99,7 @@ jobs: - name: Save bitcoind cache id: save-cache-bitcoind if : ${{ success() && steps.cache-bitcoind.outputs.cache-hit != 'true'}} - uses: actions/cache/save@v5 + uses: actions/cache/save@v6 with: path: /usr/local/bin/bitcoin* key: ${{ steps.cache-bitcoind.outputs.cache-primary-key }} @@ -117,7 +117,7 @@ jobs: - name: Save CLN cache id: save-cache-cln if : ${{ success() && steps.cache-cln.outputs.cache-hit != 'true' }} - uses: actions/cache/save@v5 + uses: actions/cache/save@v6 with: path: | /usr/local/bin/lightning* From 66edfb7c5e4bc2e7574dd876be560f220f9b6823 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 8 Aug 2026 13:10:42 +0000 Subject: [PATCH 31/56] Bump actions/setup-python from 6 to 7 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6 to 7. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4b6a60..71402f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: sudo chown -R $USER /usr/local/libexec - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6 + uses: actions/setup-python@v7 with: python-version: ${{ matrix.python-version }} From 76f10bc2c5ffbb44f91fe2d89782c49336d99fde Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sat, 8 Aug 2026 15:11:01 +0200 Subject: [PATCH 32/56] README: update CLN version --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index eb2425b..59a8fe2 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,4 @@ - - - - + + + +
- - - - - - - -
@@ -47,6 +35,18 @@
+ + + + + + + +
From 6ff3069fa84a3faf8a69d6dedfba66dec8a8079d Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sat, 8 Aug 2026 15:14:39 +0200 Subject: [PATCH 33/56] README: update binaries notes --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 59a8fe2..2927042 100644 --- a/README.md +++ b/README.md @@ -64,11 +64,10 @@ Release binaries for * x86_64-linux * armv7-linux (Raspberry Pi 32bit) * aarch64-linux (Raspberry Pi 64bit) +* universal-apple-darwin (macOS) can be found on the [release](https://github.com/daywalker90/cln-nip47/releases) page. If you are unsure about your architecture you can run ``uname -m``. -They require ``glibc>=2.31``, which you can check with ``ldd --version``. - # Building You can build the plugin yourself instead of using the release binaries. First clone the repo: @@ -85,7 +84,7 @@ cargo build --release After that the binary will be here: ``target/release/cln-nip47`` -Note: Release binaries are built using ``cross`` and the ``optimized`` profile. +Note: Release binaries are built with the ``optimized`` profile. # Documentation From 408b015d91305198772f4a0b3e0cfcda29415406 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sat, 8 Aug 2026 17:12:48 +0200 Subject: [PATCH 34/56] improve handling of stale or duplicate events --- src/main.rs | 10 +++++ src/nwc.rs | 94 +++++++++++++++++++++++++++-------------- src/structs.rs | 2 + src/tasks.rs | 46 ++++++++++++++++++-- src/util.rs | 23 ++++++++-- tests/test_cln-nip47.py | 1 - 6 files changed, 137 insertions(+), 39 deletions(-) diff --git a/src/main.rs b/src/main.rs index c8136fa..b88c6e3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -157,6 +157,16 @@ async fn main() -> Result<(), anyhow::Error> { } } + let plugin_clone_cleanup = plugin.clone(); + tokio::spawn(async move { + loop { + if let Err(e) = tasks::cleanup_event_ids(plugin_clone_cleanup.clone()).await { + log::warn!("Error in cleanup_event_ids thread: {e}"); + } + time::sleep(Duration::from_secs(10)).await; + } + }); + plugin.join().await } diff --git a/src/nwc.rs b/src/nwc.rs index 68a1597..9677fa6 100644 --- a/src/nwc.rs +++ b/src/nwc.rs @@ -31,9 +31,9 @@ use crate::{ nwc_keysend::pay_keysend_response, nwc_lookups::{list_transactions_response, lookup_invoice_response}, nwc_pay::pay_invoice_response, - structs::{NwcStore, PluginState, WalletService}, + structs::{ID_MAX_AGE, NwcStore, PluginState, WalletService}, tasks::budget_task, - util::{build_capabilities, build_notifications_vec, is_read_only_nwc}, + util::{build_capabilities, build_notifications_vec, is_read_only_nwc, save_event_id}, }; #[allow(clippy::too_many_lines)] @@ -225,6 +225,7 @@ pub fn stop_nwc_budget_job(plugin: &Plugin, label: &String) { } } +#[allow(clippy::too_many_lines)] async fn nwc_request_handler( notification: ClientNotification, nostr_client: &client::Client, @@ -246,44 +247,73 @@ async fn nwc_request_handler( | ClientNotification::Shutdown => return Ok(()), }; - if let Some(expi) = event.tags.expiration() { - if expi < Timestamp::now() { - return Ok(()); - } - } log::debug!("relay_url:{relay_url} subscription_id:{subscription_id} {event:?}"); let mut use_nip44 = check_nip44_support(&event); let request = decrypt_request(&event.content, wallet_keys, &client_pubkey, &mut use_nip44)?; - let responses = match request.params { - nip47::RequestParams::PayInvoice(pay_invoice_request) => { - pay_invoice_response(plugin.clone(), pay_invoice_request, label).await + let responses = if event.tags.expiration().is_some() + && event.tags.expiration().unwrap() < Timestamp::now() + { + vec![( + nip47::Response { + result_type: request.method, + error: Some(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Event expired".to_owned(), + }), + result: None, + }, + None, + )] + } else if event.created_at.as_secs() < (Timestamp::now().as_secs() - ID_MAX_AGE) { + vec![( + nip47::Response { + result_type: request.method, + error: Some(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Event created too far in the past".to_owned(), + }), + result: None, + }, + None, + )] + } else { + { + let mut rpc = plugin.state().rpc_lock.lock().await; + save_event_id(&mut rpc, event.id.to_hex(), event.created_at).await?; } - nip47::RequestParams::PayKeysend(pay_keysend_request) => { - pay_keysend_response(plugin.clone(), pay_keysend_request, label).await - } - nip47::RequestParams::MakeInvoice(make_invoice_request) => { - make_invoice_response(plugin.clone(), make_invoice_request).await - } - nip47::RequestParams::LookupInvoice(lookup_invoice_request) => { - lookup_invoice_response(plugin.clone(), lookup_invoice_request).await - } - nip47::RequestParams::ListTransactions(list_transactions_request) => { - list_transactions_response(plugin.clone(), list_transactions_request).await - } - nip47::RequestParams::GetBalance => get_balance_response(plugin.clone(), label).await, - nip47::RequestParams::GetInfo => get_info_response(plugin.clone(), label).await, - nip47::RequestParams::MakeHoldInvoice(make_hold_invoice_request) => { - make_hold_invoice_response(plugin.clone(), make_hold_invoice_request).await - } - nip47::RequestParams::CancelHoldInvoice(cancel_hold_invoice_request) => { - cancel_hold_invoice_response(plugin.clone(), cancel_hold_invoice_request).await - } - nip47::RequestParams::SettleHoldInvoice(settle_hold_invoice_request) => { - settle_hold_invoice_response(plugin.clone(), settle_hold_invoice_request).await + + match request.params { + nip47::RequestParams::PayInvoice(pay_invoice_request) => { + pay_invoice_response(plugin.clone(), pay_invoice_request, label).await + } + nip47::RequestParams::PayKeysend(pay_keysend_request) => { + pay_keysend_response(plugin.clone(), pay_keysend_request, label).await + } + nip47::RequestParams::MakeInvoice(make_invoice_request) => { + make_invoice_response(plugin.clone(), make_invoice_request).await + } + nip47::RequestParams::LookupInvoice(lookup_invoice_request) => { + lookup_invoice_response(plugin.clone(), lookup_invoice_request).await + } + nip47::RequestParams::ListTransactions(list_transactions_request) => { + list_transactions_response(plugin.clone(), list_transactions_request).await + } + nip47::RequestParams::GetBalance => get_balance_response(plugin.clone(), label).await, + nip47::RequestParams::GetInfo => get_info_response(plugin.clone(), label).await, + nip47::RequestParams::MakeHoldInvoice(make_hold_invoice_request) => { + make_hold_invoice_response(plugin.clone(), make_hold_invoice_request).await + } + nip47::RequestParams::CancelHoldInvoice(cancel_hold_invoice_request) => { + cancel_hold_invoice_response(plugin.clone(), cancel_hold_invoice_request).await + } + nip47::RequestParams::SettleHoldInvoice(settle_hold_invoice_request) => { + settle_hold_invoice_response(plugin.clone(), settle_hold_invoice_request).await + } } }; + for (response, id) in responses { let content = match encrypt_response_content(&response, wallet_keys, &client_pubkey, use_nip44) { diff --git a/src/structs.rs b/src/structs.rs index ff0ff59..a60c49a 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -15,6 +15,8 @@ use tonic::transport::Channel; use crate::hold::hold_client::HoldClient; pub const NOT_INV_ERR: &str = "Not an invoice or invalid invoice"; +pub const ID_STORE: &str = "eventids"; +pub const ID_MAX_AGE: u64 = 7_200; #[derive(Clone)] pub struct PluginState { diff --git a/src/tasks.rs b/src/tasks.rs index e46ed40..3427834 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -1,13 +1,17 @@ -use std::{path::Path, time::Duration}; +use std::{path::Path, str::FromStr, time::Duration}; use anyhow::anyhow; use cln_plugin::Plugin; -use cln_rpc::ClnRpc; +use cln_rpc::{ + ClnRpc, + model::requests::{DeldatastoreRequest, ListdatastoreRequest}, +}; use nostr::types::Timestamp; use tokio::{sync::oneshot, time}; use crate::{ - structs::PluginState, + PLUGIN_NAME, + structs::{ID_MAX_AGE, ID_STORE, PluginState}, util::{load_nwc_store, update_nwc_store}, }; @@ -57,3 +61,39 @@ pub async fn budget_task( } Ok(()) } + +pub async fn cleanup_event_ids(plugin: Plugin) -> Result<(), anyhow::Error> { + let mut rpc = ClnRpc::new( + Path::new(&plugin.configuration().lightning_dir).join(&plugin.configuration().rpc_file), + ) + .await?; + + loop { + { + let ids = rpc + .call_typed(&ListdatastoreRequest { + key: Some(vec![format!("{}-{}", PLUGIN_NAME, ID_STORE)]), + }) + .await? + .datastore; + + let now = Timestamp::now(); + for id in ids { + if id.string.is_none() { + continue; + } + let timestamp = Timestamp::from_str(&id.string.unwrap())?; + if now.as_secs() - timestamp.as_secs() > ID_MAX_AGE { + rpc.call_typed(&DeldatastoreRequest { + generation: None, + key: id.key.clone(), + }) + .await?; + log::debug!("Cleaned up event id: {}", id.key.last().unwrap()); + } + } + } + + tokio::time::sleep(Duration::from_secs(120)).await; + } +} diff --git a/src/util.rs b/src/util.rs index 6d769ad..f6064f7 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,13 +1,12 @@ use anyhow::anyhow; use cln_plugin::Plugin; use cln_rpc::{ - model::requests::{DatastoreMode, DatastoreRequest, ListdatastoreRequest}, ClnRpc, + model::requests::{DatastoreMode, DatastoreRequest, ListdatastoreRequest}, }; -use nostr::nips::nip47; +use nostr::{nips::nip47, types::Timestamp}; use crate::{ - structs::{NwcStore, PluginState}, OPT_NOTIFICATIONS, PLUGIN_NAME, WALLET_HOLD_METHODS, @@ -15,6 +14,7 @@ use crate::{ WALLET_NOTIFICATIONS, WALLET_PAY_METHODS, WALLET_READ_METHODS, + structs::{ID_STORE, NwcStore, PluginState}, }; pub fn budget_amount_check( @@ -97,6 +97,23 @@ pub fn is_read_only_nwc(nwc_store: &NwcStore) -> bool { false } +pub async fn save_event_id( + rpc: &mut ClnRpc, + id: String, + timestamp: Timestamp, +) -> Result<(), anyhow::Error> { + rpc.call_typed(&DatastoreRequest { + key: vec![format!("{}-{}", PLUGIN_NAME, ID_STORE), id.clone()], + generation: None, + hex: None, + mode: Some(DatastoreMode::MUST_CREATE), + string: Some(timestamp.to_string()), + }) + .await?; + log::debug!("stored event id:{id}"); + Ok(()) +} + pub fn at_or_above_version(my_version: &str, min_version: &str) -> Result { let clean_start_my_version = my_version .split_once('v') diff --git a/tests/test_cln-nip47.py b/tests/test_cln-nip47.py index fbf4742..7e055ec 100644 --- a/tests/test_cln-nip47.py +++ b/tests/test_cln-nip47.py @@ -26,7 +26,6 @@ from nostr_sdk import ( MakeInvoiceRequest, Method, NostrSdkError, - NostrSigner, NostrWalletConnect, NostrWalletConnectUri, PayInvoiceRequest, From 651b11b229df231a9122f3c89832939f16532588 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sat, 8 Aug 2026 17:17:08 +0200 Subject: [PATCH 35/56] return error on unsupported invoice type lookup for holdinvoices --- src/nwc_lookups.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/nwc_lookups.rs b/src/nwc_lookups.rs index a9dc343..32fe39e 100644 --- a/src/nwc_lookups.rs +++ b/src/nwc_lookups.rs @@ -288,7 +288,12 @@ async fn get_and_decode_holdinvoice( let ph = match invoice_decoded.item_type { DecodeType::BOLT12_INVOICE => invoice_decoded.invoice_payment_hash.unwrap(), DecodeType::BOLT11_INVOICE => invoice_decoded.payment_hash.unwrap().to_string(), - _ => todo!(), + _ => { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Not a supported invoice type".to_owned(), + }); + } }; let payment_hash_hash = match hex::decode(&ph) { Ok(p) => p, From b2078bab813e00e6e90c01ad22daf264ecaf94bd Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 00:13:06 +0200 Subject: [PATCH 36/56] remove budget task, update budget on demand --- src/nwc.rs | 21 +--------- src/nwc_balance.rs | 7 +++- src/nwc_keysend.rs | 19 ++++++--- src/nwc_pay.rs | 38 +++++++++++------- src/parse.rs | 4 +- src/rpc.rs | 24 +++++++----- src/structs.rs | 4 +- src/tasks.rs | 50 ------------------------ src/util.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 157 insertions(+), 106 deletions(-) diff --git a/src/nwc.rs b/src/nwc.rs index 9677fa6..c761614 100644 --- a/src/nwc.rs +++ b/src/nwc.rs @@ -15,7 +15,7 @@ use nostr_sdk::{ error::Error, relay::RelayStatus, }; -use tokio::{sync::oneshot, time}; +use tokio::time; use crate::{ OPT_NOTIFICATIONS, @@ -32,7 +32,6 @@ use crate::{ nwc_lookups::{list_transactions_response, lookup_invoice_response}, nwc_pay::pay_invoice_response, structs::{ID_MAX_AGE, NwcStore, PluginState, WalletService}, - tasks::budget_task, util::{build_capabilities, build_notifications_vec, is_read_only_nwc, save_event_id}, }; @@ -57,7 +56,7 @@ pub async fn run_nwc( } if nwc_store.interval_config.is_some() { - start_nwc_budget_job(&plugin, label.clone()); + log::debug!("NWC {label} uses an interval budget"); } let nostr_client_clone = nostr_client.clone(); @@ -207,22 +206,6 @@ pub async fn stop_nwc(plugin: Plugin, label: &String) { if let Some(wallet_service) = locked_handles.remove(label) { wallet_service.client.shutdown().await; } - - stop_nwc_budget_job(&plugin, label); -} - -pub fn start_nwc_budget_job(plugin: &Plugin, label: String) { - let (tx, rx) = oneshot::channel::<()>(); - tokio::spawn(budget_task(rx, plugin.clone(), label.clone())); - plugin.state().budget_jobs.lock().insert(label, tx); -} - -pub fn stop_nwc_budget_job(plugin: &Plugin, label: &String) { - let mut budget_jobs = plugin.state().budget_jobs.lock(); - let job = budget_jobs.remove(label); - if let Some(j) = job { - let _ = j.send(()); - } } #[allow(clippy::too_many_lines)] diff --git a/src/nwc_balance.rs b/src/nwc_balance.rs index 1c3c1ea..2fde546 100644 --- a/src/nwc_balance.rs +++ b/src/nwc_balance.rs @@ -2,7 +2,10 @@ use cln_plugin::Plugin; use cln_rpc::{model::requests::ListpeerchannelsRequest, primitives::ChannelState}; use nostr::nips::nip47; -use crate::{structs::PluginState, util::load_nwc_store}; +use crate::{ + structs::PluginState, + util::{get_budget_msat, load_nwc_store}, +}; pub async fn get_balance_response( plugin: Plugin, @@ -40,7 +43,7 @@ async fn get_balance( message: e.to_string(), })?; - let balance = if let Some(bdgt_amt) = nwc_store.budget_msat { + let balance = if let Some(bdgt_amt) = get_budget_msat(&nwc_store) { bdgt_amt } else { let listpeerchannels = rpc diff --git a/src/nwc_keysend.rs b/src/nwc_keysend.rs index dc1ee5e..c53c88d 100644 --- a/src/nwc_keysend.rs +++ b/src/nwc_keysend.rs @@ -10,7 +10,14 @@ use nostr::nips::nip47::{self}; use crate::{ structs::{NwcStore, PluginState}, - util::{at_or_above_version, budget_amount_check, load_nwc_store, update_nwc_store}, + util::{ + at_or_above_version, + budget_amount_check, + get_budget_msat, + load_nwc_store, + update_budget_msat, + update_nwc_store, + }, }; pub async fn pay_keysend_response( @@ -65,7 +72,7 @@ async fn pay_keysend( message: e.to_string(), })?; - budget_amount_check(Some(params.amount), None, nwc_store.budget_msat).map_err(|e| { + budget_amount_check(Some(params.amount), None, get_budget_msat(&nwc_store)).map_err(|e| { nip47::NIP47Error { code: nip47::ErrorCode::QuotaExceeded, message: e.to_string(), @@ -120,8 +127,8 @@ async fn xkeysend( .await { Ok(o) => { - if let Some(ref mut bdg) = nwc_store.budget_msat { - *bdg = bdg.saturating_sub(o.amount_sent_msat.msat()); + if nwc_store.budget_msat.is_some() { + update_budget_msat(&mut nwc_store, o.amount_sent_msat.msat()); update_nwc_store(rpc, label, nwc_store) .await .map_err(|e| nip47::NIP47Error { @@ -200,8 +207,8 @@ async fn keysend( .await { Ok(o) => { - if let Some(ref mut bdg) = nwc_store.budget_msat { - *bdg = bdg.saturating_sub(o.amount_sent_msat.msat()); + if nwc_store.budget_msat.is_some() { + update_budget_msat(&mut nwc_store, o.amount_sent_msat.msat()); update_nwc_store(rpc, label, nwc_store) .await .map_err(|e| nip47::NIP47Error { diff --git a/src/nwc_pay.rs b/src/nwc_pay.rs index 4f4e859..d41210b 100644 --- a/src/nwc_pay.rs +++ b/src/nwc_pay.rs @@ -12,7 +12,14 @@ use nostr::nips::nip47; use crate::{ structs::{NOT_INV_ERR, NwcStore, PluginState}, - util::{at_or_above_version, budget_amount_check, load_nwc_store, update_nwc_store}, + util::{ + at_or_above_version, + budget_amount_check, + get_budget_msat, + load_nwc_store, + update_budget_msat, + update_nwc_store, + }, }; pub async fn pay_invoice_response( @@ -172,17 +179,20 @@ async fn load_nwc_and_check_budget( ) })?; - budget_amount_check(params.amount, Some(invoice_amt_msat), nwc_store.budget_msat).map_err( - |e| { - ( - nip47::NIP47Error { - code: nip47::ErrorCode::QuotaExceeded, - message: e.to_string(), - }, - Some(id.to_owned()), - ) - }, - )?; + budget_amount_check( + params.amount, + Some(invoice_amt_msat), + get_budget_msat(&nwc_store), + ) + .map_err(|e| { + ( + nip47::NIP47Error { + code: nip47::ErrorCode::QuotaExceeded, + message: e.to_string(), + }, + Some(id.to_owned()), + ) + })?; Ok(nwc_store) } @@ -211,8 +221,8 @@ async fn update_budget_and_create_response( preimage: Secret, id: &str, ) -> Result<(nip47::PayInvoiceResponse, Option), (nip47::NIP47Error, Option)> { - if let Some(ref mut bdg) = nwc_store.budget_msat { - *bdg = bdg.saturating_sub(amount_sent_msat); + if nwc_store.budget_msat.is_some() { + update_budget_msat(nwc_store, amount_sent_msat); update_nwc_store(rpc, label, nwc_store.clone()) .await .map_err(|e| { diff --git a/src/parse.rs b/src/parse.rs index b1dffed..efd1e36 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -2,12 +2,12 @@ use std::path::Path; use anyhow::anyhow; use cln_plugin::ConfiguredPlugin; -use cln_rpc::{model::requests::GetinfoRequest, ClnRpc}; +use cln_rpc::{ClnRpc, model::requests::GetinfoRequest}; use nostr::types::RelayUrl; use crate::{ - structs::{PluginState, TimeUnit}, OPT_RELAYS, + structs::{PluginState, TimeUnit}, }; pub async fn read_startup_options( diff --git a/src/rpc.rs b/src/rpc.rs index fea29c7..1aba835 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -15,10 +15,16 @@ use serde_json::json; use crate::{ PLUGIN_NAME, - nwc::{run_nwc, send_nwc_info_event, start_nwc_budget_job, stop_nwc, stop_nwc_budget_job}, + nwc::{run_nwc, send_nwc_info_event, stop_nwc}, parse::parse_time_period, structs::{BudgetIntervalConfig, NwcStore, PluginState}, - util::{build_capabilities, is_read_only_nwc, load_nwc_store, update_nwc_store}, + util::{ + build_capabilities, + get_budget_msat, + is_read_only_nwc, + load_nwc_store, + update_nwc_store, + }, }; pub async fn nwc_create( @@ -62,6 +68,7 @@ pub async fn nwc_create( interval_secs: interval, reset_budget_msat: bgt_msat, last_reset: Timestamp::now().as_secs(), + spend_since_last_reset: 0, }; result.insert("interval_config".to_owned(), serde_json::to_value(&conf)?); Some(conf) @@ -119,8 +126,6 @@ pub async fn nwc_budget( let (label, budget_msat, interval_secs) = parse_full_args(args)?; - stop_nwc_budget_job(&plugin, &label); - let mut nwc_store = load_nwc_store(&mut rpc, &label).await?; let is_old_nwc_read_only = is_read_only_nwc(&nwc_store); @@ -132,6 +137,7 @@ pub async fn nwc_budget( interval_secs: interval, reset_budget_msat: budget, last_reset: Timestamp::now().as_secs(), + spend_since_last_reset: 0, }; nwc_store.interval_config = Some(interval_config.clone()); } else { @@ -146,10 +152,6 @@ pub async fn nwc_budget( update_nwc_store(&mut rpc, &label, nwc_store.clone()).await?; - if nwc_store.interval_config.is_some() { - start_nwc_budget_job(&plugin, label.clone()); - } - if is_old_nwc_read_only != is_new_nwc_read_only { let wallet_keys = Keys::new(SecretKey::from_hex(&nwc_store.walletkey)?); let (method_capabilities, _) = build_capabilities(is_new_nwc_read_only, &plugin); @@ -181,7 +183,8 @@ pub async fn nwc_list( let mut nwcs = Vec::new(); if let Some(lbl) = label { - let nwc_store = load_nwc_store(&mut rpc, &lbl).await?; + let mut nwc_store = load_nwc_store(&mut rpc, &lbl).await?; + nwc_store.budget_msat = get_budget_msat(&nwc_store); let wallet_key = Keys::new(SecretKey::from_hex(&nwc_store.walletkey)?); let client_key = Keys::new(nwc_store.uri.secret.clone()); let mut nwc_json = json!(nwc_store); @@ -206,7 +209,8 @@ pub async fn nwc_list( for datastore in all_stored_nwcs { let label = datastore.key.last().unwrap().to_owned(); - let nwc_store = load_nwc_store(&mut rpc, &label).await?; + let mut nwc_store = load_nwc_store(&mut rpc, &label).await?; + nwc_store.budget_msat = get_budget_msat(&nwc_store); let wallet_key = Keys::new(SecretKey::from_hex(&nwc_store.walletkey)?); let client_key = Keys::new(nwc_store.uri.secret.clone()); let mut nwc_json = json!(nwc_store); diff --git a/src/structs.rs b/src/structs.rs index a60c49a..6018153 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -9,7 +9,6 @@ use nostr::{ use nostr_sdk::client::Client; use parking_lot::Mutex; use serde::{Deserialize, Serialize}; -use tokio::sync::oneshot; use tonic::transport::Channel; use crate::hold::hold_client::HoldClient; @@ -23,7 +22,6 @@ pub struct PluginState { pub config: Arc>, pub handles: Arc>>, pub rpc_lock: Arc>, - pub budget_jobs: Arc>>>, pub hold_client: Arc>>>, } impl PluginState { @@ -32,7 +30,6 @@ impl PluginState { config: Arc::new(Mutex::new(Config::default())), handles: Arc::new(tokio::sync::Mutex::new(HashMap::new())), rpc_lock: Arc::new(tokio::sync::Mutex::new(ClnRpc::new(path).await?)), - budget_jobs: Arc::new(Mutex::new(HashMap::new())), hold_client: Arc::new(Mutex::new(None)), }) } @@ -86,6 +83,7 @@ pub struct BudgetIntervalConfig { pub interval_secs: u64, pub reset_budget_msat: u64, pub last_reset: u64, + pub spend_since_last_reset: u64, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/src/tasks.rs b/src/tasks.rs index 3427834..42c9c61 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -1,67 +1,17 @@ use std::{path::Path, str::FromStr, time::Duration}; -use anyhow::anyhow; use cln_plugin::Plugin; use cln_rpc::{ ClnRpc, model::requests::{DeldatastoreRequest, ListdatastoreRequest}, }; use nostr::types::Timestamp; -use tokio::{sync::oneshot, time}; use crate::{ PLUGIN_NAME, structs::{ID_MAX_AGE, ID_STORE, PluginState}, - util::{load_nwc_store, update_nwc_store}, }; -pub async fn budget_task( - mut rx: oneshot::Receiver<()>, - plugin: Plugin, - label: String, -) -> Result<(), anyhow::Error> { - let mut rpc = ClnRpc::new( - Path::new(&plugin.configuration().lightning_dir).join(&plugin.configuration().rpc_file), - ) - .await?; - loop { - let mut nwc_store = load_nwc_store(&mut rpc, &label).await?; - let interval_config = nwc_store - .interval_config - .as_mut() - .ok_or_else(|| anyhow!("interval_config disappeared!"))?; - let now = Timestamp::now().as_secs(); - log::debug!( - "interval:{} now:{} prev:{}", - interval_config.interval_secs, - now, - interval_config.last_reset - ); - let next_reset = std::cmp::max( - interval_config - .interval_secs - .saturating_sub(now.saturating_sub(interval_config.last_reset)), - 1, - ); - tokio::select! { - _ = &mut rx => { - log::info!("Stopping budget task for {label}"); - break; - } - () = time::sleep(Duration::from_secs(next_reset)) => { - log::info!("Refreshing budget for {label}"); - *nwc_store.budget_msat - .as_mut() - .ok_or_else(||anyhow!("budget_msat missing"))? = interval_config.reset_budget_msat; - interval_config.last_reset = Timestamp::now().as_secs(); - update_nwc_store(&mut rpc, &label, nwc_store).await?; - log::info!("Done refreshing budget for {label}"); - } - } - } - Ok(()) -} - pub async fn cleanup_event_ids(plugin: Plugin) -> Result<(), anyhow::Error> { let mut rpc = ClnRpc::new( Path::new(&plugin.configuration().lightning_dir).join(&plugin.configuration().rpc_file), diff --git a/src/util.rs b/src/util.rs index f6064f7..2012e6c 100644 --- a/src/util.rs +++ b/src/util.rs @@ -17,6 +17,46 @@ use crate::{ structs::{ID_STORE, NwcStore, PluginState}, }; +pub fn get_budget_msat(nwc_store: &NwcStore) -> Option { + match nwc_store.budget_msat { + Some(b) => { + if let Some(conf) = &nwc_store.interval_config { + let now = Timestamp::now().as_secs(); + let spend = if now.saturating_sub(conf.last_reset) >= conf.interval_secs { + 0 + } else { + conf.spend_since_last_reset + }; + Some(conf.reset_budget_msat.saturating_sub(spend)) + } else { + Some(b) + } + } + None => None, + } +} + +pub fn update_budget_msat(nwc_store: &mut NwcStore, amount_spent_msat: u64) { + if let Some(bdg) = nwc_store.budget_msat.as_mut() { + if let Some(conf) = nwc_store.interval_config.as_mut() { + let now = Timestamp::now().as_secs(); + if now.saturating_sub(conf.last_reset) >= conf.interval_secs { + conf.last_reset = now; + conf.spend_since_last_reset = amount_spent_msat; + } else { + conf.spend_since_last_reset = conf + .spend_since_last_reset + .saturating_add(amount_spent_msat); + } + *bdg = conf + .reset_budget_msat + .saturating_sub(conf.spend_since_last_reset); + } else { + *bdg = bdg.saturating_sub(amount_spent_msat); + } + } +} + pub fn budget_amount_check( request_amt_msat: Option, invoice_amt_msat: Option, @@ -224,3 +264,59 @@ fn test_budget_check() { assert!(budget_amount_check(Some(0), Some(0), Some(1)).is_ok()); assert!(budget_amount_check(Some(0), Some(0), Some(0)).is_ok()); } + +#[test] +fn test_budget_interval_helpers() { + use crate::structs::BudgetIntervalConfig; + + let now = Timestamp::now().as_secs(); + let conf = BudgetIntervalConfig { + interval_secs: 10, + reset_budget_msat: 1000, + last_reset: now, + spend_since_last_reset: 0, + }; + let mut store = NwcStore { + uri: nostr::nips::nip47::NostrWalletConnectUri::new( + nostr::key::Keys::generate().public_key(), + vec![], + nostr::key::Keys::generate().secret_key().clone(), + None, + ), + walletkey: "test".to_owned(), + budget_msat: Some(1000), + interval_config: Some(conf), + }; + + assert_eq!(get_budget_msat(&store), Some(1000)); + + update_budget_msat(&mut store, 300); + assert_eq!( + store + .interval_config + .as_ref() + .unwrap() + .spend_since_last_reset, + 300 + ); + assert_eq!(get_budget_msat(&store), Some(700)); + + update_budget_msat(&mut store, 500); + assert_eq!(get_budget_msat(&store), Some(200)); + + store.interval_config.as_mut().unwrap().last_reset = now.saturating_sub(20); + let old_last_reset = store.interval_config.as_ref().unwrap().last_reset; + assert_eq!(get_budget_msat(&store), Some(1000)); + + update_budget_msat(&mut store, 400); + assert!(store.interval_config.as_ref().unwrap().last_reset > old_last_reset); + assert_eq!( + store + .interval_config + .as_ref() + .unwrap() + .spend_since_last_reset, + 400 + ); + assert_eq!(get_budget_msat(&store), Some(600)); +} From f79db424cd12d38a96a8178ce902f25abac994b8 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 01:38:27 +0200 Subject: [PATCH 37/56] determine available methods from help rpc isntead of version string --- src/nwc_keysend.rs | 10 +++------- src/nwc_pay.rs | 23 +++-------------------- src/parse.rs | 35 +++++++++++++++++++++++++++++++---- src/structs.rs | 13 ++++++++++--- src/util.rs | 28 ---------------------------- 5 files changed, 47 insertions(+), 62 deletions(-) diff --git a/src/nwc_keysend.rs b/src/nwc_keysend.rs index c53c88d..1ef56f3 100644 --- a/src/nwc_keysend.rs +++ b/src/nwc_keysend.rs @@ -11,7 +11,6 @@ use nostr::nips::nip47::{self}; use crate::{ structs::{NwcStore, PluginState}, util::{ - at_or_above_version, budget_amount_check, get_budget_msat, load_nwc_store, @@ -20,6 +19,8 @@ use crate::{ }, }; +pub const XKEYSEND_COMMAND: &str = "xkeysend"; + pub async fn pay_keysend_response( plugin: Plugin, params: nip47::PayKeysendRequest, @@ -84,12 +85,7 @@ async fn pay_keysend( message: e.to_string(), })?; - let my_cln_version = plugin.state().config.lock().my_cln_version.clone(); - - if at_or_above_version(&my_cln_version, "26.06").map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Other, - message: e.to_string(), - })? { + if plugin.state().config.lock().has_xkeysend { xkeysend(&mut rpc, params, pubkey, nwc_store, label).await } else { keysend(&mut rpc, params, pubkey, nwc_store, label).await diff --git a/src/nwc_pay.rs b/src/nwc_pay.rs index d41210b..ffc348a 100644 --- a/src/nwc_pay.rs +++ b/src/nwc_pay.rs @@ -13,7 +13,6 @@ use nostr::nips::nip47; use crate::{ structs::{NOT_INV_ERR, NwcStore, PluginState}, util::{ - at_or_above_version, budget_amount_check, get_budget_msat, load_nwc_store, @@ -22,6 +21,8 @@ use crate::{ }, }; +pub const XPAY_COMMAND: &str = "xpay"; + pub async fn pay_invoice_response( plugin: Plugin, params: nip47::PayInvoiceRequest, @@ -63,10 +64,7 @@ async fn pay_invoice( let nwc_store = load_nwc_and_check_budget(&mut rpc, label, ¶ms, invoice_amt_msat, &id).await?; - let my_version = plugin.state().config.lock().clone().my_cln_version; - let use_xpay = check_cln_version(&my_version, &id)?; - - if use_xpay { + if plugin.state().config.lock().has_xpay { pay_with_xpay_full(&mut rpc, params, label, nwc_store, &id).await } else { pay_with_legacy_full(&mut rpc, params, label, nwc_store, &id).await @@ -197,21 +195,6 @@ async fn load_nwc_and_check_budget( Ok(nwc_store) } -fn check_cln_version( - my_version: &str, - id: &str, -) -> Result)> { - at_or_above_version(my_version, "24.11").map_err(|e| { - ( - nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - }, - Some(id.to_owned()), - ) - }) -} - async fn update_budget_and_create_response( rpc: &mut ClnRpc, label: &str, diff --git a/src/parse.rs b/src/parse.rs index efd1e36..1589944 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,12 +1,14 @@ -use std::path::Path; +use std::{collections::HashSet, path::Path}; use anyhow::anyhow; use cln_plugin::ConfiguredPlugin; -use cln_rpc::{ClnRpc, model::requests::GetinfoRequest}; +use cln_rpc::{ClnRpc, model::requests::HelpRequest}; use nostr::types::RelayUrl; use crate::{ OPT_RELAYS, + nwc_keysend::XKEYSEND_COMMAND, + nwc_pay::XPAY_COMMAND, structs::{PluginState, TimeUnit}, }; @@ -33,9 +35,34 @@ pub async fn read_startup_options( Path::new(&plugin.configuration().lightning_dir).join(&plugin.configuration().rpc_file), ) .await?; - let version = rpc.call_typed(&GetinfoRequest {}).await?.version; + let help = rpc.call_typed(&HelpRequest { command: None }).await?; + let mut config = state.config.lock(); - config.my_cln_version = version; + + let mut available_commands = HashSet::new(); + for command in &help.help { + if let Some(method) = command.command.split_ascii_whitespace().next() { + available_commands.insert(method); + } + } + + log::debug!( + "Found {} commands available: {}", + available_commands.len(), + available_commands + .iter() + .copied() + .collect::>() + .join(" ") + ); + + config.has_xkeysend = available_commands.contains(XKEYSEND_COMMAND); + config.has_xpay = available_commands.contains(XPAY_COMMAND); + log::debug!( + "Using xpay:{} xkeysend:{}", + config.has_xpay, + config.has_xkeysend + ); for relay in relays_str { log::debug!("RELAY:{relay}"); config.relays.push(RelayUrl::parse(&relay)?); diff --git a/src/structs.rs b/src/structs.rs index 6018153..1ebfb85 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,4 +1,9 @@ -use std::{collections::HashMap, path::PathBuf, str::FromStr, sync::Arc}; +use std::{ + collections::{HashMap, HashSet}, + path::PathBuf, + str::FromStr, + sync::Arc, +}; use cln_rpc::ClnRpc; use nostr::{ @@ -44,13 +49,15 @@ pub struct WalletService { #[derive(Clone, Debug)] pub struct Config { pub relays: Vec, - pub my_cln_version: String, + pub has_xkeysend: bool, + pub has_xpay: bool, } impl Config { pub fn default() -> Config { Config { relays: Vec::new(), - my_cln_version: String::new(), + has_xkeysend: false, + has_xpay: false, } } } diff --git a/src/util.rs b/src/util.rs index 2012e6c..20fa53f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -154,34 +154,6 @@ pub async fn save_event_id( Ok(()) } -pub fn at_or_above_version(my_version: &str, min_version: &str) -> Result { - let clean_start_my_version = my_version - .split_once('v') - .ok_or_else(|| anyhow!("Could not find v in version string"))? - .1; - let full_clean_my_version: String = clean_start_my_version - .chars() - .take_while(|x| x.is_ascii_digit() || *x == '.') - .collect(); - - let my_version_parts: Vec<&str> = full_clean_my_version.split('.').collect(); - let min_version_parts: Vec<&str> = min_version.split('.').collect(); - - if my_version_parts.len() <= 1 || my_version_parts.len() > 3 { - return Err(anyhow!("Version string parse error: {my_version}")); - } - for (my, min) in my_version_parts.iter().zip(min_version_parts.iter()) { - let my_num: u32 = my.parse()?; - let min_num: u32 = min.parse()?; - - if my_num != min_num { - return Ok(my_num > min_num); - } - } - - Ok(my_version_parts.len() >= min_version_parts.len()) -} - pub fn build_capabilities(is_read_only: bool, plugin: &Plugin) -> (String, String) { let holdinvoice_support = plugin.state().hold_client.lock().is_some(); From c1bb7606cfa5662de65d56bd13517c6d0a5fc0aa Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 12:12:32 +0200 Subject: [PATCH 38/56] reserve budget during in-flight payments, drop lock while paying --- src/main.rs | 11 ++ src/nwc_keysend.rs | 258 ++++++++++++++++++++++------------------ src/nwc_pay.rs | 205 ++++++++++++++++--------------- src/rpc.rs | 1 + src/structs.rs | 10 +- src/util.rs | 134 ++++++++++++++++++++- tests/test_cln-nip47.py | 47 +++++--- 7 files changed, 425 insertions(+), 241 deletions(-) diff --git a/src/main.rs b/src/main.rs index b88c6e3..10c925c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -200,6 +200,17 @@ async fn load_nwcs(plugin: Plugin, rpc: &mut ClnRpc) -> Result<(), } } + // We don't keep track of inflight payments succeeding + // while the plugin is dynamically restarted + if nwc_store.reserved_msat != 0 { + log::warn!( + "Releasing {} msat leftover budget reservation for {label}", + nwc_store.reserved_msat + ); + nwc_store.reserved_msat = 0; + update_nwc_store(rpc, label, nwc_store.clone()).await?; + } + run_nwc(plugin.clone(), label.clone(), nwc_store.clone()).await?; } Ok(()) diff --git a/src/nwc_keysend.rs b/src/nwc_keysend.rs index 1ef56f3..8cc8f39 100644 --- a/src/nwc_keysend.rs +++ b/src/nwc_keysend.rs @@ -3,19 +3,23 @@ use std::{collections::HashMap, str::FromStr}; use cln_plugin::Plugin; use cln_rpc::{ ClnRpc, + RpcError, model::requests::{KeysendRequest, XkeysendRequest}, - primitives::{Amount, PublicKey, TlvEntry, TlvStream}, + primitives::{Amount, PublicKey, Secret, TlvEntry, TlvStream}, }; use nostr::nips::nip47::{self}; use crate::{ - structs::{NwcStore, PluginState}, + structs::PluginState, util::{ budget_amount_check, get_budget_msat, load_nwc_store, - update_budget_msat, - update_nwc_store, + payment_fee_reserve_msat, + refund_budget, + reserve_budget, + rpc_socket_path, + settle_budget, }, }; @@ -57,8 +61,6 @@ async fn pay_keysend( params: nip47::PayKeysendRequest, label: &str, ) -> Result { - let mut rpc = plugin.state().rpc_lock.lock().await; - if params.preimage.is_some() { return Err(nip47::NIP47Error { code: nip47::ErrorCode::Other, @@ -66,42 +68,99 @@ async fn pay_keysend( }); } - let nwc_store = load_nwc_store(&mut rpc, label) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - })?; - - budget_amount_check(Some(params.amount), None, get_budget_msat(&nwc_store)).map_err(|e| { - nip47::NIP47Error { - code: nip47::ErrorCode::QuotaExceeded, - message: e.to_string(), - } - })?; - let pubkey = PublicKey::from_str(¶ms.pubkey).map_err(|e| nip47::NIP47Error { code: nip47::ErrorCode::Other, message: e.to_string(), })?; - if plugin.state().config.lock().has_xkeysend { - xkeysend(&mut rpc, params, pubkey, nwc_store, label).await + let reservation = { + let mut rpc = plugin.state().rpc_lock.lock().await; + + let nwc_store = load_nwc_store(&mut rpc, label) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + + budget_amount_check(Some(params.amount), None, get_budget_msat(&nwc_store)).map_err( + |e| nip47::NIP47Error { + code: nip47::ErrorCode::QuotaExceeded, + message: e.to_string(), + }, + )?; + + // Reserve amount plus worst case fee so concurrent payments can never + // exceed the budget. + if get_budget_msat(&nwc_store).unwrap_or(u64::MAX) + < params + .amount + .saturating_add(payment_fee_reserve_msat(params.amount)) + { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::QuotaExceeded, + message: "Payment and estimated fees exceed the available budget".to_owned(), + }); + } + + reserve_budget(&mut rpc, label, &nwc_store, params.amount) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })? + }; + + let has_xkeysend = plugin.state().config.lock().has_xkeysend; + let mut pay_rpc = + ClnRpc::new(rpc_socket_path(&plugin)) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: format!("Could not connect to lightningd: {e}"), + })?; + + let pay_result = if has_xkeysend { + xkeysend(&mut pay_rpc, ¶ms, pubkey).await } else { - keysend(&mut rpc, params, pubkey, nwc_store, label).await + keysend(&mut pay_rpc, ¶ms, pubkey).await + }; + + match pay_result { + Ok((amount_sent_msat, amount_msat, preimage)) => { + let mut rpc = plugin.state().rpc_lock.lock().await; + if let Err(e) = settle_budget(&mut rpc, label, reservation, amount_sent_msat).await { + log::error!("Error updating budget after successful keysend: {e}"); + } + + let preimage = hex::encode(preimage.to_vec()); + let fees_paid = amount_sent_msat.saturating_sub(amount_msat); + + Ok(nip47::PayKeysendResponse { + preimage, + fees_paid: Some(fees_paid), + }) + } + Err(e) => { + let mut rpc = plugin.state().rpc_lock.lock().await; + if let Err(refund_err) = refund_budget(&mut rpc, label, reservation).await { + log::error!( + "Error refunding budget reservation after failed keysend: {refund_err}" + ); + } + Err(map_keysend_error(&e, has_xkeysend)) + } } } async fn xkeysend( - rpc: &mut ClnRpc, - params: nip47::PayKeysendRequest, + pay_rpc: &mut ClnRpc, + params: &nip47::PayKeysendRequest, pubkey: PublicKey, - mut nwc_store: NwcStore, - label: &str, -) -> Result { +) -> Result<(u64, u64, Secret), RpcError> { let mut extratlvs = HashMap::with_capacity(params.tlv_records.len()); - for tlv in params.tlv_records { - extratlvs.insert(tlv.tlv_type.to_string(), tlv.value); + for tlv in ¶ms.tlv_records { + extratlvs.insert(tlv.tlv_type.to_string(), tlv.value.clone()); } let extratlvs = if extratlvs.is_empty() { None @@ -109,7 +168,7 @@ async fn xkeysend( Some(extratlvs) }; - match rpc + let o = pay_rpc .call_typed(&XkeysendRequest { extratlvs, label: None, @@ -120,62 +179,24 @@ async fn xkeysend( amount_msat: Amount::from_msat(params.amount), destination: pubkey, }) - .await - { - Ok(o) => { - if nwc_store.budget_msat.is_some() { - update_budget_msat(&mut nwc_store, o.amount_sent_msat.msat()); - update_nwc_store(rpc, label, nwc_store) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - })?; - } + .await?; - let preimage = hex::encode(o.payment_preimage.to_vec()); - - let fees_paid = o.amount_sent_msat.msat() - o.amount_msat.msat(); - - Ok(nip47::PayKeysendResponse { - preimage, - fees_paid: Some(fees_paid), - }) - } - Err(e) => match e.code { - Some(c) => match c { - 203 | 205 | 207 | 219 => Err(nip47::NIP47Error { - code: nip47::ErrorCode::PaymentFailed, - message: e.to_string(), - }), - 209 => Err(nip47::NIP47Error { - code: nip47::ErrorCode::Other, - message: e.to_string(), - }), - _ => Err(nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - }), - }, - None => Err(nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - }), - }, - } + Ok(( + o.amount_sent_msat.msat(), + o.amount_msat.msat(), + o.payment_preimage, + )) } async fn keysend( - rpc: &mut ClnRpc, - params: nip47::PayKeysendRequest, + pay_rpc: &mut ClnRpc, + params: &nip47::PayKeysendRequest, pubkey: PublicKey, - mut nwc_store: NwcStore, - label: &str, -) -> Result { +) -> Result<(u64, u64, Secret), RpcError> { let mut extratlvs = TlvStream { entries: Vec::new(), }; - for tlv in params.tlv_records { + for tlv in ¶ms.tlv_records { extratlvs.entries.push(TlvEntry { typ: tlv.tlv_type, value: tlv.value.as_bytes().to_owned(), @@ -187,7 +208,7 @@ async fn keysend( Some(extratlvs) }; - match rpc + let o = pay_rpc .call_typed(&KeysendRequest { exemptfee: None, extratlvs, @@ -200,47 +221,48 @@ async fn keysend( amount_msat: Amount::from_msat(params.amount), destination: pubkey, }) - .await - { - Ok(o) => { - if nwc_store.budget_msat.is_some() { - update_budget_msat(&mut nwc_store, o.amount_sent_msat.msat()); - update_nwc_store(rpc, label, nwc_store) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - })?; - } + .await?; - let preimage = hex::encode(o.payment_preimage.to_vec()); + Ok(( + o.amount_sent_msat.msat(), + o.amount_msat.msat(), + o.payment_preimage, + )) +} - let fees_paid = o.amount_sent_msat.msat() - o.amount_msat.msat(); +fn map_keysend_error(e: &RpcError, is_xkeysend: bool) -> nip47::NIP47Error { + let Some(c) = e.code else { + return nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + }; + }; - Ok(nip47::PayKeysendResponse { - preimage, - fees_paid: Some(fees_paid), - }) + let failed_codes = if is_xkeysend { + vec![203, 205, 207, 219] + } else { + vec![203, 205, 210] + }; + + if failed_codes.contains(&c) { + nip47::NIP47Error { + code: nip47::ErrorCode::PaymentFailed, + message: e.to_string(), + } + } else if is_xkeysend && c == 209 { + nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: e.to_string(), + } + } else if !is_xkeysend && c == 206 { + nip47::NIP47Error { + code: nip47::ErrorCode::InsufficientBalance, + message: e.to_string(), + } + } else { + nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), } - Err(e) => match e.code { - Some(c) => match c { - 203 | 205 | 210 => Err(nip47::NIP47Error { - code: nip47::ErrorCode::PaymentFailed, - message: e.to_string(), - }), - 206 => Err(nip47::NIP47Error { - code: nip47::ErrorCode::InsufficientBalance, - message: e.to_string(), - }), - _ => Err(nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - }), - }, - None => Err(nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - }), - }, } } diff --git a/src/nwc_pay.rs b/src/nwc_pay.rs index ffc348a..9ff8635 100644 --- a/src/nwc_pay.rs +++ b/src/nwc_pay.rs @@ -16,8 +16,11 @@ use crate::{ budget_amount_check, get_budget_msat, load_nwc_store, - update_budget_msat, - update_nwc_store, + payment_fee_reserve_msat, + refund_budget, + reserve_budget, + rpc_socket_path, + settle_budget, }, }; @@ -53,21 +56,92 @@ async fn pay_invoice( params: nip47::PayInvoiceRequest, label: &str, ) -> Result<(nip47::PayInvoiceResponse, Option), (nip47::NIP47Error, Option)> { - let mut rpc = plugin.state().rpc_lock.lock().await; + let (id, reservation) = { + let mut rpc = plugin.state().rpc_lock.lock().await; - let decoded_invoice = decode_and_validate_invoice(&mut rpc, ¶ms).await?; + let decoded_invoice = decode_and_validate_invoice(&mut rpc, ¶ms).await?; - let id = get_payment_id(¶ms, &decoded_invoice)?; + let id = get_payment_id(¶ms, &decoded_invoice)?; - let invoice_amt_msat = get_invoice_amount_msat(&decoded_invoice, &id)?; + let invoice_amt_msat = get_invoice_amount_msat(&decoded_invoice, &id)?; - let nwc_store = - load_nwc_and_check_budget(&mut rpc, label, ¶ms, invoice_amt_msat, &id).await?; + let nwc_store = + load_nwc_and_check_budget(&mut rpc, label, ¶ms, invoice_amt_msat, &id).await?; - if plugin.state().config.lock().has_xpay { - pay_with_xpay_full(&mut rpc, params, label, nwc_store, &id).await + // Reserve the invoice amount plus the worst case fee so that no + // combination of concurrent payments can exceed the budget and so that + // balance queries during the payment reflect the reserved amount. + if get_budget_msat(&nwc_store).unwrap_or(u64::MAX) + < invoice_amt_msat.saturating_add(payment_fee_reserve_msat(invoice_amt_msat)) + { + return Err(( + nip47::NIP47Error { + code: nip47::ErrorCode::QuotaExceeded, + message: "Payment and estimated fees exceed the available budget".to_owned(), + }, + Some(id), + )); + } + + let reservation = reserve_budget(&mut rpc, label, &nwc_store, invoice_amt_msat) + .await + .map_err(|e| { + ( + nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + }, + Some(id.clone()), + ) + })?; + + (id, reservation) + }; + + let has_xpay = plugin.state().config.lock().has_xpay; + + let mut pay_rpc = ClnRpc::new(rpc_socket_path(&plugin)).await.map_err(|e| { + ( + nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: format!("Could not connect to lightningd: {e}"), + }, + Some(id.clone()), + ) + })?; + + let pay_result = if has_xpay { + pay_with_xpay(&mut pay_rpc, ¶ms).await } else { - pay_with_legacy_full(&mut rpc, params, label, nwc_store, &id).await + pay_with_legacy(&mut pay_rpc, ¶ms).await + }; + + match pay_result { + Ok((amount_sent_msat, amount_msat, preimage)) => { + let mut rpc = plugin.state().rpc_lock.lock().await; + if let Err(e) = settle_budget(&mut rpc, label, reservation, amount_sent_msat).await { + log::error!("Error updating budget after successful payment: {e}"); + } + + let preimage_str = hex::encode(preimage.to_vec()); + let fees_paid = amount_sent_msat.saturating_sub(amount_msat); + Ok(( + nip47::PayInvoiceResponse { + preimage: preimage_str, + fees_paid: Some(fees_paid), + }, + Some(id), + )) + } + Err(e) => { + let mut rpc = plugin.state().rpc_lock.lock().await; + if let Err(refund_err) = refund_budget(&mut rpc, label, reservation).await { + log::error!( + "Error refunding budget reservation after failed payment: {refund_err}" + ); + } + Err(map_cln_error_to_nip47(&e, &id, has_xpay)) + } } } @@ -195,41 +269,6 @@ async fn load_nwc_and_check_budget( Ok(nwc_store) } -async fn update_budget_and_create_response( - rpc: &mut ClnRpc, - label: &str, - nwc_store: &mut NwcStore, - amount_sent_msat: u64, - amount_msat: u64, - preimage: Secret, - id: &str, -) -> Result<(nip47::PayInvoiceResponse, Option), (nip47::NIP47Error, Option)> { - if nwc_store.budget_msat.is_some() { - update_budget_msat(nwc_store, amount_sent_msat); - update_nwc_store(rpc, label, nwc_store.clone()) - .await - .map_err(|e| { - ( - nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - }, - Some(id.to_owned()), - ) - })?; - } - - let preimage_str = hex::encode(preimage.to_vec()); - let fees_paid = amount_sent_msat - amount_msat; - Ok(( - nip47::PayInvoiceResponse { - preimage: preimage_str, - fees_paid: Some(fees_paid), - }, - Some(id.to_owned()), - )) -} - fn map_cln_error_to_nip47( e: &RpcError, id: &str, @@ -292,14 +331,11 @@ fn map_cln_error_to_nip47( } } -async fn pay_with_xpay_full( - rpc: &mut ClnRpc, - params: nip47::PayInvoiceRequest, - label: &str, - mut nwc_store: NwcStore, - id: &str, -) -> Result<(nip47::PayInvoiceResponse, Option), (nip47::NIP47Error, Option)> { - let payment_result = rpc +async fn pay_with_xpay( + pay_rpc: &mut ClnRpc, + params: &nip47::PayInvoiceRequest, +) -> Result<(u64, u64, Secret), RpcError> { + let payment_result = pay_rpc .call_typed(&XpayRequest { amount_msat: params.amount.map(Amount::from_msat), maxdelay: None, @@ -307,39 +343,26 @@ async fn pay_with_xpay_full( partial_msat: None, retry_for: None, layers: None, - invstring: params.invoice, + invstring: params.invoice.clone(), payer_note: None, dev_use_shadow: None, label: None, localinvreqid: None, }) - .await - .map_err(|e| map_cln_error_to_nip47(&e, id, true))?; + .await?; - let amount_sent_msat = payment_result.amount_sent_msat.msat(); - let amount_msat = payment_result.amount_msat.msat(); - let preimage = payment_result.payment_preimage; - - update_budget_and_create_response( - rpc, - label, - &mut nwc_store, - amount_sent_msat, - amount_msat, - preimage, - id, - ) - .await + Ok(( + payment_result.amount_sent_msat.msat(), + payment_result.amount_msat.msat(), + payment_result.payment_preimage, + )) } -async fn pay_with_legacy_full( - rpc: &mut ClnRpc, - params: nip47::PayInvoiceRequest, - label: &str, - mut nwc_store: NwcStore, - id: &str, -) -> Result<(nip47::PayInvoiceResponse, Option), (nip47::NIP47Error, Option)> { - let payment_result = rpc +async fn pay_with_legacy( + pay_rpc: &mut ClnRpc, + params: &nip47::PayInvoiceRequest, +) -> Result<(u64, u64, Secret), RpcError> { + let payment_result = pay_rpc .call_typed(&PayRequest { amount_msat: params.amount.map(Amount::from_msat), description: None, @@ -353,23 +376,13 @@ async fn pay_with_legacy_full( retry_for: None, riskfactor: None, exclude: None, - bolt11: params.invoice, + bolt11: params.invoice.clone(), }) - .await - .map_err(|e| map_cln_error_to_nip47(&e, id, false))?; + .await?; - let amount_sent_msat = payment_result.amount_sent_msat.msat(); - let amount_msat = payment_result.amount_msat.msat(); - let preimage = payment_result.payment_preimage; - - update_budget_and_create_response( - rpc, - label, - &mut nwc_store, - amount_sent_msat, - amount_msat, - preimage, - id, - ) - .await + Ok(( + payment_result.amount_sent_msat.msat(), + payment_result.amount_msat.msat(), + payment_result.payment_preimage, + )) } diff --git a/src/rpc.rs b/src/rpc.rs index 1aba835..f00cc04 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -83,6 +83,7 @@ pub async fn nwc_create( walletkey: wallet_keys.secret_key().to_secret_hex(), budget_msat, interval_config, + reserved_msat: 0, }; rpc.call_typed(&DatastoreRequest { diff --git a/src/structs.rs b/src/structs.rs index 1ebfb85..c66de4e 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,9 +1,4 @@ -use std::{ - collections::{HashMap, HashSet}, - path::PathBuf, - str::FromStr, - sync::Arc, -}; +use std::{collections::HashMap, path::PathBuf, str::FromStr, sync::Arc}; use cln_rpc::ClnRpc; use nostr::{ @@ -90,6 +85,7 @@ pub struct BudgetIntervalConfig { pub interval_secs: u64, pub reset_budget_msat: u64, pub last_reset: u64, + #[serde(default)] pub spend_since_last_reset: u64, } @@ -101,4 +97,6 @@ pub struct NwcStore { pub budget_msat: Option, #[serde(skip_serializing_if = "Option::is_none")] pub interval_config: Option, + #[serde(default)] + pub reserved_msat: u64, } diff --git a/src/util.rs b/src/util.rs index 20fa53f..2467ab1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,8 @@ +use std::{ + cmp::max, + path::{Path, PathBuf}, +}; + use anyhow::anyhow; use cln_plugin::Plugin; use cln_rpc::{ @@ -17,20 +22,29 @@ use crate::{ structs::{ID_STORE, NwcStore, PluginState}, }; +/// CLN's default maximum fee for a payment is `max(5000msat, 1% of amount)`, +/// so reserving this much guarantees a settled payment never exceeds the budget. +pub const MIN_FEE_RESERVE_MSAT: u64 = 5_000; + +pub fn payment_fee_reserve_msat(amount_msat: u64) -> u64 { + max(MIN_FEE_RESERVE_MSAT, amount_msat.saturating_div(100)) +} + pub fn get_budget_msat(nwc_store: &NwcStore) -> Option { match nwc_store.budget_msat { Some(b) => { - if let Some(conf) = &nwc_store.interval_config { + let available = if let Some(conf) = &nwc_store.interval_config { let now = Timestamp::now().as_secs(); let spend = if now.saturating_sub(conf.last_reset) >= conf.interval_secs { 0 } else { conf.spend_since_last_reset }; - Some(conf.reset_budget_msat.saturating_sub(spend)) + conf.reset_budget_msat.saturating_sub(spend) } else { - Some(b) - } + b + }; + Some(available.saturating_sub(nwc_store.reserved_msat)) } None => None, } @@ -128,6 +142,72 @@ pub async fn update_nwc_store( Ok(()) } +pub fn rpc_socket_path(plugin: &Plugin) -> PathBuf { + Path::new(&plugin.configuration().lightning_dir).join(&plugin.configuration().rpc_file) +} + +/// Reserve the payment amount plus the worst case fee so that no combination +/// of concurrent payments can exceed the budget. Must be called while holding +/// the global rpc lock. Returns the reserved amount (0 if there is no budget). +pub async fn reserve_budget( + rpc: &mut ClnRpc, + label: &str, + nwc_store: &NwcStore, + amount_msat: u64, +) -> Result { + if nwc_store.budget_msat.is_none() { + return Ok(0); + } + + let reservation = amount_msat.saturating_add(payment_fee_reserve_msat(amount_msat)); + let mut new_store = nwc_store.clone(); + new_store.reserved_msat = new_store.reserved_msat.saturating_add(reservation); + update_nwc_store(rpc, label, new_store).await?; + + Ok(reservation) +} + +/// Release the reservation of a payment that did not succeed. Must be called +/// while holding the global rpc lock. +pub async fn refund_budget( + rpc: &mut ClnRpc, + label: &str, + reservation: u64, +) -> Result<(), anyhow::Error> { + if reservation == 0 { + return Ok(()); + } + + let mut nwc_store = load_nwc_store(rpc, label).await?; + nwc_store.reserved_msat = nwc_store.reserved_msat.saturating_sub(reservation); + update_nwc_store(rpc, label, nwc_store).await?; + + Ok(()) +} + +/// Record the actual amount spent by a successful payment and release the +/// remainder of the reservation. Must be called while holding the global rpc +/// lock. Charges the real spend first so that an error leaves the budget +/// conservatively low rather than too high. +pub async fn settle_budget( + rpc: &mut ClnRpc, + label: &str, + reservation: u64, + amount_spent_msat: u64, +) -> Result<(), anyhow::Error> { + // A zero reservation only happens when the nwc has no budget at all. + if reservation == 0 { + return Ok(()); + } + + let mut nwc_store = load_nwc_store(rpc, label).await?; + update_budget_msat(&mut nwc_store, amount_spent_msat); + nwc_store.reserved_msat = nwc_store.reserved_msat.saturating_sub(reservation); + update_nwc_store(rpc, label, nwc_store).await?; + + Ok(()) +} + pub fn is_read_only_nwc(nwc_store: &NwcStore) -> bool { if let Some(budget_msat) = nwc_store.budget_msat { if budget_msat == 0 && nwc_store.interval_config.is_none() { @@ -258,6 +338,7 @@ fn test_budget_interval_helpers() { walletkey: "test".to_owned(), budget_msat: Some(1000), interval_config: Some(conf), + reserved_msat: 0, }; assert_eq!(get_budget_msat(&store), Some(1000)); @@ -292,3 +373,48 @@ fn test_budget_interval_helpers() { ); assert_eq!(get_budget_msat(&store), Some(600)); } + +#[test] +fn test_payment_fee_reserve() { + // fee reserve is at least 5000msat + assert_eq!(payment_fee_reserve_msat(0), 5000); + assert_eq!(payment_fee_reserve_msat(1000), 5000); + assert_eq!(payment_fee_reserve_msat(499_999), 5000); + // and 1% of the amount above that + assert_eq!(payment_fee_reserve_msat(500_000), 5000); + assert_eq!(payment_fee_reserve_msat(1_000_000), 10_000); + assert_eq!(payment_fee_reserve_msat(123_456_789), 1_234_567); +} + +#[test] +fn test_budget_reservations() { + let store = NwcStore { + uri: nostr::nips::nip47::NostrWalletConnectUri::new( + nostr::key::Keys::generate().public_key(), + vec![], + nostr::key::Keys::generate().secret_key().clone(), + None, + ), + walletkey: "test".to_owned(), + budget_msat: Some(10_000), + interval_config: None, + reserved_msat: 0, + }; + + // reserving reduces the available budget + let mut stored = store.clone(); + stored.reserved_msat = 1_500; + assert_eq!(get_budget_msat(&stored), Some(8_500)); + + // a full spend plus reservation can never be under budget + let full_reservation = + stored.clone().budget_msat.unwrap() + payment_fee_reserve_msat(store.budget_msat.unwrap()); + stored.reserved_msat = full_reservation; + assert_eq!(get_budget_msat(&stored), Some(0)); + + // reservations do not affect a store without a budget + let mut no_budget = store; + no_budget.budget_msat = None; + no_budget.reserved_msat = 1_000; + assert_eq!(get_budget_msat(&no_budget), None); +} diff --git a/tests/test_cln-nip47.py b/tests/test_cln-nip47.py index 7e055ec..cb2b6dd 100644 --- a/tests/test_cln-nip47.py +++ b/tests/test_cln-nip47.py @@ -394,7 +394,7 @@ async def test_pay_keysend(nostr_relay, node_factory, get_plugin): # noqa: F811 {"log-level": "debug"}, ], ) - uri_str = l1.rpc.call("nip47-create", ["test1", 3000])["uri"] + uri_str = l1.rpc.call("nip47-create", ["test1", 8000])["uri"] LOGGER.info(uri_str) uri = NostrWalletConnectUri.parse(uri_str) client = Client() @@ -416,7 +416,7 @@ async def test_pay_keysend(nostr_relay, node_factory, get_plugin): # noqa: F811 await nwc.pay_keysend( PayKeysendRequest( id="id123", - amount=2001, + amount=7500, pubkey=l2.info["id"], preimage=None, tlv_records=[KeysendTlvRecord(tlv_type=1234, value="a5c7e3d9b")], @@ -428,7 +428,7 @@ async def test_pay_keysend(nostr_relay, node_factory, get_plugin): # noqa: F811 await nwc.pay_keysend( PayKeysendRequest( id="id123", - amount=2001, + amount=7500, pubkey=l2.info["id"], preimage="or3ijro3ijroi", tlv_records=[KeysendTlvRecord(tlv_type=1234, value="a5c7e3d9b")], @@ -957,7 +957,7 @@ async def test_pay_invoice(nostr_relay, node_factory, get_plugin): # noqa: F811 {"log-level": "debug"}, ], ) - uri_str = l1.rpc.call("nip47-create", ["test1", 3001])["uri"] + uri_str = l1.rpc.call("nip47-create", ["test1", 9000])["uri"] uri = NostrWalletConnectUri.parse(uri_str) client = Client() await client.add_relay(RelayUrl.parse(url)) @@ -985,7 +985,7 @@ async def test_pay_invoice(nostr_relay, node_factory, get_plugin): # noqa: F811 ) invoice = l2.rpc.call( "invoice", - {"label": generate_random_label(), "description": "test3", "amount_msat": 2}, + {"label": generate_random_label(), "description": "test3", "amount_msat": 6500}, ) with pytest.raises(NostrSdkError.Generic, match="Payment exceeds budget"): await nwc.pay_invoice( @@ -1009,7 +1009,7 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 {"log-level": "debug"}, ], ) - uri_str = l1.rpc.call("nip47-create", ["test1", 3000])["uri"] + uri_str = l1.rpc.call("nip47-create", ["test1", 8000])["uri"] LOGGER.info(uri_str) invoice = l2.rpc.call( "invoice", @@ -1038,7 +1038,7 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 invoice = l2.rpc.call( "invoice", - {"label": generate_random_label(), "description": "test1", "amount_msat": 1}, + {"label": generate_random_label(), "description": "test1", "amount_msat": 5500}, ) with pytest.raises(NostrSdkError.Generic, match="Payment exceeds budget"): await nwc.pay_invoice( @@ -1064,7 +1064,7 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 revoke = l1.rpc.call("nip47-revoke", ["test1"]) assert revoke["revoked"] == "test1" - uri_str = l1.rpc.call("nip47-create", ["test1", 3000, "10sec"])["uri"] + uri_str = l1.rpc.call("nip47-create", ["test1", 8000, "10sec"])["uri"] uri = NostrWalletConnectUri.parse(uri_str) client = Client() await client.add_relay(RelayUrl.parse(url)) @@ -1078,7 +1078,11 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 ) invoice_exceeded = l2.rpc.call( "invoice", - {"label": generate_random_label(), "description": "test1", "amount_msat": 3000}, + {"label": generate_random_label(), "description": "test1", "amount_msat": 5500}, + ) + invoice_fee_exceeded = l2.rpc.call( + "invoice", + {"label": generate_random_label(), "description": "test1", "amount_msat": 1}, ) result = await nwc.pay_invoice( PayInvoiceRequest(id=None, amount=None, invoice=invoice["bolt11"]) @@ -1086,17 +1090,26 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 assert result.preimage is not None list = l1.rpc.call("nip47-list", ["test1"])[0] - assert list["test1"]["budget_msat"] == 0 + assert list["test1"]["budget_msat"] == 5000 with pytest.raises(NostrSdkError.Generic, match="Payment exceeds budget"): await nwc.pay_invoice( PayInvoiceRequest(id=None, amount=None, invoice=invoice_exceeded["bolt11"]) ) + with pytest.raises( + NostrSdkError.Generic, + match="Payment and estimated fees exceed the available budget", + ): + await nwc.pay_invoice( + PayInvoiceRequest( + id=None, amount=None, invoice=invoice_fee_exceeded["bolt11"] + ) + ) await asyncio.sleep(11) list = l1.rpc.call("nip47-list", ["test1"])[0] - assert list["test1"]["budget_msat"] == 3000 + assert list["test1"]["budget_msat"] == 8000 invoice = l2.rpc.call( "invoice", @@ -1108,7 +1121,7 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 assert result.preimage is not None list = l1.rpc.call("nip47-list", ["test1"])[0] - assert list["test1"]["budget_msat"] == 0 + assert list["test1"]["budget_msat"] == 5000 with pytest.raises(NostrSdkError.Generic, match="Payment exceeds budget"): await nwc.pay_invoice( @@ -1136,7 +1149,7 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 await asyncio.sleep(11) list = l1.rpc.call("nip47-list", ["test1"])[0] - assert list["test1"]["budget_msat"] == 3000 + assert list["test1"]["budget_msat"] == 8000 @pytest.mark.asyncio @@ -1184,9 +1197,9 @@ async def test_budget_command(nostr_relay, node_factory, get_plugin): # noqa: F PayInvoiceRequest(id=None, amount=None, invoice=invoice["bolt11"]) ) - l1.rpc.call("nip47-budget", ["test1", 5000, "15s"]) + l1.rpc.call("nip47-budget", ["test1", 10000, "15s"]) balance = await nwc.get_balance() - assert balance.balance == 5000 + assert balance.balance == 10000 with pytest.raises( RpcError, match="`budget_msat` must be greater than 0 if you use `interval`" @@ -1199,7 +1212,7 @@ async def test_budget_command(nostr_relay, node_factory, get_plugin): # noqa: F assert pay.preimage is not None balance = await nwc.get_balance() - assert balance.balance == 0 + assert balance.balance == 5000 get_info = await nwc.get_info() assert get_info.methods == [ @@ -1230,7 +1243,7 @@ async def test_budget_command(nostr_relay, node_factory, get_plugin): # noqa: F await asyncio.sleep(18) balance = await nwc.get_balance() - assert balance.balance == 5000 + assert balance.balance == 10000 l1.rpc.call("nip47-budget", ["test1", 0]) balance = await nwc.get_balance() From 29c1ace33c1f052df6babed68f9dbbef06d0159b Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 13:31:41 +0200 Subject: [PATCH 39/56] bound list_transactions work to prevent DoS list_transactions was a DoS vector: it loaded the full invoice and payment history, made one decode rpc-call per entry, and re-serialized the whole result set for every trimmed entry (O(n^2)), all while holding the global rpc lock that serializes every other nwc request and command. - run list_transactions on a dedicated rpc connection so it no longer stalls the rest of the plugin - page backwards through listinvoices by created_index instead of fetching the whole history, using the wait rpc to get the newest index - only decode the kept candidates and honor the from/until and unpaid filters while collecting them - cap work per request at MAX_TRANSACTIONS (500) regardless of node history size - limit pays and hold invoices to the same bound before decoding - make trim_to_size a single serialization pass instead of O(n^2) --- src/nwc_lookups.rs | 560 ++++++++++++++++++++++++++++++++------------- 1 file changed, 403 insertions(+), 157 deletions(-) diff --git a/src/nwc_lookups.rs b/src/nwc_lookups.rs index 32fe39e..80d76b8 100644 --- a/src/nwc_lookups.rs +++ b/src/nwc_lookups.rs @@ -4,7 +4,15 @@ use cln_plugin::Plugin; use cln_rpc::{ ClnRpc, model::{ - requests::{DecodeRequest, ListinvoicesRequest, ListpaysRequest}, + requests::{ + DecodeRequest, + ListinvoicesIndex, + ListinvoicesRequest, + ListpaysRequest, + WaitIndexname, + WaitRequest, + WaitSubsystem, + }, responses::{ DecodeResponse, DecodeType, @@ -22,8 +30,16 @@ use tonic::transport::Channel; use crate::{ hold::{self, ListRequest, hold_client::HoldClient, list_request::Constraint}, structs::{NOT_INV_ERR, PluginState}, + util::rpc_socket_path, }; +#[allow(clippy::doc_markdown)] +/// Hard bound on the number of transactions a single `list_transactions` +/// request will fetch, decode and return (DoS protection). +const MAX_TRANSACTIONS: usize = 500; +/// Bound on the size of a `list_transactions` response in bytes. +const RESPONSE_LIMIT_BYTES: usize = 127 * 1024; + pub async fn lookup_invoice_response( plugin: Plugin, params: nip47::LookupInvoiceRequest, @@ -367,7 +383,37 @@ async fn list_transactions( plugin: Plugin, params: nip47::ListTransactionsRequest, ) -> Result, nip47::NIP47Error> { - let mut rpc = plugin.state().rpc_lock.lock().await; + if params.limit == Some(0) { + return Ok(Vec::new()); + } + + let mut rpc = ClnRpc::new(rpc_socket_path(&plugin)) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: format!("Could not connect to lightningd: {e}"), + })?; + + let limit = usize::try_from(params.limit.unwrap_or(MAX_TRANSACTIONS as u64)).map_err(|e| { + nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: format!("32-bit usize limit exceeded: {e}"), + } + })?; + let offset = usize::try_from(params.offset.unwrap_or(0)).map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: format!("32-bit usize limit exceeded: {e}"), + })?; + let want = limit.saturating_add(offset); + // Enough candidates from each source to satisfy the requested window even + // if some get filtered out again by `from`/`until`/`unpaid`. + let per_source = want + .saturating_mul(2) + .saturating_add(16) + .min(MAX_TRANSACTIONS); + let from = params.from.map(|t| t.as_secs()); + let until = params.until.map(|t| t.as_secs()); + let unpaid = params.unpaid.unwrap_or(false); let (query_invoices, query_payments) = match params.transaction_type { Some(t) => match t { @@ -377,70 +423,29 @@ async fn list_transactions( None => (true, true), }; - let unpaid = params.unpaid.unwrap_or(false); - let mut transactions: Vec = Vec::new(); + if query_invoices { - let list_invoices = rpc - .call_typed(&ListinvoicesRequest { - index: None, - invstring: None, - label: None, - limit: None, - offer_id: None, - payment_hash: None, - start: None, - }) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - })? - .invoices; + let invoices = collect_invoices(&mut rpc, per_source, from, until, unpaid).await?; + transactions.extend(invoices); - for list_invoice in list_invoices { - if !unpaid && list_invoice.status == ListinvoicesInvoicesStatus::UNPAID { - continue; - } - - match make_lookup_response_from_listinvoices(&mut rpc, list_invoice).await { - Ok(t) => transactions.push(t), - Err(_e) => (), - } - } - - let holdinvoice_support = plugin.state().hold_client.lock().is_some(); - - if holdinvoice_support { - let mut hold_client = plugin.state().hold_client.lock().clone().unwrap(); - list_holdinvoices_to_transactions(&mut hold_client, &mut rpc, &mut transactions) - .await?; + let hold_client = plugin.state().hold_client.lock().clone(); + if let Some(mut hold_client) = hold_client { + list_holdinvoices_to_transactions( + &mut hold_client, + &mut rpc, + per_source, + from, + until, + &mut transactions, + ) + .await?; } } if query_payments { - let list_pays = rpc - .call_typed(&ListpaysRequest { - bolt11: None, - index: None, - limit: None, - payment_hash: None, - start: None, - status: None, - }) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - })? - .pays; - - for list_pay in list_pays { - match make_lookup_response_from_listpays(&mut rpc, list_pay).await { - Ok(t) => transactions.push(t), - Err(_e) => (), - } - } + let pays = collect_pays(&mut rpc, per_source, from, until).await?; + transactions.extend(pays); } transactions.sort_by_key(|t| Reverse(t.created_at)); @@ -463,14 +468,211 @@ async fn list_transactions( } } - transactions = trim_to_size(transactions, 127 * 1024); + transactions = trim_to_size(transactions, RESPONSE_LIMIT_BYTES); Ok(transactions) } +async fn max_invoice_created_index(rpc: &mut ClnRpc) -> Result, cln_rpc::RpcError> { + let current_index = rpc + .call_typed(&WaitRequest { + indexname: WaitIndexname::CREATED, + subsystem: WaitSubsystem::INVOICES, + nextvalue: 0, + }) + .await? + .created + .ok_or_else(|| cln_rpc::RpcError { + code: Some(-32700), + message: "Missing created field in wait response".to_owned(), + data: None, + })?; + + if current_index == 0 { + return Ok(None); + } + Ok(Some(current_index)) +} + +async fn first_invoice_created_index(rpc: &mut ClnRpc) -> Result { + let first_index = rpc + .call_typed(&ListinvoicesRequest { + label: None, + invstring: None, + payment_hash: None, + offer_id: None, + index: Some(ListinvoicesIndex::CREATED), + start: Some(0), + limit: Some(1), + }) + .await? + .invoices + .first() + .map_or(0, |i| i.created_index); + Ok(first_index) +} + +/// Collect up to `per_source` most recent paid/public invoices within +/// `[from, until]` by paging backwards through `created_index`, decoding only +/// what is needed. Bounded by `MAX_TRANSACTIONS` regardless of node history. +async fn collect_invoices( + rpc: &mut ClnRpc, + per_source: usize, + from: Option, + until: Option, + unpaid: bool, +) -> Result, nip47::NIP47Error> { + const PAGE: u32 = 200; + const MAX_RAW_ROWS: usize = MAX_TRANSACTIONS * 8; + + let Some(max_idx) = max_invoice_created_index(rpc) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })? + else { + return Ok(Vec::new()); + }; + + let first_index = first_invoice_created_index(rpc) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + + let mut out = Vec::new(); + let mut raw_seen = 0usize; + let mut start_excl = max_idx.saturating_add(1); + + loop { + let page_start = start_excl.saturating_sub(u64::from(PAGE)); + let rows = rpc + .call_typed(&ListinvoicesRequest { + index: Some(ListinvoicesIndex::CREATED), + invstring: None, + label: None, + limit: Some(PAGE), + offer_id: None, + payment_hash: None, + start: Some(page_start), + }) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })? + .invoices; + + // Process newest first within the page. + for row in rows.into_iter().rev() { + raw_seen += 1; + if raw_seen > MAX_RAW_ROWS { + return Ok(out); + } + + if row.created_index >= start_excl { + continue; + } + + if !unpaid && row.status == ListinvoicesInvoicesStatus::UNPAID { + continue; + } + + match make_lookup_response_from_listinvoices(rpc, row).await { + Ok(tx) => { + let created = tx.created_at.as_secs(); + if let Some(f) = from { + // Newest first, so everything older is also out of range. + if created < f { + return Ok(out); + } + } + if let Some(u) = until { + if created > u { + continue; + } + } + out.push(tx); + if out.len() >= per_source { + return Ok(out); + } + } + Err(_e) => (), + } + } + + if page_start == 0 || page_start < first_index { + break; + } + start_excl = page_start; + } + + Ok(out) +} + +/// Collect up to `per_source` most recent payments within `[from, until]`, +/// decoding only the kept subset. `listpays` returns `created_at` directly, so +/// the whole payment history can be filtered before any decode. +async fn collect_pays( + rpc: &mut ClnRpc, + per_source: usize, + from: Option, + until: Option, +) -> Result, nip47::NIP47Error> { + let pays = rpc + .call_typed(&ListpaysRequest { + bolt11: None, + index: None, + limit: None, + payment_hash: None, + start: None, + status: None, + }) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })? + .pays; + + // Newest first. + let mut pays = pays; + pays.sort_by_key(|p| Reverse((p.created_at, p.created_index.unwrap_or(0)))); + + let mut out = Vec::new(); + for pay in pays { + if out.len() >= per_source { + break; + } + if let Some(u) = until { + if pay.created_at > u { + continue; + } + } + if let Some(f) = from { + // Newest first, so the rest are also out of range. + if pay.created_at < f { + break; + } + } + + match make_lookup_response_from_listpays(rpc, pay).await { + Ok(tx) => out.push(tx), + Err(_e) => (), + } + } + + Ok(out) +} + async fn list_holdinvoices_to_transactions( hold_client: &mut HoldClient, rpc: &mut ClnRpc, + per_source: usize, + from: Option, + until: Option, transactions: &mut Vec, ) -> Result<(), nip47::NIP47Error> { let lookup_request = ListRequest { constraint: None }; @@ -484,98 +686,136 @@ async fn list_holdinvoices_to_transactions( })? .into_inner(); - for hold_invoice in hold_lookup.invoices { - let invoice_decoded = rpc - .call_typed(&DecodeRequest { - string: hold_invoice.invoice.clone(), - }) - .await - .map_err(|e| nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: e.to_string(), - })?; + // Decode only the most recent `per_source` hold invoices (newest first). + let mut hold_invoices = hold_lookup.invoices; + hold_invoices.sort_by_key(|inv| Reverse(inv.created_at)); - let description = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => invoice_decoded.offer_description, - DecodeType::BOLT11_INVOICE => invoice_decoded.description, - _ => continue, - }; - let description_hash = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => None, - DecodeType::BOLT11_INVOICE => invoice_decoded.description_hash.map(|h| h.to_string()), - _ => continue, - }; - - let created_at = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => { - Timestamp::from_secs(invoice_decoded.invoice_created_at.unwrap()) + let mut kept = 0usize; + for hold_invoice in hold_invoices { + if kept >= per_source { + break; + } + if let Some(u) = until { + if hold_invoice.created_at > u { + continue; } - DecodeType::BOLT11_INVOICE => Timestamp::from_secs(invoice_decoded.created_at.unwrap()), - _ => continue, - }; - - let amount = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => invoice_decoded.invoice_amount_msat.unwrap().msat(), - DecodeType::BOLT11_INVOICE => { - if let Some(amt) = invoice_decoded.amount_msat { - amt.msat() - } else { - // amount: `any` but have to put a value... - 0 - } + } + if let Some(f) = from { + // Newest first, so the rest are also out of range. + if hold_invoice.created_at < f { + break; } - _ => continue, - }; + } - let expires_at = match invoice_decoded.item_type { - DecodeType::BOLT12_INVOICE => invoice_decoded - .invoice_relative_expiry - .map(|e_at| created_at + Timestamp::from_secs(u64::from(e_at))), - DecodeType::BOLT11_INVOICE => invoice_decoded - .expiry - .map(|e_at| created_at + Timestamp::from_secs(e_at)), - _ => continue, - }; - - let state = match hold_invoice.state() { - hold::InvoiceState::Unpaid => nip47::TransactionState::Pending, - hold::InvoiceState::Accepted => nip47::TransactionState::Accepted, - hold::InvoiceState::Paid => nip47::TransactionState::Settled, - hold::InvoiceState::Cancelled => nip47::TransactionState::Expired, - }; - - let settled_at = if hold_invoice.settled_at() != 0 { - Some(Timestamp::from_secs(hold_invoice.settled_at())) - } else { - None - }; - - let preimage = if hold_invoice.state() == hold::InvoiceState::Paid { - Some(hex::encode(hold_invoice.preimage())) - } else { - None - }; - - transactions.push(nip47::LookupInvoiceResponse { - transaction_type: Some(nip47::TransactionType::Incoming), - invoice: Some(hold_invoice.invoice.clone()), - description, - description_hash, - preimage, - payment_hash: hex::encode(hold_invoice.payment_hash), - amount, - fees_paid: 0, - created_at, - expires_at, - settled_at, - metadata: None, - state: Some(state), - }); + match make_lookup_response_from_holdinvoice(rpc, &hold_invoice).await { + Ok(tx) => { + transactions.push(tx); + kept += 1; + } + Err(_e) => (), + } } Ok(()) } +async fn make_lookup_response_from_holdinvoice( + rpc: &mut ClnRpc, + hold_invoice: &hold::Invoice, +) -> Result { + let not_invoice_err = Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: NOT_INV_ERR.to_owned(), + }); + + let invoice_decoded = rpc + .call_typed(&DecodeRequest { + string: hold_invoice.invoice.clone(), + }) + .await + .map_err(|e| nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: e.to_string(), + })?; + + let description = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded.offer_description, + DecodeType::BOLT11_INVOICE => invoice_decoded.description, + _ => return not_invoice_err, + }; + let description_hash = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => None, + DecodeType::BOLT11_INVOICE => invoice_decoded.description_hash.map(|h| h.to_string()), + _ => return not_invoice_err, + }; + + let created_at = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => { + Timestamp::from_secs(invoice_decoded.invoice_created_at.unwrap()) + } + DecodeType::BOLT11_INVOICE => Timestamp::from_secs(invoice_decoded.created_at.unwrap()), + _ => return not_invoice_err, + }; + + let amount = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded.invoice_amount_msat.unwrap().msat(), + DecodeType::BOLT11_INVOICE => { + if let Some(amt) = invoice_decoded.amount_msat { + amt.msat() + } else { + // amount: `any` but have to put a value... + 0 + } + } + _ => return not_invoice_err, + }; + + let expires_at = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => invoice_decoded + .invoice_relative_expiry + .map(|e_at| created_at + Timestamp::from_secs(u64::from(e_at))), + DecodeType::BOLT11_INVOICE => invoice_decoded + .expiry + .map(|e_at| created_at + Timestamp::from_secs(e_at)), + _ => return not_invoice_err, + }; + + let state = match hold_invoice.state() { + hold::InvoiceState::Unpaid => nip47::TransactionState::Pending, + hold::InvoiceState::Accepted => nip47::TransactionState::Accepted, + hold::InvoiceState::Paid => nip47::TransactionState::Settled, + hold::InvoiceState::Cancelled => nip47::TransactionState::Expired, + }; + + let settled_at = if hold_invoice.settled_at() != 0 { + Some(Timestamp::from_secs(hold_invoice.settled_at())) + } else { + None + }; + + let preimage = if hold_invoice.state() == hold::InvoiceState::Paid { + Some(hex::encode(hold_invoice.preimage())) + } else { + None + }; + + Ok(nip47::LookupInvoiceResponse { + transaction_type: Some(nip47::TransactionType::Incoming), + invoice: Some(hold_invoice.invoice.clone()), + description, + description_hash, + preimage, + payment_hash: hex::encode(hold_invoice.payment_hash.clone()), + amount, + fees_paid: 0, + created_at, + expires_at, + settled_at, + metadata: None, + state: Some(state), + }) +} + async fn make_lookup_response_from_listinvoices( rpc: &mut ClnRpc, list_invoice: ListinvoicesInvoices, @@ -779,30 +1019,36 @@ async fn make_lookup_response_from_listpays( }) } +/// Keep only the newest transactions that fit within `max_size` bytes. +/// Transactions must be sorted newest first. Serializes each transaction only +/// once and chunks the remaining (O(n), not O(n^2)). fn trim_to_size( mut transactions: Vec, max_size: usize, ) -> Vec { - let length_before = transactions.len(); - while !transactions.is_empty() { - match serde_json::to_vec(&transactions) { + let mut total_size = 0usize; + let mut keep = 0usize; + for tx in &transactions { + match serde_json::to_vec(tx) { Ok(serialized) => { - if serialized.len() <= max_size { - log::info!( - "Trimmed {} transactions to stay under {}bytes", - length_before - transactions.len(), - max_size - ); - return transactions; + total_size += serialized.len(); + if total_size > max_size { + break; } - transactions.pop(); } Err(e) => { - log::warn!("Failed to serialize transactions: {e}"); - return transactions; + log::warn!("Failed to serialize transaction: {e}"); + break; } } + keep += 1; } - Vec::new() + let trimmed = transactions.len() - keep; + if trimmed > 0 { + log::info!("Trimmed {trimmed} transactions to stay under {max_size} bytes"); + } + transactions.truncate(keep); + + transactions } From b31f42691f03930d21d8fca2a324c81d4127f6bf Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 14:24:42 +0200 Subject: [PATCH 40/56] fix unaccepted hold invoices in the accepted handler holdinvoice_accepted_handler waited on the hold plugin track stream and sent a HoldInvoiceAccepted notification afterwards, without checking the reason the stream ended. When an invoice was cancelled, the stream just closes and the handler still sent a spurious accepted notification. Only send the notification if the stream actually reported the invoice as accepted. The hold plugin has no expired state: an invoice that expires without ever being accepted stays Unpaid and the track stream never ends, so the handler previously hung forever, holding a task and a gRPC stream open. Pass the invoice's expiry into the handler and time out the track stream at that point (capped), so it gives up on expired invoices and never sends a spurious accepted notification. When re-spawning pending invoices at startup, derive the expiry from the decoded bolt11. Add test_hold_invoice_expiry which creates an invoice with a short expiry and asserts the handler gives up without sending the notification. --- src/main.rs | 48 +++++++++++++++++++++++- src/nwc_hold.rs | 10 ++++- src/nwc_notifications.rs | 52 ++++++++++++++++++++++++-- tests/test_cln-nip47.py | 80 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 182 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 10c925c..d8e6c91 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,13 @@ use cln_plugin::{ Plugin, options::{ConfigOption, DefaultBooleanConfigOption, StringArrayConfigOption}, }; -use cln_rpc::{ClnRpc, model::requests::ListdatastoreRequest}; +use cln_rpc::{ + ClnRpc, + model::{ + requests::{DecodeRequest, ListdatastoreRequest}, + responses::DecodeType, + }, +}; use nostr::nips::nip47; use nwc::run_nwc; use nwc_notifications::{payment_received_handler, payment_sent_handler}; @@ -225,8 +231,47 @@ async fn load_pending_hold_invoices(plugin: Plugin) -> Result<(), a .into_inner() .invoices; + let mut rpc = plugin.state().rpc_lock.lock().await; + for invoice in invoices { if invoice.state() == InvoiceState::Accepted || invoice.state() == InvoiceState::Unpaid { + // Bound the accepted handler by the invoice's expiry. The hold + // plugin has no expired state, so without this it would wait + // forever on an invoice that expires while Unpaid. + let invoice_decoded = rpc + .call_typed(&DecodeRequest { + string: invoice.invoice.clone(), + }) + .await?; + + if !invoice_decoded.valid { + log::warn!( + "Skipping hold invoice {}, could not decode it", + hex::encode(&invoice.payment_hash) + ); + continue; + } + + let (created_at, expiry) = match invoice_decoded.item_type { + DecodeType::BOLT12_INVOICE => ( + invoice_decoded.invoice_created_at, + invoice_decoded.invoice_relative_expiry.map(u64::from), + ), + DecodeType::BOLT11_INVOICE => { + (invoice_decoded.created_at, invoice_decoded.expiry) + } + _ => continue, + }; + + let (Some(created_at), Some(expiry)) = (created_at, expiry) else { + log::warn!( + "Skipping hold invoice {}: missing creation time or expiry", + hex::encode(&invoice.payment_hash) + ); + continue; + }; + let expires_at = created_at.saturating_add(expiry); + log::debug!( "Starting holdinvoice accepted handler for {}", hex::encode(&invoice.payment_hash) @@ -234,6 +279,7 @@ async fn load_pending_hold_invoices(plugin: Plugin) -> Result<(), a tokio::spawn(holdinvoice_accepted_handler( plugin.clone(), invoice.payment_hash, + expires_at, )); } } diff --git a/src/nwc_hold.rs b/src/nwc_hold.rs index c87ce50..4ce23a3 100644 --- a/src/nwc_hold.rs +++ b/src/nwc_hold.rs @@ -107,6 +107,8 @@ async fn make_hold_invoice( })? .into_inner(); + let expires_at = Timestamp::now() + expiry; + let response = nip47::MakeHoldInvoiceResponse { invoice: Some(holdinvoice.bolt11), transaction_type: nip47::TransactionType::Incoming, @@ -114,12 +116,16 @@ async fn make_hold_invoice( description_hash: params.description_hash, amount: params.amount, created_at: Timestamp::now(), - expires_at: Timestamp::now() + expiry, + expires_at, metadata: None, payment_hash: params.payment_hash, }; - tokio::spawn(holdinvoice_accepted_handler(plugin, payment_hash)); + tokio::spawn(holdinvoice_accepted_handler( + plugin, + payment_hash, + expires_at.as_secs(), + )); Ok(response) } diff --git a/src/nwc_notifications.rs b/src/nwc_notifications.rs index 04accbd..4fa1164 100644 --- a/src/nwc_notifications.rs +++ b/src/nwc_notifications.rs @@ -28,6 +28,11 @@ use crate::{ structs::{NOT_INV_ERR, PluginState, WalletService}, }; +/// Upper bound on how long a `holdinvoice_accepted_handler` waits for an +/// invoice to be accepted, so a client cannot pin a task and a gRPC stream +/// open forever with an absurd expiry. +const MAX_HOLD_WAIT_SECS: u64 = 24 * 60 * 60; + pub async fn payment_received_handler( plugin: Plugin, args: serde_json::Value, @@ -396,6 +401,7 @@ async fn send_notification( pub async fn holdinvoice_accepted_handler( plugin: Plugin, payment_hash: Vec, + expires_at: u64, ) -> Result<(), anyhow::Error> { let mut hold_client = plugin.state().hold_client.lock().clone().unwrap(); @@ -404,13 +410,51 @@ pub async fn holdinvoice_accepted_handler( }; let mut track_stream = hold_client.track(track_request).await?.into_inner(); - while let Some(response) = track_stream.message().await? { - log::debug!("Invoice status: {}", response.state().as_str_name()); - if response.state() == InvoiceState::Accepted { - break; + // The hold plugin has no expired state: an invoice that expires without + // being accepted stays in the Unpaid state and the track stream never ends + // on its own. Bound the wait by the invoice's expiry (capped) so we do not + // hold this task and gRPC stream open forever. + let wait_duration = expires_at + .saturating_sub(Timestamp::now().as_secs()) + .min(MAX_HOLD_WAIT_SECS); + let deadline = tokio::time::Instant::now() + tokio::time::Duration::from_secs(wait_duration); + + let mut accepted = false; + loop { + match tokio::time::timeout_at(deadline, track_stream.message()).await { + Err(_elapsed) => { + log::debug!( + "Hold invoice {} expired before being accepted", + hex::encode(&payment_hash) + ); + break; + } + Ok(Err(e)) => return Err(e.into()), + // The stream ended without an Accepted state: the invoice was + // cancelled before it ever got accepted. + Ok(Ok(None)) => break, + Ok(Ok(Some(response))) => { + log::debug!("Invoice status: {}", response.state().as_str_name()); + match response.state() { + InvoiceState::Accepted => { + accepted = true; + break; + } + InvoiceState::Paid | InvoiceState::Cancelled => break, + InvoiceState::Unpaid => (), + } + } } } + if !accepted { + log::debug!( + "Hold invoice {} was not accepted, skipping notification", + hex::encode(&payment_hash) + ); + return Ok(()); + } + let list_request = ListRequest { constraint: Some(Constraint::PaymentHash(payment_hash.clone())), }; diff --git a/tests/test_cln-nip47.py b/tests/test_cln-nip47.py index cb2b6dd..3671804 100644 --- a/tests/test_cln-nip47.py +++ b/tests/test_cln-nip47.py @@ -1294,9 +1294,10 @@ async def test_hold_invoice( "log-level": "debug", "plugin": get_plugin, "important-plugin": get_hold, + "hold-grpc-port": node_factory.get_unused_port(), "nip47-relays": url, "may_reconnect": True, - "broken_log": r"Relay receiver exited with error", + "broken_log": r"Relay receiver exited with error|Connection failed", }, ], ) @@ -1824,3 +1825,80 @@ async def test_hold_invoice( ): hold_events.append(content) assert len(hold_events) == 1 + + +@pytest.mark.asyncio +async def test_hold_invoice_expiry( + node_factory, + get_plugin, # noqa: F811 + get_hold, # noqa: F811 + nostr_relay, +): + url = nostr_relay + l2 = node_factory.get_node( + options={ + "log-level": "debug", + "plugin": get_plugin, + "important-plugin": get_hold, + "hold-grpc-port": node_factory.get_unused_port(), + "nip47-relays": url, + }, + broken_log=r"Relay receiver exited with error|Connection failed", + ) + uri_res = l2.rpc.call("nip47-create", ["test1", 3010]) + uri_str = uri_res["uri"] + client_pubkey = PublicKey.parse(uri_res["clientkey_public"]) + LOGGER.info(uri_str) + uri = NostrWalletConnectUri.parse(uri_str) + + preimage = secrets.token_hex(32) + payment_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() + LOGGER.info(f"preimage: {preimage}") + LOGGER.info(f"payment_hash: {payment_hash}") + content = { + "method": "make_hold_invoice", + "params": { + "amount": 5000, + "payment_hash": payment_hash, + "expiry": 5, + }, + } + content = json.dumps(content) + keys = Keys(uri.secret()) + encrypted_content = keys.nip04_encrypt(uri.public_key(), content) + event = ( + await EventBuilder(Kind(23194), encrypted_content) + .tags([Tag.public_key(uri.public_key())]) + .finalize_async(keys) + ) + client = Client() + relay_url = RelayUrl.parse(url) + await client.add_relay(relay_url) + await client.connect() + + await fetch_info_event(client, uri) + + (responses1, _res) = await fetch_event_responses( + client, client_pubkey, 23195, client.send_event(event), 1 + ) + error_events = [] + success_events = [] + for event in responses1: + LOGGER.info(event) + content = keys.nip04_decrypt(uri.public_key(), event.content()) + content = json.loads(content) + LOGGER.info(content) + if "result" in content and content["result"] is not None: + success_events.append(content) + if "error" in content and content["error"] is not None: + error_events.append(content) + + assert len(success_events) == 1 + assert len(error_events) == 0 + + # The hold plugin has no expired state: an invoice that expires without + # ever being accepted (and without being cancelled) stays `Unpaid` and the + # track stream never ends on its own. The handler must give up at the + # invoice's expiry instead of hanging, and must not send a spurious + # accepted notification. + l2.daemon.wait_for_log("was not accepted, skipping notification", timeout=20) From e175198c45949ed993980f0f3eb961129da602df Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 15:48:20 +0200 Subject: [PATCH 41/56] misc: fix log levels, substraction overflow, invoice valid check --- src/main.rs | 6 ++---- src/nwc.rs | 8 ++++---- src/nwc_lookups.rs | 20 ++++++++++++++++++++ src/nwc_notifications.rs | 6 +++--- src/tasks.rs | 2 +- tests/test_cln-nip47.py | 14 +------------- 6 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/main.rs b/src/main.rs index d8e6c91..f0d23f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -89,7 +89,7 @@ async fn main() -> Result<(), anyhow::Error> { unsafe { std::env::set_var( "CLN_PLUGIN_LOG", - "cln_plugin=info,cln_rpc=info,cln_nip47=debug,info", + "cln_plugin=info,cln_rpc=info,cln_nip47=trace,info", ); }; log_panics::init(); @@ -257,9 +257,7 @@ async fn load_pending_hold_invoices(plugin: Plugin) -> Result<(), a invoice_decoded.invoice_created_at, invoice_decoded.invoice_relative_expiry.map(u64::from), ), - DecodeType::BOLT11_INVOICE => { - (invoice_decoded.created_at, invoice_decoded.expiry) - } + DecodeType::BOLT11_INVOICE => (invoice_decoded.created_at, invoice_decoded.expiry), _ => continue, }; diff --git a/src/nwc.rs b/src/nwc.rs index c761614..7b5ff6d 100644 --- a/src/nwc.rs +++ b/src/nwc.rs @@ -230,7 +230,7 @@ async fn nwc_request_handler( | ClientNotification::Shutdown => return Ok(()), }; - log::debug!("relay_url:{relay_url} subscription_id:{subscription_id} {event:?}"); + log::trace!("relay_url:{relay_url} subscription_id:{subscription_id} {event:?}"); let mut use_nip44 = check_nip44_support(&event); let request = decrypt_request(&event.content, wallet_keys, &client_pubkey, &mut use_nip44)?; @@ -334,7 +334,7 @@ async fn nwc_request_handler( ); continue; } - log::debug!("SENT RESPONSE {response_event:?}"); + log::trace!("SENT RESPONSE {response_event:?}"); } Ok(()) @@ -394,7 +394,7 @@ fn decrypt_request( } } }; - log::debug!("Decrypted (nip44_v2:{use_nip44}):{content}"); + log::trace!("Decrypted (nip44_v2:{use_nip44}):{content}"); let request: nip47::Request = match serde_json::from_str(&content) { Ok(o) => o, Err(e) => { @@ -417,7 +417,7 @@ fn encrypt_response_content( return Err(anyhow!("Error serializing response! {e}")); } }; - log::debug!("RESPONSE:{response_str}"); + log::trace!("RESPONSE:{response_str}"); if use_nip44 { match nip44::encrypt( wallet_keys.secret_key(), diff --git a/src/nwc_lookups.rs b/src/nwc_lookups.rs index 80d76b8..42ce1f6 100644 --- a/src/nwc_lookups.rs +++ b/src/nwc_lookups.rs @@ -245,6 +245,7 @@ async fn lookup_holdinvoice( }) } +#[allow(clippy::too_many_lines)] async fn get_and_decode_holdinvoice( rpc: &mut ClnRpc, hold_client: &mut HoldClient, @@ -289,6 +290,12 @@ async fn get_and_decode_holdinvoice( code: nip47::ErrorCode::Internal, message: e.to_string(), })?; + if !invoice_decoded.valid { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Invalid invoice decoded".to_owned(), + }); + } Ok((hold_invoice, invoice_decoded)) } else { @@ -301,6 +308,13 @@ async fn get_and_decode_holdinvoice( code: nip47::ErrorCode::Internal, message: e.to_string(), })?; + if !invoice_decoded.valid { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Invalid invoice decoded".to_owned(), + }); + } + let ph = match invoice_decoded.item_type { DecodeType::BOLT12_INVOICE => invoice_decoded.invoice_payment_hash.unwrap(), DecodeType::BOLT11_INVOICE => invoice_decoded.payment_hash.unwrap().to_string(), @@ -351,6 +365,12 @@ async fn get_and_decode_holdinvoice( code: nip47::ErrorCode::Internal, message: e.to_string(), })?; + if !invoice_decoded.valid { + return Err(nip47::NIP47Error { + code: nip47::ErrorCode::Other, + message: "Invalid invoice decoded".to_owned(), + }); + } Ok((hold_invoice, invoice_decoded)) } } diff --git a/src/nwc_notifications.rs b/src/nwc_notifications.rs index 4fa1164..964faff 100644 --- a/src/nwc_notifications.rs +++ b/src/nwc_notifications.rs @@ -348,7 +348,7 @@ async fn send_notification( notification: &String, wallet_service: &WalletService, ) -> Result<(), anyhow::Error> { - log::debug!("NOTIFICATION: {notification}"); + log::trace!("NOTIFICATION: {notification}"); let content_encrypted_nip04 = nip04::encrypt( wallet_service.wallet_secret.secret_key(), &wallet_service.client_pubkey, @@ -369,7 +369,7 @@ async fn send_notification( .join(", ") ); } - log::debug!("NIP04 NOTIFICATION SENT: {event_nip04:?}"); + log::trace!("NIP04 NOTIFICATION SENT: {event_nip04:?}"); let content_encrypted_nip44 = nip44::encrypt( wallet_service.wallet_secret.secret_key(), @@ -392,7 +392,7 @@ async fn send_notification( .join(", ") ); } - log::debug!("NIP44 NOTIFICATION SENT: {event_nip44:?}"); + log::trace!("NIP44 NOTIFICATION SENT: {event_nip44:?}"); Ok(()) } diff --git a/src/tasks.rs b/src/tasks.rs index 42c9c61..7a53195 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -33,7 +33,7 @@ pub async fn cleanup_event_ids(plugin: Plugin) -> Result<(), anyhow continue; } let timestamp = Timestamp::from_str(&id.string.unwrap())?; - if now.as_secs() - timestamp.as_secs() > ID_MAX_AGE { + if now.as_secs().saturating_sub(timestamp.as_secs()) > ID_MAX_AGE { rpc.call_typed(&DeldatastoreRequest { generation: None, key: id.key.clone(), diff --git a/tests/test_cln-nip47.py b/tests/test_cln-nip47.py index 3671804..9e225d9 100644 --- a/tests/test_cln-nip47.py +++ b/tests/test_cln-nip47.py @@ -139,7 +139,6 @@ async def test_get_balance(nostr_relay, node_factory, get_plugin): # noqa: F811 wait_for_announce=True, opts=[ { - "log-level": "debug", "plugin": get_plugin, "nip47-relays": url, "broken_log": r"Relay receiver exited with error|Connection failed", @@ -190,7 +189,6 @@ async def test_get_info(nostr_relay, node_factory, get_plugin): # noqa: F811 url = nostr_relay l1 = node_factory.get_node( options={ - "log-level": "debug", "plugin": get_plugin, "nip47-relays": url, }, @@ -296,7 +294,6 @@ async def test_make_invoice(nostr_relay, node_factory, get_plugin): # noqa: F81 url = nostr_relay l1 = node_factory.get_node( options={ - "log-level": "debug", "plugin": get_plugin, "nip47-relays": url, }, @@ -385,7 +382,6 @@ async def test_pay_keysend(nostr_relay, node_factory, get_plugin): # noqa: F811 wait_for_announce=True, opts=[ { - "log-level": "debug", "plugin": get_plugin, "nip47-relays": url, "broken_log": r"Relay receiver exited with error|Connection failed", @@ -444,7 +440,6 @@ async def test_lookup_invoice(nostr_relay, node_factory, get_plugin): # noqa: F wait_for_announce=True, opts=[ { - "log-level": "debug", "plugin": get_plugin, "nip47-relays": url, "broken_log": r"Relay receiver exited with error|Connection failed", @@ -671,7 +666,6 @@ async def test_list_transactions(nostr_relay, node_factory, get_plugin): # noqa wait_for_announce=True, opts=[ { - "log-level": "debug", "plugin": get_plugin, "nip47-relays": url, "broken_log": r"Relay receiver exited with error|Connection failed", @@ -775,13 +769,12 @@ async def test_notifications(nostr_relay, node_factory, get_plugin): # noqa: F8 wait_for_announce=True, opts=[ { - "log-level": "debug", "plugin": get_plugin, "nip47-relays": url, "broken_log": r"Relay receiver exited with error|Connection failed", }, {"log-level": "debug"}, - {"log-level": "debug", "plugin": get_plugin, "nip47-relays": url}, + {"plugin": get_plugin, "nip47-relays": url}, ], ) uri_res = l1.rpc.call("nip47-create", ["test1"]) @@ -949,7 +942,6 @@ async def test_pay_invoice(nostr_relay, node_factory, get_plugin): # noqa: F811 wait_for_announce=True, opts=[ { - "log-level": "debug", "plugin": get_plugin, "nip47-relays": url, "broken_log": r"Relay receiver exited with error|Connection failed", @@ -1001,7 +993,6 @@ async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 wait_for_announce=True, opts=[ { - "log-level": "debug", "plugin": get_plugin, "nip47-relays": url, "broken_log": r"Relay receiver exited with error|Connection failed", @@ -1160,7 +1151,6 @@ async def test_budget_command(nostr_relay, node_factory, get_plugin): # noqa: F wait_for_announce=True, opts=[ { - "log-level": "debug", "plugin": get_plugin, "nip47-relays": url, "broken_log": r"Relay receiver exited with error|Connection failed", @@ -1291,7 +1281,6 @@ async def test_hold_invoice( "may_reconnect": True, }, { - "log-level": "debug", "plugin": get_plugin, "important-plugin": get_hold, "hold-grpc-port": node_factory.get_unused_port(), @@ -1837,7 +1826,6 @@ async def test_hold_invoice_expiry( url = nostr_relay l2 = node_factory.get_node( options={ - "log-level": "debug", "plugin": get_plugin, "important-plugin": get_hold, "hold-grpc-port": node_factory.get_unused_port(), From 2044151b4a610744d9c12b2de89e7d57d699e958 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 16:04:28 +0200 Subject: [PATCH 42/56] use cln-rpc structs for notification deserialization --- src/nwc_notifications.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/nwc_notifications.rs b/src/nwc_notifications.rs index 964faff..ffd1499 100644 --- a/src/nwc_notifications.rs +++ b/src/nwc_notifications.rs @@ -4,6 +4,7 @@ use anyhow::anyhow; use cln_plugin::Plugin; use cln_rpc::{ ClnRpc, + Notification, model::{ requests::{DecodeRequest, ListinvoicesRequest, ListpaysRequest, ListpeerchannelsRequest}, responses::{ @@ -14,6 +15,7 @@ use cln_rpc::{ ListpaysPaysStatus, }, }, + notifications::{InvoicePaymentNotification, SendPaySuccessNotification}, primitives::Sha256, }; use nostr::{ @@ -40,13 +42,12 @@ pub async fn payment_received_handler( if !plugin.option(&OPT_NOTIFICATIONS).unwrap() { return Ok(()); } - let label = args - .get("invoice_payment") - .ok_or_else(|| anyhow!("Malformed invoice_payment notification: missing invoice_payment"))? - .get("label") - .ok_or_else(|| anyhow!("Malformed invoice_payment notification: missing label"))? - .as_str() - .ok_or_else(|| anyhow!("label not a string"))?; + + let notif: Notification = serde_json::from_value(args)?; + let inv_pay_notif: InvoicePaymentNotification = match notif { + Notification::InvoicePayment(invoice_payment_notification) => invoice_payment_notification, + _ => return Err(anyhow!("Wrong notification type, expected invoice_payment")), + }; let mut rpc = plugin.state().rpc_lock.lock().await; @@ -54,7 +55,7 @@ pub async fn payment_received_handler( .call_typed(&ListinvoicesRequest { index: None, invstring: None, - label: Some(label.to_owned()), + label: Some(inv_pay_notif.label), limit: None, offer_id: None, payment_hash: None, @@ -191,14 +192,15 @@ pub async fn payment_sent_handler( if !plugin.option(&OPT_NOTIFICATIONS).unwrap() { return Ok(()); } - let payment_hash = args - .get("sendpay_success") - .ok_or_else(|| anyhow!("Malformed sendpay_success notification: missing sendpay_success"))? - .get("payment_hash") - .ok_or_else(|| anyhow!("Malformed sendpay_success notification: missing payment_hash"))? - .as_str() - .ok_or_else(|| anyhow!("payment_hash not a string"))? - .to_owned(); + + let notif: Notification = serde_json::from_value(args)?; + let send_pay_notif: SendPaySuccessNotification = match notif { + Notification::SendPaySuccess(send_pay_success_notification) => { + send_pay_success_notification + } + _ => return Err(anyhow!("Wrong notification type, expected sendpay_success")), + }; + let payment_hash = hex::encode(send_pay_notif.payment_hash); let mut rpc = plugin.state().rpc_lock.lock().await; From 2fcc48ce34e66b0bd811072532eabf0403efaeb0 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 16:24:05 +0200 Subject: [PATCH 43/56] don't fail sending notifications on the first client error --- src/nwc_notifications.rs | 44 +++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/nwc_notifications.rs b/src/nwc_notifications.rs index ffd1499..61ec787 100644 --- a/src/nwc_notifications.rs +++ b/src/nwc_notifications.rs @@ -77,19 +77,31 @@ pub async fn payment_received_handler( )); }; + let payment_hash_str = hex::encode(invoice.payment_hash); + let invoice_decoded = rpc .call_typed(&DecodeRequest { string: invstring.clone(), }) .await?; + if !invoice_decoded.valid { + return Err(anyhow!("Invalid invoice decoded for {payment_hash_str}")); + } + let notification = make_payment_received_from_listinvoices(invoice, invstring, invoice_decoded)?; let clients = plugin.state().handles.lock().await; for wallet_service in clients.values() { - send_notification(¬ification, wallet_service).await?; + if let Err(e) = send_notification(¬ification, wallet_service).await { + log::warn!( + "Failed sending payment_received notification for {payment_hash_str} \ + to client {}: {e}", + wallet_service.client_pubkey + ); + } } Ok(()) @@ -227,7 +239,13 @@ pub async fn payment_sent_handler( let clients = plugin.state().handles.lock().await; for wallet_service in clients.values() { - send_notification(¬ification, wallet_service).await?; + if let Err(e) = send_notification(¬ification, wallet_service).await { + log::warn!( + "Failed sending payment_sent notification for {payment_hash} to\ + client {}: {e}", + wallet_service.client_pubkey + ); + } } Ok(()) @@ -405,6 +423,8 @@ pub async fn holdinvoice_accepted_handler( payment_hash: Vec, expires_at: u64, ) -> Result<(), anyhow::Error> { + let payment_hash_str = hex::encode(&payment_hash); + let mut hold_client = plugin.state().hold_client.lock().clone().unwrap(); let track_request = TrackRequest { @@ -425,10 +445,7 @@ pub async fn holdinvoice_accepted_handler( loop { match tokio::time::timeout_at(deadline, track_stream.message()).await { Err(_elapsed) => { - log::debug!( - "Hold invoice {} expired before being accepted", - hex::encode(&payment_hash) - ); + log::debug!("Hold invoice {payment_hash_str} expired before being accepted"); break; } Ok(Err(e)) => return Err(e.into()), @@ -450,10 +467,7 @@ pub async fn holdinvoice_accepted_handler( } if !accepted { - log::debug!( - "Hold invoice {} was not accepted, skipping notification", - hex::encode(&payment_hash) - ); + log::debug!("Hold invoice {payment_hash_str} was not accepted, skipping notification"); return Ok(()); } @@ -522,7 +536,7 @@ pub async fn holdinvoice_accepted_handler( .await? .channels; - let payment_hash_hash = Sha256::from_str(&hex::encode(payment_hash))?; + let payment_hash_hash = Sha256::from_str(&payment_hash_str)?; let mut lowest_htlc_expiry = 0; @@ -562,7 +576,13 @@ pub async fn holdinvoice_accepted_handler( let notification = serde_json::to_string(&content).unwrap(); for wallet_service in clients.values() { - send_notification(¬ification, wallet_service).await?; + if let Err(e) = send_notification(¬ification, wallet_service).await { + log::warn!( + "Failed sending hold_invoice_accepted {payment_hash_str} notification\ + to client {}: {e}", + wallet_service.client_pubkey + ); + } } Ok(()) } From 102de7345d93d6b3e49d91d88b44417821906b12 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 16:46:14 +0200 Subject: [PATCH 44/56] fix holdinvoice always having 0 deadline --- src/nwc_notifications.rs | 2 +- tests/test_cln-nip47.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/nwc_notifications.rs b/src/nwc_notifications.rs index 61ec787..302ba96 100644 --- a/src/nwc_notifications.rs +++ b/src/nwc_notifications.rs @@ -538,7 +538,7 @@ pub async fn holdinvoice_accepted_handler( let payment_hash_hash = Sha256::from_str(&payment_hash_str)?; - let mut lowest_htlc_expiry = 0; + let mut lowest_htlc_expiry = u32::MAX; for peer in list_peer_channels { if let Some(htlcs) = peer.htlcs { diff --git a/tests/test_cln-nip47.py b/tests/test_cln-nip47.py index 9e225d9..b463ad6 100644 --- a/tests/test_cln-nip47.py +++ b/tests/test_cln-nip47.py @@ -1376,6 +1376,8 @@ async def test_hold_invoice( assert lookup_hold.state.name == "PENDING" assert lookup_hold.settled_at is None + getinfo = l2.rpc.getinfo() + (responses2, _res) = await fetch_event_responses( client, client_pubkey, @@ -1394,6 +1396,10 @@ async def test_hold_invoice( if content["notification_type"] == "hold_invoice_accepted": hold_events.append(content) assert content["notification"]["payment_hash"] == payment_hash + assert len(hold_events) == 1 + assert ( + hold_events[0]["notification"]["settle_deadline"] > getinfo["blockheight"] + 1 + ) lookup_hold = await nwc.lookup_invoice( LookupInvoiceRequest( From ffd9bd21e961d043f59ca1c1e9b3f1f33532cd34 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 16:57:09 +0200 Subject: [PATCH 45/56] fix legacy keysend tlv byte decoding --- src/nwc_keysend.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/nwc_keysend.rs b/src/nwc_keysend.rs index 8cc8f39..aec724d 100644 --- a/src/nwc_keysend.rs +++ b/src/nwc_keysend.rs @@ -199,7 +199,11 @@ async fn keysend( for tlv in ¶ms.tlv_records { extratlvs.entries.push(TlvEntry { typ: tlv.tlv_type, - value: tlv.value.as_bytes().to_owned(), + value: hex::decode(&tlv.value).map_err(|e| RpcError { + code: Some(-32700), + message: format!("Could not decode tlv bytes: {e}"), + data: None, + })?, }); } let extratlvs = if extratlvs.entries.is_empty() { From ae4e00967d7449b92fb926015e89daf4b3cbfdba Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 17:23:19 +0200 Subject: [PATCH 46/56] fix paying of amount-less invoices with a request amount --- src/nwc_pay.rs | 78 +++++++++++++++++++++++------------------ tests/test_cln-nip47.py | 15 ++++++++ 2 files changed, 58 insertions(+), 35 deletions(-) diff --git a/src/nwc_pay.rs b/src/nwc_pay.rs index 9ff8635..429e0a7 100644 --- a/src/nwc_pay.rs +++ b/src/nwc_pay.rs @@ -63,16 +63,42 @@ async fn pay_invoice( let id = get_payment_id(¶ms, &decoded_invoice)?; - let invoice_amt_msat = get_invoice_amount_msat(&decoded_invoice, &id)?; + let invoice_amt_msat = get_invoice_amount_msat(&decoded_invoice); let nwc_store = load_nwc_and_check_budget(&mut rpc, label, ¶ms, invoice_amt_msat, &id).await?; + let amt_msat = match (params.amount, invoice_amt_msat) { + (None, None) => { + return Err(( + nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: "No amount found in request or invoice".to_owned(), + }, + Some(id.clone()), + )); + } + (None, Some(b)) => b, + (Some(a), None) => a, + (Some(a), Some(b)) => { + if a != b { + return Err(( + nip47::NIP47Error { + code: nip47::ErrorCode::Internal, + message: "request amount does not match invoice amount".to_owned(), + }, + Some(id.clone()), + )); + } + a + } + }; + // Reserve the invoice amount plus the worst case fee so that no // combination of concurrent payments can exceed the budget and so that // balance queries during the payment reflect the reserved amount. if get_budget_msat(&nwc_store).unwrap_or(u64::MAX) - < invoice_amt_msat.saturating_add(payment_fee_reserve_msat(invoice_amt_msat)) + < amt_msat.saturating_add(payment_fee_reserve_msat(amt_msat)) { return Err(( nip47::NIP47Error { @@ -83,7 +109,7 @@ async fn pay_invoice( )); } - let reservation = reserve_budget(&mut rpc, label, &nwc_store, invoice_amt_msat) + let reservation = reserve_budget(&mut rpc, label, &nwc_store, amt_msat) .await .map_err(|e| { ( @@ -215,30 +241,15 @@ fn get_payment_id( Ok(id) } -fn get_invoice_amount_msat( - decoded_invoice: &DecodeResponse, - id: &str, -) -> Result)> { - decoded_invoice - .amount_msat - .as_ref() - .ok_or_else(|| { - ( - nip47::NIP47Error { - code: nip47::ErrorCode::Internal, - message: "Missing amount_msat in decoded invoice".to_owned(), - }, - Some(id.to_owned()), - ) - }) - .map(Amount::msat) +fn get_invoice_amount_msat(decoded_invoice: &DecodeResponse) -> Option { + decoded_invoice.amount_msat.as_ref().map(Amount::msat) } async fn load_nwc_and_check_budget( rpc: &mut ClnRpc, label: &str, params: &nip47::PayInvoiceRequest, - invoice_amt_msat: u64, + invoice_amt_msat: Option, id: &str, ) -> Result)> { let nwc_store = load_nwc_store(rpc, label).await.map_err(|e| { @@ -251,20 +262,17 @@ async fn load_nwc_and_check_budget( ) })?; - budget_amount_check( - params.amount, - Some(invoice_amt_msat), - get_budget_msat(&nwc_store), - ) - .map_err(|e| { - ( - nip47::NIP47Error { - code: nip47::ErrorCode::QuotaExceeded, - message: e.to_string(), - }, - Some(id.to_owned()), - ) - })?; + budget_amount_check(params.amount, invoice_amt_msat, get_budget_msat(&nwc_store)).map_err( + |e| { + ( + nip47::NIP47Error { + code: nip47::ErrorCode::QuotaExceeded, + message: e.to_string(), + }, + Some(id.to_owned()), + ) + }, + )?; Ok(nwc_store) } diff --git a/tests/test_cln-nip47.py b/tests/test_cln-nip47.py index b463ad6..36260fd 100644 --- a/tests/test_cln-nip47.py +++ b/tests/test_cln-nip47.py @@ -984,6 +984,21 @@ async def test_pay_invoice(nostr_relay, node_factory, get_plugin): # noqa: F811 PayInvoiceRequest(id=None, amount=None, invoice=invoice["bolt11"]) ) + invoice = l2.rpc.call( + "invoice", + { + "label": generate_random_label(), + "description": "test1", + "amount_msat": "any", + }, + ) + result = await nwc.pay_invoice( + PayInvoiceRequest(id=None, amount=2, invoice=invoice["bolt11"]) + ) + pay = l1.rpc.call("listpays", {"payment_hash": invoice["payment_hash"]})["pays"][0] + assert result.preimage == pay["preimage"] + assert pay["amount_msat"] == 2 + @pytest.mark.asyncio async def test_persistency(nostr_relay, node_factory, get_plugin): # noqa: F811 From 101234e8ef99e2ee2da551ecba728f1f7f77e660 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 17:25:47 +0200 Subject: [PATCH 47/56] fix error type if payment route was too expensive --- src/nwc_pay.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nwc_pay.rs b/src/nwc_pay.rs index 429e0a7..4799465 100644 --- a/src/nwc_pay.rs +++ b/src/nwc_pay.rs @@ -314,8 +314,8 @@ fn map_cln_error_to_nip47( } else if !is_xpay && c == 206 { ( nip47::NIP47Error { - code: nip47::ErrorCode::InsufficientBalance, - message: e.to_string(), + code: nip47::ErrorCode::PaymentFailed, + message: format!("Route too expensive: {e}"), }, Some(id.to_owned()), ) From a26eb7a5398d7834a16cb3610c572f912f6e4327 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 17:40:50 +0200 Subject: [PATCH 48/56] fix nwc-create rpc erroring if running nwc failed, log instead since we already persist it --- src/parse.rs | 8 ++++---- src/rpc.rs | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/parse.rs b/src/parse.rs index 1589944..e07ea53 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -79,10 +79,10 @@ pub fn parse_time_period(input: &str) -> Result { if let Ok(time_unit) = unit.parse() { match time_unit { TimeUnit::Second => Ok(value), - TimeUnit::Minute => Ok(value * 60), - TimeUnit::Hour => Ok(value * 60 * 60), - TimeUnit::Day => Ok(value * 60 * 60 * 24), - TimeUnit::Week => Ok(value * 60 * 60 * 24 * 7), + TimeUnit::Minute => Ok(value.saturating_mul(60)), + TimeUnit::Hour => Ok(value.saturating_mul(60 * 60)), + TimeUnit::Day => Ok(value.saturating_mul(60 * 60 * 24)), + TimeUnit::Week => Ok(value.saturating_mul(60 * 60 * 24 * 7)), } } else { Err(anyhow!(format!("Unsupported time unit: {unit}"))) diff --git a/src/rpc.rs b/src/rpc.rs index f00cc04..a2c2c38 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -95,7 +95,9 @@ pub async fn nwc_create( }) .await?; - run_nwc(plugin.clone(), label.clone(), nwc_store.clone()).await?; + if let Err(e) = run_nwc(plugin.clone(), label.clone(), nwc_store.clone()).await { + log::warn!("Failed running nwc: {e}"); + } Ok(serde_json::Value::Object(result)) } From 1d5bd7c82150f0be0ab11e6831cee92298cbce6f Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 17:44:13 +0200 Subject: [PATCH 49/56] remove since filter because we now dedup all events by id --- src/nwc.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/nwc.rs b/src/nwc.rs index 7b5ff6d..e6beb81 100644 --- a/src/nwc.rs +++ b/src/nwc.rs @@ -19,7 +19,6 @@ use tokio::time; use crate::{ OPT_NOTIFICATIONS, - STARTUP_DELAY, nwc_balance::get_balance_response, nwc_hold::{ cancel_hold_invoice_response, @@ -105,8 +104,7 @@ pub async fn run_nwc( let filter = Filter::new() .kind(Kind::WalletConnectRequest) - .author(client_keys_clone.public_key()) - .since(Timestamp::now() - STARTUP_DELAY - 1); + .author(client_keys_clone.public_key()); let mut notifications = nostr_client_clone.notifications(); From d93a03014b92f62383bcb7362fcf3dc746994d87 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 17:46:36 +0200 Subject: [PATCH 50/56] fix reuse of retries counter in check_hold_support --- src/main.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index f0d23f3..effac61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -329,12 +329,12 @@ async fn check_hold_support(plugin: Plugin) -> Result<(), anyhow::E cert_dir.to_str().unwrap() ); - let max_retries = 10; - let mut retries = 0; - while retries < max_retries && !do_certificates_exist(&cert_dir) { + let cert_max_retries = 10; + let mut cert_retries = 0; + while cert_retries < cert_max_retries && !do_certificates_exist(&cert_dir) { log::debug!("Hold certificates incomplete. Waiting..."); time::sleep(Duration::from_millis(500)).await; - retries += 1; + cert_retries += 1; } let ca_cert = tokio::fs::read(cert_dir.join("ca.pem")).await?; @@ -355,15 +355,18 @@ async fn check_hold_support(plugin: Plugin) -> Result<(), anyhow::E .keep_alive_while_idle(true) .connect_timeout(Duration::from_secs(5)); - let max_retries = 20; + let chan_max_retries = 20; + let mut chan_retries = 0; let channel = loop { match endpoint.connect().await { Ok(channel) => break channel, - Err(e) if retries < max_retries => { - retries += 1; + Err(e) if chan_retries < chan_max_retries => { + chan_retries += 1; - log::debug!("Hold gRPC server not ready, retrying ({retries}/{max_retries}): {e}"); + log::debug!( + "Hold gRPC server not ready, retrying ({chan_retries}/{chan_max_retries}): {e}" + ); time::sleep(Duration::from_millis(500)).await; } From 7d132a60e4a7543aa9108e6b8e631fc18a473d79 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 17:48:43 +0200 Subject: [PATCH 51/56] add note for global notifications --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2927042..1f1eb7f 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ # cln-nip47 -A core lightning plugin to connect wallets via Nostr Wallet Connect (NWC) as specified in [NIP-47](https://github.com/nostr-protocol/nips/blob/master/47.md). +A core lightning plugin to connect wallets via Nostr Wallet Connect (NWC) as specified in [NIP-47](https://github.com/nostr-protocol/nips/blob/master/47.md). It is intended to be used by a single user, since all notifications got to all NWC's. * [Installation](#installation) * [Building](#building) From e98d4a1a54ec9fdf5e723a95a6c30fdce2532790 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 17:56:45 +0200 Subject: [PATCH 52/56] refactor read to receive in code to match docs --- src/main.rs | 2 +- src/util.rs | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index effac61..1c3323a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,7 +63,7 @@ const OPT_NOTIFICATIONS: DefaultBooleanConfigOption = ConfigOption::new_bool_wit "Enable/disable nip47-notifications. Default is `true`", ); pub const PLUGIN_NAME: &str = "cln-nip47"; -pub const WALLET_READ_METHODS: [nip47::Method; 5] = [ +pub const WALLET_READ_OR_RECEIVE_METHODS: [nip47::Method; 5] = [ nip47::Method::MakeInvoice, nip47::Method::LookupInvoice, nip47::Method::ListTransactions, diff --git a/src/util.rs b/src/util.rs index 2467ab1..2543e55 100644 --- a/src/util.rs +++ b/src/util.rs @@ -18,7 +18,7 @@ use crate::{ WALLET_HOLD_NOTIFICATIONS, WALLET_NOTIFICATIONS, WALLET_PAY_METHODS, - WALLET_READ_METHODS, + WALLET_READ_OR_RECEIVE_METHODS, structs::{ID_STORE, NwcStore, PluginState}, }; @@ -234,11 +234,13 @@ pub async fn save_event_id( Ok(()) } -pub fn build_capabilities(is_read_only: bool, plugin: &Plugin) -> (String, String) { +pub fn build_capabilities(is_receive_only: bool, plugin: &Plugin) -> (String, String) { let holdinvoice_support = plugin.state().hold_client.lock().is_some(); - let mut methods = WALLET_READ_METHODS.map(|m| m.to_string()).join(" "); - if !is_read_only { + let mut methods = WALLET_READ_OR_RECEIVE_METHODS + .map(|m| m.to_string()) + .join(" "); + if !is_receive_only { methods.push(' '); methods.push_str(WALLET_PAY_METHODS.map(|m| m.to_string()).join(" ").as_str()); } @@ -274,10 +276,13 @@ pub fn build_capabilities(is_read_only: bool, plugin: &Plugin) -> ( (methods, notifications) } -pub fn build_methods_vec(is_read_only: bool, plugin: &Plugin) -> Vec { +pub fn build_methods_vec( + is_receive_only: bool, + plugin: &Plugin, +) -> Vec { let holdinvoice_support = plugin.state().hold_client.lock().is_some(); - let mut methods = WALLET_READ_METHODS.to_vec(); - if !is_read_only { + let mut methods = WALLET_READ_OR_RECEIVE_METHODS.to_vec(); + if !is_receive_only { methods.extend_from_slice(&WALLET_PAY_METHODS); } if holdinvoice_support { From cc7d075bf46a251092716569037e97d991c7e126 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 18:11:28 +0200 Subject: [PATCH 53/56] CHANGELOG update --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08b1dbf..0b21d0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,22 @@ ### Changed - `pay_keysend` will use the new CLN `xkeysend` command using CLN v26.06+ +- Budget is updated on demand instead of by a background task +- `list_transactions` is bounded (at most 500 transactions and ~128kB response) to prevent DoS +- Upgrade ``nostr_sdk`` and CLN dependencies ### Removed - ``multi_pay_invoice`` and ``multi_pay_keysend``, they were removed from the spec +### Fixed +- ``nip47-create`` no longer fails if the NWC fails to start, the error is only logged, since the NWC at that point is already persisted to the DB +- Paying amount-less invoices with a request amount +- Legacy keysend TLV byte decoding +- Report the correct error when the payment route is too expensive +- Notifications are sent per client, so one failing client no longer prevents the others from receiving them +- Available payment methods are now determined from the node's RPC instead of it's (custom) version string +- Deduplication of request events instead of filtering unreliably + ## [0.1.9] 2026-04-23 ### Fixed From 7241ae39d3135f1c99c04b6b7d054bd523c8bf0b Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 18:14:12 +0200 Subject: [PATCH 54/56] bump to version 0.2.0 --- CHANGELOG.md | 2 +- Cargo.lock | 2 +- Cargo.toml | 2 +- coffee.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b21d0a..f8097c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## [0.2.0] 2026-08-09 ### Added - holdinvoice methods: ``make_hold_invoice``, ``cancel_hold_invoice``, ``settle_hold_invoice`` diff --git a/Cargo.lock b/Cargo.lock index 20ea17f..a6393f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -311,7 +311,7 @@ dependencies = [ [[package]] name = "cln-nip47" -version = "0.1.9" +version = "0.2.0" dependencies = [ "anyhow", "cln-plugin", diff --git a/Cargo.toml b/Cargo.toml index 42419e6..a21e37a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cln-nip47" -version = "0.1.9" +version = "0.2.0" edition = "2024" rust-version = "1.85.0" diff --git a/coffee.yml b/coffee.yml index 980549a..fa75d76 100644 --- a/coffee.yml +++ b/coffee.yml @@ -1,6 +1,6 @@ plugin: name: cln-nip47 - version: 0.1.9 + version: 0.2.0 lang: rust install: | cargo build --release && cp target/release/cln-nip47 . && cargo clean From 7c100cf5d28cd5d1298de810f9c24832e881f69c Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Sun, 9 Aug 2026 20:07:11 +0200 Subject: [PATCH 55/56] add default relays --- CHANGELOG.md | 1 + README.md | 7 ++++++- src/nwc.rs | 6 ++++++ src/parse.rs | 25 +++++++++++++------------ 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8097c3..e77df0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - holdinvoice notification: ``hold_invoice_accepted`` ### Changed +- there are now five default relays that will be used if ``nip47-relays`` is not set - `pay_keysend` will use the new CLN `xkeysend` command using CLN v26.06+ - Budget is updated on demand instead of by a background task - `list_transactions` is bounded (at most 500 transactions and ~128kB response) to prevent DoS diff --git a/README.md b/README.md index 1f1eb7f..5556fd0 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,12 @@ It is highly recommended to use your own private relay since public relays may l For a private relay you can for example use [nostr-rs-relay](https://github.com/scsibug/nostr-rs-relay) with ``pubkey_whitelist`` set to both ``clientkey_public`` and ``walletkey_public`` (returned from ``nip47-create``/``nip47-list``). ## Options -* ``nip47-relays``: Specify the relays that you want to use with your NWC. Can be set multiple times to use multiple relays. NWC's you create will save these and even if you add or remove relays keep the relays from the moment you created that NWC. You must set this atleast one time. +* ``nip47-relays``: Specify the relays that you want to use with your NWC. Can be set multiple times to use multiple relays. NWC's you create will save these and even if you add or remove relays keep the relays from the moment you created that NWC. If you don't set this yourself these default relays will be used: + * ``wss://nos.lol`` + * ``wss://relay.primal.net`` + * ``wss://relay.getalby.com/v1`` + * ``wss://relay.nostr.net`` + * ``wss://relay.snort.social`` * ``nip47-notifications``: Enable/disable nip47 notifications. Default is enabled (``true``) ## Methods diff --git a/src/nwc.rs b/src/nwc.rs index e6beb81..dfd0e2c 100644 --- a/src/nwc.rs +++ b/src/nwc.rs @@ -332,6 +332,12 @@ async fn nwc_request_handler( ); continue; } + for success in send_result.success { + log::trace!("Sent to {}", success.0); + } + for failure in send_result.failed { + log::trace!("Failed to send to {}: {}", failure.0, failure.1); + } log::trace!("SENT RESPONSE {response_event:?}"); } diff --git a/src/parse.rs b/src/parse.rs index e07ea53..a8f9768 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -16,19 +16,20 @@ pub async fn read_startup_options( plugin: &ConfiguredPlugin, state: &PluginState, ) -> Result<(), anyhow::Error> { - let relays_str = if let Some(relays) = plugin.option(&OPT_RELAYS).unwrap() { - if relays.is_empty() { - return Err(anyhow!( - "Empty `{}` option, must specify atleast one relay url!", - OPT_RELAYS.name() - )); - } - relays + let relays_str = if plugin + .option(&OPT_RELAYS) + .unwrap() + .is_none_or(|v| v.is_empty()) + { + vec![ + "wss://nos.lol".to_owned(), + "wss://relay.primal.net".to_owned(), + "wss://relay.getalby.com/v1".to_owned(), + "wss://relay.nostr.net".to_owned(), + "wss://relay.snort.social".to_owned(), + ] } else { - return Err(anyhow!( - "`{}` not set, must specify atleast one relay url!", - OPT_RELAYS.name() - )); + plugin.option(&OPT_RELAYS).unwrap().unwrap() }; let mut rpc = ClnRpc::new( From 27768bb95d4b0d4a65b1f7f7c46a8637d4feea73 Mon Sep 17 00:00:00 2001 From: daywalker90 Date: Wed, 12 Aug 2026 00:17:36 +0200 Subject: [PATCH 56/56] CI: fix mkdir error --- .github/workflows/ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 71402f9..3b0ca26 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,9 +44,7 @@ jobs: - name: Create cache paths run: | - sudo mkdir /usr/local/libexec - sudo mkdir /usr/local/libexec/c-lightning - sudo mkdir /usr/local/libexec/c-lightning/plugins + sudo mkdir -p /usr/local/libexec/c-lightning/plugins sudo chown -R $USER /usr/local/libexec - name: Set up Python ${{ matrix.python-version }}