diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 0484f65..103d97a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -23,12 +23,10 @@ jobs: uses: actions/checkout@v4 - name: Install nix - uses: cachix/install-nix-action@v25 - with: - install_url: https://releases.nixos.org/nix/nix-2.20.1/install + uses: cachix/install-nix-action@v30 - name: Set up cachix - uses: cachix/cachix-action@v14 + uses: cachix/cachix-action@v15 with: name: holochain-ci diff --git a/CHANGELOG.md b/CHANGELOG.md index c2723bb..b0f2715 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,3 +4,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + +### Changed + +- Rewritten as a canonical client for **Holochain 0.6**. The conductor wire + protocol is implemented directly: msgpack framing, the `{type, value}` + envelope, and fully-typed admin/app request and response models. +- Zome-call signing is now done in-language with `msgpack` + `hashlib.sha512` + + `PyNaCl`, dropping the archived native `holochain-serialization` dependency. +- Response deserialization into typed models (HoloHashes, `CellId`, `AppInfo`, + `CellInfo`, capability grants, …) via a type-driven serde layer. +- Async-only API surface (`AdminClient`, `AppClient`). +- Test fixture upgraded to Holochain 0.6 (hdk 0.6 / hdi 0.7, current DNA/app + manifest format). +- Tooling moved to Python 3.10+ and the modern Holonix flake. diff --git a/HANDOFF-python-client-zome-call-signing.md b/HANDOFF-python-client-zome-call-signing.md new file mode 100644 index 0000000..65999cc --- /dev/null +++ b/HANDOFF-python-client-zome-call-signing.md @@ -0,0 +1,159 @@ +# Handoff: Zome-call signing for a Holochain Python client + +**Audience:** an agent building/maintaining the downstream Python client library for Holochain. +**Date:** 2026-06-03. **Holochain target at time of writing:** 0.6.1. + +## TL;DR + +Do **not** depend on a native serialization crate (the `holochain-serialization-python` +pyo3 binding — now **archived**). The JS client (`@holochain/client`) dropped its +equivalent (`holochain-serialization-js`) and now signs zome calls with plain +**msgpack + sha512 + ed25519**, entirely in-language. A Python client should do the +same with `msgpack` + `hashlib.sha512` + `PyNaCl`. No Rust, no `maturin`, no +per-platform wheels, no chasing Holochain crate versions. + +## Why the native serialization lib is obsolete + +Old model (pre-0.4-ish): the client sent **structured** zome-call params; the +conductor re-serialized them with its canonical Rust serializer and hashed the +result. Client and conductor therefore had to serialize **byte-identically**, which +is why a Rust binding existed — to reuse the exact `holochain_serialized_bytes` +encoder. (`holochain-serialization-python::get_data_to_sign` reflects this: it +returns only the 32-/now-64-byte **hash**, not the bytes.) + +Current model: the client serializes the params itself, signs the hash of *those* +bytes, and transmits **both the bytes and the signature** as `CallZomeRequestSigned +{ bytes, signature }`. The conductor verifies by hashing the **received bytes** — it +does **not** re-serialize. Consequence: the client's serialization no longer has to +match any canonical Rust impl. Any msgpack encoding the conductor can deserialize +into `ZomeCallParams` works, in any field order. That removed the entire reason for +the serialization library. + +## Reference implementation (TypeScript, authoritative) + +Repo `holochain/holochain-client-js`, branch `main`: + +- `src/api/app/websocket.ts:652` — `signZomeCall(request)`. The core: + ```ts + const zome_call_params = { + cap_secret, cell_id, zome_name, fn_name, provenance, + payload: encode(request.payload), // payload is itself msgpack-encoded first + nonce: await randomNonce(), + expires_at: getNonceExpiration(), + }; + const bytes = encode(zome_call_params); // @msgpack/msgpack + const bytesHash = new Uint8Array(sha512.array(bytes)); // js-sha512 + const signature = sodium.crypto_sign(bytesHash, keyPair.privateKey) + .subarray(0, sodium.crypto_sign_BYTES); // detached, 64 bytes + return { bytes, signature }; // CallZomeRequestSigned + ``` +- `src/api/zome-call-signing.ts` — signing keypair, nonce, cap-secret, credential store. +- `src/api/admin/websocket.ts:324` — `grantSigningKey` / `authorizeSigningCredentials`. +- `src/utils/hash-parts.ts` — HoloHash byte layout & type prefixes. + +Relevant runtime deps in the JS client `package.json` (no serialization lib): +`@msgpack/msgpack`, `js-sha512`, `@bitgo/blake2b`, `libsodium-wrappers`, +`js-base64`, `lodash-es`. + +## The signing algorithm, precisely + +1. **Signing keypair is ephemeral**, not the agent's real key. Generate an ed25519 + keypair; authorize it on the conductor via the admin API (below). Store + `{ capSecret, keyPair, signingKey }` per `cellId`. +2. **`signingKey` (= `provenance`)** is a 39-byte AgentPubKey holohash: + `[132, 32, 36]` (Agent prefix) + 32-byte ed25519 public key + 4-byte DHT location. + The JS client fakes the location as the last 4 bytes of a supplied agent key, else + `[0,0,0,0]`. Hash layout (from `hash-parts.ts`): 3-byte type prefix + 32-byte core + + 4-byte location = 39 bytes. Agent prefix `[132,32,36]`, Dna `[132,45,36]`, etc. +3. **`payload` is double-encoded**: msgpack-encode the call payload first, then it + becomes a `bytes` field inside the params map that is msgpack-encoded again. +4. **`expires_at`** = `(now_ms + 5*60*1000) * 1000` → microseconds since epoch (i64). +5. **`nonce`** = 32 random bytes. **`cap_secret`** = 64 bytes (or `None`/absent if the + grant is unrestricted). +6. **`cell_id`** is a 2-element array `[dna_hash(39B), agent_pubkey(39B)]`. +7. Serialize the params map with msgpack, `sha512` the bytes, ed25519-sign the **hash** + (detached 64-byte signature), send `{ bytes, signature }`. + +## Python implementation guidance + +```python +import time, secrets, hashlib, msgpack +from nacl.signing import SigningKey # PyNaCl; wraps libsodium ed25519 + +def now_micros_plus_5min() -> int: + return (int(time.time() * 1000) + 5 * 60 * 1000) * 1000 + +def sign_zome_call(*, cell_id, zome_name, fn_name, payload, + provenance, cap_secret, signing_key: SigningKey): + params = { + "cap_secret": cap_secret, # bytes(64) or None + "cell_id": [cell_id[0], cell_id[1]], # [dna_hash, agent_pubkey], 39B each + "zome_name": zome_name, # str + "fn_name": fn_name, # str + "provenance": provenance, # 39-byte signing AgentPubKey + "payload": msgpack.packb(payload, use_bin_type=True), + "nonce": secrets.token_bytes(32), + "expires_at": now_micros_plus_5min(), + } + data = msgpack.packb(params, use_bin_type=True) + bytes_hash = hashlib.sha512(data).digest() + signature = signing_key.sign(bytes_hash).signature # 64-byte detached ed25519 sig + return {"bytes": data, "signature": signature} # CallZomeRequestSigned +``` + +### Correctness pitfalls (will silently produce a rejected signature) + +- **`use_bin_type=True` is mandatory.** Byte fields (hashes, nonce, cap_secret, + pre-encoded payload) must serialize as msgpack `bin`, not `str`. Equivalent to JS + encoding `Uint8Array`. +- **Pass `bytes`, not `list[int]`,** for all byte fields so msgpack picks `bin`. +- **Field order doesn't matter for correctness** (conductor hashes received bytes), + but you MUST hash exactly the bytes you transmit. Don't re-encode between hashing + and sending. +- **ed25519 key material:** libsodium `crypto_sign` secret keys are 64 bytes + (seed+pubkey); PyNaCl `SigningKey` takes a 32-byte seed. Use one consistently and + make sure the public key embedded in `provenance` matches the private key you sign + with. Prefer `nacl.bindings.crypto_sign_*` if you need byte-exact libsodium parity. +- Sign the **sha512 hash**, not the raw bytes. + +### Capability authorization (required before any signed call works) + +Mirror `AdminWebsocket.authorizeSigningCredentials` / `grantSigningKey` +(`src/api/admin/websocket.ts:324`): + +1. Generate ephemeral signing keypair → derive 39-byte `signingKey`. +2. `randomCapSecret()` = 64 random bytes. +3. Admin call `grant_zome_call_capability` with: + ``` + cap_grant: { + tag: "zome-call-signing-key", + functions: { type: "all" } // or { type: "listed", value: [[zome, fn], ...] } + access: { type: "assigned", value: { secret: capSecret, assignees: [signingKey] } } + } + ``` +4. Store `{ capSecret, keyPair, signingKey }` keyed by `cellId`; reuse for signing. + +## Suggested Python deps + +`msgpack` (the `msgpack` PyPI package), `PyNaCl` (ed25519 / libsodium). `hashlib` +covers sha512 from stdlib. msgpack/admin transport is WebSocket — pick an async ws +client (e.g. `websockets`). No native build toolchain required. + +## Open items to verify against a live 0.6.1 conductor + +- Exact admin `grant_zome_call_capability` request shape on the **Python** transport + (field names are snake_case on the wire; cross-check the conductor API for 0.6.1). +- Whether `cap_secret` should be **absent** vs explicit `None` for unrestricted + grants — match what the conductor's serde expects (`Option`). +- Confirm `CallZomeRequestSigned` is the accepted request variant name in 0.6.1. +- App-authentication token flow (`issue_app_authentication_token`) precedes app-ws + calls in the JS client — replicate it. + +## Don't + +- Don't revive the pyo3 `holochain-serialization-python` crate. It's archived, it ties + you to specific Holochain crate versions (painful: every Holochain bump is a port — + e.g. 0.2→0.6 renamed `ZomeCallUnsigned`→`ZomeCallParams`, `data_to_sign()`→ + `serialize_and_hash()`, changed the hash from blake2b/32B to sha512/64B), and it + only returns the hash anyway — which is the wrong shape for the send-the-bytes + protocol. diff --git a/README.md b/README.md index dbe8bc4..195da3e 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,36 @@ # Holochain Client - Python -> [!WARNING] -> :radioactive: This package is under development, it is not a complete Holochain client and is not fully tested! :radioactive: +A Python client for the [Holochain](https://holochain.org/) Conductor API, targeting **Holochain 0.6**. + +It speaks the admin and app websocket interfaces directly: msgpack framing, the +`{type, value}` request/response envelope, fully-typed request/response models, +and client-side zome-call signing (msgpack + SHA-512 + ed25519 via PyNaCl — no +native dependency). + +> [!NOTE] +> Unstable-feature endpoints (countersigning, DNA migration) are not yet covered. + +### Usage + +```python +from holochain_client import AdminClient, AppClient, AppBundlePath + +admin = await AdminClient.connect("ws://localhost:4444") +app_info = await admin.install_app( + AppBundlePath(path="my-app.happ"), installed_app_id="my-app" +) +await admin.enable_app("my-app") + +cell_id = app_info.cell_info["my-role"][0].cell_id +port = await admin.attach_app_interface(allowed_origins="*") +token = await admin.issue_app_authentication_token("my-app") + +app = await AppClient.connect(f"ws://localhost:{port}", token.token) +app.credentials.set(cell_id, await admin.authorize_signing_credentials(cell_id)) + +result = await app.call_zome(cell_id, "my_zome", "my_fn", {"some": "payload"}) +``` ### Set up a development environment @@ -53,15 +81,20 @@ poetry run pytest To select a single test suite, pass the path to `pytest`. For example: ```bash -poetry run tests/api/app/client_test.py +poetry run pytest tests/app/test_zome_call.py ``` -To run a single test, pass the path to the test suite and the use the `-k` flag. For example: +To run a single test, pass the path to the test suite and then use the `-k` flag. For example: ```bash -poetry run pytest tests/api/app/client_test.py -k test_call_zome +poetry run pytest tests/app/test_zome_call.py -k test_echo ``` +The integration tests boot a real conductor with `hc sandbox`. To run them +against an already-running conductor instead, set `HC_ADMIN_PORT` to its admin +port. The unit tests (`tests/test_serde.py`, `tests/test_signing.py`) need no +conductor. + > [!TIP] > By default `pytest` captures output. Use the `-s` flag in combination with `RUST_LOG=info` to debug tests against Holochain. diff --git a/fixture/Cargo.lock b/fixture/Cargo.lock index 63ac872..94288db 100644 --- a/fixture/Cargo.lock +++ b/fixture/Cargo.lock @@ -1,41 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli 0.28.1", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] +version = 4 [[package]] name = "android-tzdata" @@ -52,27 +17,12 @@ dependencies = [ "libc", ] -[[package]] -name = "approx" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" -dependencies = [ - "num-traits", -] - [[package]] name = "arrayref" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "arrayvec" version = "0.7.4" @@ -85,83 +35,20 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "backtrace" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" -dependencies = [ - "addr2line", - "cc", - "cfg-if 1.0.0", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - [[package]] name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bit-set" -version = "0.5.3" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "blake2b_simd" -version = "0.5.11" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +checksum = "b79834656f71332577234b50bfc009996f7449e0c056884e6a02492ded0ca2f3" dependencies = [ "arrayref", - "arrayvec 0.5.2", + "arrayvec", "constant_time_eq", ] @@ -180,72 +67,12 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" -[[package]] -name = "bytecheck" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "bytes" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" - -[[package]] -name = "camino" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" -dependencies = [ - "camino", - "cargo-platform", - "semver 1.0.20", - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "cc" version = "1.0.83" @@ -255,12 +82,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -277,31 +98,32 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.0", + "windows-targets", ] [[package]] name = "colored" -version = "1.9.4" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f741c91823341bebf717d4c71bda820630ce065443b58bd1b7451af008355" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "is-terminal", - "lazy_static", - "winapi", + "windows-sys", ] [[package]] name = "constant_time_eq" -version = "0.1.5" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" [[package]] name = "convert_case" -version = "0.4.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "core-foundation-sys" @@ -309,19 +131,6 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" -[[package]] -name = "corosensei" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80128832c58ea9cbd041d2a759ec449224487b2c1e400453d99d244eead87a8e" -dependencies = [ - "autocfg", - "cfg-if 1.0.0", - "libc", - "scopeguard", - "windows-sys 0.33.0", -] - [[package]] name = "cpufeatures" version = "0.2.12" @@ -331,123 +140,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cranelift-bforest" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2ab4512dfd3a6f4be184403a195f76e81a8a9f9e6c898e19d2dc3ce20e0115" -dependencies = [ - "cranelift-entity", -] - -[[package]] -name = "cranelift-codegen" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98b022ed2a5913a38839dfbafe6cf135342661293b08049843362df4301261dc" -dependencies = [ - "arrayvec 0.7.4", - "bumpalo", - "cranelift-bforest", - "cranelift-codegen-meta", - "cranelift-codegen-shared", - "cranelift-egraph", - "cranelift-entity", - "cranelift-isle", - "gimli 0.26.2", - "log", - "regalloc2", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-codegen-meta" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "639307b45434ad112a98f8300c0f0ab085cbefcd767efcdef9ef19d4c0756e74" -dependencies = [ - "cranelift-codegen-shared", -] - -[[package]] -name = "cranelift-codegen-shared" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278e52e29c53fcf32431ef08406c295699a70306d05a0715c5b1bf50e33a9ab7" - -[[package]] -name = "cranelift-egraph" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624b54323b06e675293939311943ba82d323bb340468ce1889be5da7932c8d73" -dependencies = [ - "cranelift-entity", - "fxhash", - "hashbrown 0.12.3", - "indexmap 1.9.3", - "log", - "smallvec", -] - -[[package]] -name = "cranelift-entity" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a59bcbca89c3f1b70b93ab3cbba5e5e0cbf3e63dadb23c7525cb142e21a9d4c" - -[[package]] -name = "cranelift-frontend" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d70abacb8cfef3dc8ff7e8836e9c1d70f7967dfdac824a4cd5e30223415aca6" -dependencies = [ - "cranelift-codegen", - "log", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-isle" -version = "0.91.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "393bc73c451830ff8dbb3a07f61843d6cb41a084f9996319917c0b291ed785bb" - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" - [[package]] name = "crypto-common" version = "0.1.6" @@ -460,108 +152,60 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.4" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", -] - -[[package]] -name = "darling" -version = "0.20.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8" -dependencies = [ - "darling_core 0.20.5", - "darling_macro 0.20.5", + "darling_core", + "darling_macro", ] [[package]] name = "darling_core" -version = "0.14.4" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 1.0.109", -] - -[[package]] -name = "darling_core" -version = "0.20.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core 0.14.4", - "quote", - "syn 1.0.109", + "syn 2.0.117", ] [[package]] name = "darling_macro" -version = "0.20.5" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ - "darling_core 0.20.5", + "darling_core", "quote", - "syn 2.0.48", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if 1.0.0", - "hashbrown 0.14.3", - "lock_api", - "once_cell", - "parking_lot_core", + "syn 2.0.117", ] [[package]] -name = "derivative" -version = "2.2.0" +name = "derive_more" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "derive_more-impl", ] [[package]] -name = "derive_more" -version = "0.99.17" +name = "derive_more-impl" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 1.0.109", + "syn 2.0.117", + "unicode-xid", ] [[package]] @@ -580,81 +224,19 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "enum-iterator" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" -dependencies = [ - "enum-iterator-derive", -] - -[[package]] -name = "enum-iterator-derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "enumset" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" -dependencies = [ - "enumset_derive", -] - -[[package]] -name = "enumset_derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" -dependencies = [ - "darling 0.20.5", - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - [[package]] name = "fixture" version = "0.0.1" dependencies = [ "fixture_integrity", "hdk", + "holochain_serialized_bytes", "serde", ] @@ -663,6 +245,7 @@ name = "fixture_integrity" version = "0.0.1" dependencies = [ "hdi", + "holochain_serialized_bytes", "serde", ] @@ -673,25 +256,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "form_urlencoded" -version = "1.2.1" +name = "futures" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -743,7 +311,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.117", ] [[package]] @@ -776,27 +344,6 @@ dependencies = [ "slab", ] -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "gcollections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f551fdf23ef80329f754919669147a71c67b6cfe3569cd93b6fabdd62044377" -dependencies = [ - "bit-set", - "num-integer", - "num-traits", - "trilean", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -809,39 +356,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" -dependencies = [ - "fallible-iterator", - "indexmap 1.9.3", - "stable_deref_trait", -] - -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", + "r-efi", + "wasip2", ] [[package]] @@ -852,13 +374,16 @@ checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "hdi" -version = "0.3.5" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95c916ed1bb10308ffb9e7295a3ea94c5485df949c8808e513e94c21fc10e7e8" +checksum = "df74665942cd911ca26245b3a68c783eef902018d41994e3192660fd9b157e0c" dependencies = [ + "getrandom", "hdk_derive", "holo_hash", "holochain_integrity_types", + "holochain_serialized_bytes", + "holochain_serialized_bytes_derive", "holochain_wasmer_guest", "paste", "serde", @@ -869,9 +394,9 @@ dependencies = [ [[package]] name = "hdk" -version = "0.2.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73cd358ee67fe3c93a20441508c63a10f479a48b234c51f41cf8a7926aec7601" +checksum = "cccd037e53cd05e518f66b839f0a57cda156a55dca0599bb1a4518ec371da801" dependencies = [ "getrandom", "hdi", @@ -881,87 +406,96 @@ dependencies = [ "holochain_zome_types", "paste", "serde", - "serde_bytes", - "thiserror", "tracing", "tracing-core", ] [[package]] name = "hdk_derive" -version = "0.2.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471d8ea11118a6b8d433340969b20584b80f964ccbd86d4ef25f9663951a2b35" +checksum = "21166508c997b6a767734116f05dd0b1d9511883def0f729f2cf97d12ae18839" dependencies = [ - "darling 0.14.4", + "darling", "heck", "holochain_integrity_types", "paste", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.117", ] [[package]] name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" - -[[package]] -name = "hex" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "holo_hash" -version = "0.2.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6c9040dfd9abfbee4c63c9f456ac9e3d10332f2f5d31cf9d36d904e90ef3e0" +checksum = "8e08d69f8af5042aa369b7fda3d9bea7ffe08de680aa9f897c29e50de0e2460c" dependencies = [ "base64", "blake2b_simd", "derive_more", + "futures", "holochain_serialized_bytes", "holochain_util", "holochain_wasmer_common", - "kitsune_p2p_dht_arc", + "must_future", "serde", "serde_bytes", + "sha2", "thiserror", ] [[package]] name = "holochain_integrity_types" -version = "0.2.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f8ba6ec99cfdffa3b16ba58146fba1072841b1b6cc274ed2ac6067ed0d8874" +checksum = "71fef817394dab4d006be7a24fb0e322db32b0060c949ee05a96df02b8eef508" dependencies = [ "holo_hash", + "holochain_secure_primitive", "holochain_serialized_bytes", + "holochain_timestamp", "holochain_util", - "kitsune_p2p_dht", - "kitsune_p2p_timestamp", - "paste", "serde", "serde_bytes", "subtle", "tracing", ] +[[package]] +name = "holochain_nonce" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf25c0d084ff290a8650c2fc82bd4341fc25d0d5f7ffe9af63a2ca9d50d9f0f" +dependencies = [ + "getrandom", + "holochain_secure_primitive", + "holochain_timestamp", +] + +[[package]] +name = "holochain_secure_primitive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c794f7bd4abe9b687cc98483bbeeb83d87771073218ce52ae67c871a6d15fa4" +dependencies = [ + "paste", + "serde", + "subtle", +] + [[package]] name = "holochain_serialized_bytes" -version = "0.0.53" +version = "0.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7a5fc7c745a107f8ebcb04caab7a6b7a8463e2811f07ced19c281977583de7" +checksum = "96007831189981a1c799b92c54f59eaefc9968b2dfcfae21e84f674fcc4757db" dependencies = [ "holochain_serialized_bytes_derive", "rmp-serde", @@ -974,51 +508,57 @@ dependencies = [ [[package]] name = "holochain_serialized_bytes_derive" -version = "0.0.53" +version = "0.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3e0cf02005cbf0f514476d40e02125b26df6d4922d7a2c48a84fc588539d71" +checksum = "17c64dfa01304c2c4ceba04a823fc4b6b75602d65d26e639f33845178f057d4a" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.117", +] + +[[package]] +name = "holochain_timestamp" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a201a7ae0e181ac157c605f9335fdb4a0ddce5e4b6a5540ec9f77c5174feb073" +dependencies = [ + "chrono", + "serde", ] [[package]] name = "holochain_util" -version = "0.2.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dadd7c84533fff6207298b30fe66453457c936593f1dbef36904926d3be2ed1d" +checksum = "df26e438014aa7ca3430d33893cdd9d30b252eeece880fd36222a9a4dd0bb2d3" dependencies = [ - "cfg-if 0.1.10", - "derive_more", + "cfg-if", + "colored", "dunce", "futures", - "num_cpus", "once_cell", ] [[package]] name = "holochain_wasmer_common" -version = "0.0.92" +version = "0.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72007fd2a72d77e76ffa494e5847bf6e893e25e73fe1d1de902e1b8d5033a64e" +checksum = "5979991fcad709d52614595f548e1dbc7a48449221825bcf2c9c6bdf05dc2dd1" dependencies = [ "holochain_serialized_bytes", "serde", "serde_bytes", - "test-fuzz", "thiserror", - "wasmer", ] [[package]] name = "holochain_wasmer_guest" -version = "0.0.92" +version = "0.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c429e84a19ee446f47541a6fed10e1a4376a8a8ba6d3dbff7d07e4a7bb4c85f" +checksum = "6083d8b19601a1a5c46172ffa75fe1c873e4e0fa6138cc8829cf8621b578e5fc" dependencies = [ "holochain_serialized_bytes", "holochain_wasmer_common", - "parking_lot", "paste", "serde", "tracing", @@ -1026,34 +566,26 @@ dependencies = [ [[package]] name = "holochain_zome_types" -version = "0.2.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b276d76c217b4a4490bf4651f2e89c2a20589a5d9700855b4bd430fedb6be75a" +checksum = "34a27bd154f022c9eabae7fde1b44bd9b0f5d68005bdb43f5553d4e840824b54" dependencies = [ + "derive_more", "holo_hash", "holochain_integrity_types", + "holochain_nonce", "holochain_serialized_bytes", + "holochain_timestamp", "holochain_wasmer_common", - "kitsune_p2p_bin_data", - "kitsune_p2p_block", - "kitsune_p2p_timestamp", - "paste", "serde", "serde_bytes", + "strum", + "strum_macros", "subtle", "thiserror", "tracing", ] -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "iana-time-zone" version = "0.1.60" @@ -1083,32 +615,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "if_chain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.2.2" @@ -1116,40 +622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" dependencies = [ "equivalent", - "hashbrown 0.14.3", -] - -[[package]] -name = "intervallum" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18bfda24d3930aa647f90044d5ef87d0c8120f13b86b2d60e8aade66e656e659" -dependencies = [ - "bit-set", - "gcollections", - "num-integer", - "num-traits", - "trilean", -] - -[[package]] -name = "is-terminal" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "itertools" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" -dependencies = [ - "either", + "hashbrown", ] [[package]] @@ -1167,120 +640,11 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "kitsune_p2p_bin_data" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c10a7ee1d573389ce678cf7e35c791a7c1862fc04fd146a61c9d4c69f83435" -dependencies = [ - "base64", - "derive_more", - "holochain_util", - "kitsune_p2p_dht_arc", - "serde", - "serde_bytes", - "shrinkwraprs", -] - -[[package]] -name = "kitsune_p2p_block" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd4758b9378356d324958b1cda2fed863213140e7af01e0ab7922ababf6d9d0" -dependencies = [ - "kitsune_p2p_bin_data", - "kitsune_p2p_timestamp", - "serde", - "serde_bytes", -] - -[[package]] -name = "kitsune_p2p_dht" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8aba1e1faba352596c04c079d1f005285893eec9efeb0f4ef75e87087f5025a" -dependencies = [ - "colored", - "derivative", - "derive_more", - "futures", - "gcollections", - "intervallum", - "kitsune_p2p_dht_arc", - "kitsune_p2p_timestamp", - "must_future", - "num-traits", - "once_cell", - "rand", - "serde", - "statrs", - "thiserror", - "tracing", -] - -[[package]] -name = "kitsune_p2p_dht_arc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d84a9cf03c59eb04fdcd2ea2692da34bd819805e66c601b651b71a536f761b5" -dependencies = [ - "derive_more", - "gcollections", - "intervallum", - "num-traits", - "serde", -] - -[[package]] -name = "kitsune_p2p_timestamp" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c3a5621981bae2bf801fa2f6bd84a5823f4130aa931013f437a55e20418b72" -dependencies = [ - "chrono", - "derive_more", - "serde", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "leb128" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" - [[package]] name = "libc" -version = "0.2.153" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" - -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - -[[package]] -name = "linux-raw-sys" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" - -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "log" @@ -1288,73 +652,12 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -[[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -] - -[[package]] -name = "matrixmultiply" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" -dependencies = [ - "autocfg", - "rawpointer", -] - [[package]] name = "memchr" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" -[[package]] -name = "memmap2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" -dependencies = [ - "libc", -] - -[[package]] -name = "memmap2" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" -dependencies = [ - "adler", -] - -[[package]] -name = "more-asserts" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" - [[package]] name = "must_future" version = "0.1.2" @@ -1365,92 +668,13 @@ dependencies = [ "pin-utils", ] -[[package]] -name = "nalgebra" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "462fffe4002f4f2e1f6a9dcf12cc1a6fc0e15989014efc02a941d3e0f5dc2120" -dependencies = [ - "approx", - "matrixmultiply", - "nalgebra-macros", - "num-complex", - "num-rational", - "num-traits", - "rand", - "rand_distr", - "simba", - "typenum", -] - -[[package]] -name = "nalgebra-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "num-complex" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", ] [[package]] @@ -1459,52 +683,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.48.5", -] - [[package]] name = "paste" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pest" -version = "2.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219c0dcc30b6a27553f9cc242972b67f75b60eb0db71f0b5462f38b058c41546" -dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1517,12 +701,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1549,33 +727,13 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "quote" version = "1.0.35" @@ -1586,300 +744,59 @@ dependencies = [ ] [[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" +name = "r-efi" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_distr" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" -dependencies = [ - "num-traits", - "rand", -] - -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - -[[package]] -name = "rayon" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "regalloc2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" -dependencies = [ - "fxhash", - "log", - "slice-group-by", - "smallvec", -] - -[[package]] -name = "regex" -version = "1.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -[[package]] -name = "region" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" -dependencies = [ - "bitflags 1.3.2", - "libc", - "mach", - "winapi", -] - -[[package]] -name = "rend" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" -dependencies = [ - "bytecheck", -] - -[[package]] -name = "rkyv" -version = "0.7.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" -dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "indexmap 1.9.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rmp" -version = "0.8.12" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" dependencies = [ - "byteorder", "num-traits", - "paste", ] [[package]] name = "rmp-serde" -version = "0.15.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723ecff9ad04f4ad92fe1c8ca6c20d2196d9286e9c60727c4cb5511629260e9d" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" dependencies = [ "byteorder", "rmp", "serde", ] -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.20", + "semver", ] -[[package]] -name = "rustix" -version = "0.38.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" -dependencies = [ - "bitflags 2.4.2", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - [[package]] name = "ryu" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "self_cell" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" -dependencies = [ - "serde", -] - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] [[package]] name = "serde" -version = "1.0.166" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] @@ -1893,34 +810,32 @@ dependencies = [ ] [[package]] -name = "serde-wasm-bindgen" -version = "0.4.5" +name = "serde_bytes" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" dependencies = [ - "js-sys", "serde", - "wasm-bindgen", ] [[package]] -name = "serde_bytes" -version = "0.11.14" +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ - "serde", + "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.166" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.117", ] [[package]] @@ -1929,63 +844,22 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" dependencies = [ - "indexmap 2.2.2", + "indexmap", "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha-1" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest", -] - -[[package]] -name = "shared-buffer" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c99835bad52957e7aa241d3975ed17c1e5f8c92026377d117a606f36b84b16" -dependencies = [ - "bytes", - "memmap2 0.6.2", -] - -[[package]] -name = "shrinkwraprs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63e6744142336dfb606fe2b068afa2e1cca1ee6a5d8377277a92945d81fa331" -dependencies = [ - "bitflags 1.3.2", - "itertools", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "simba" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e82063457853d00243beda9952e910b82593e4b07ae9f721b9278a99a0d3d5c" -dependencies = [ - "approx", - "num-complex", - "num-traits", - "paste", + "ryu", + "serde", ] [[package]] -name = "simdutf8" -version = "0.1.4" +name = "sha2" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] [[package]] name = "slab" @@ -1997,63 +871,27 @@ dependencies = [ ] [[package]] -name = "slice-group-by" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" - -[[package]] -name = "smallvec" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "statrs" -version = "0.15.0" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05bdbb8e4e78216a85785a85d3ec3183144f98d0097b9281802c019bb07a6f05" -dependencies = [ - "approx", - "lazy_static", - "nalgebra", - "num-traits", - "rand", -] +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "strsim" -version = "0.10.0" +name = "strum" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" [[package]] name = "strum_macros" -version = "0.24.3" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", - "syn 1.0.109", -] - -[[package]] -name = "subprocess" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086" -dependencies = [ - "libc", - "winapi", + "syn 2.0.117", ] [[package]] @@ -2069,136 +907,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.48" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "target-lexicon" -version = "0.12.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" - -[[package]] -name = "test-fuzz" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "125df852011c4f8f31df5620f4aea38ecddb5dfb4d9bc569b30485b15ffc3d4e" -dependencies = [ - "serde", - "test-fuzz-internal", - "test-fuzz-macro", - "test-fuzz-runtime", -] - -[[package]] -name = "test-fuzz-internal" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58071dc2471840e9f374eeb0f6e405a31bccb3cc5d59bb4598f02cafc274b5c4" -dependencies = [ - "cargo_metadata", - "proc-macro2", - "quote", - "serde", - "strum_macros", -] - -[[package]] -name = "test-fuzz-macro" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856bbca0314c328004691b9c0639fb198ca764d1ce0e20d4dd8b78f2697c2a6f" -dependencies = [ - "darling 0.14.4", - "if_chain", - "lazy_static", - "proc-macro2", - "quote", - "subprocess", - "syn 1.0.109", - "test-fuzz-internal", - "toolchain_find", - "unzip-n", -] - -[[package]] -name = "test-fuzz-runtime" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "303774eb17994c2ddb59c460369f4c3a55496f013380278d78eeebd2deb896ac" -dependencies = [ - "bincode", - "hex", - "num-traits", - "serde", - "sha-1", - "test-fuzz-internal", -] - [[package]] name = "thiserror" -version = "1.0.56" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "toolchain_find" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e85654a10e7a07a47c6f19d93818f3f343e22927f2fa280c84f7c8042743413" -dependencies = [ - "home", - "lazy_static", - "regex", - "semver 0.11.0", - "walkdir", + "syn 2.0.117", ] [[package]] @@ -2220,7 +960,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.117", ] [[package]] @@ -2233,30 +973,12 @@ dependencies = [ "valuable", ] -[[package]] -name = "trilean" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683ba5022fe6dbd7133cad150478ccf51bdb6d861515181e5fc6b4323d4fa424" - [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "ucd-trie" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -2264,47 +986,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-width" -version = "0.1.11" +name = "unicode-segmentation" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] -name = "unzip-n" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7e85a0596447f0f2ac090e16bc4c516c6fe91771fb0c0ccf7fa3dae896b9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "url" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "uuid" -version = "1.7.0" +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "valuable" @@ -2319,28 +1010,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] -name = "walkdir" -version = "2.4.0" +name = "wasip2" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "same-file", - "winapi-util", + "wit-bindgen", ] -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - [[package]] name = "wasm-bindgen" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] @@ -2355,7 +1039,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -2377,7 +1061,7 @@ checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.117", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2388,227 +1072,13 @@ version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" -[[package]] -name = "wasm-encoder" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" -dependencies = [ - "leb128", -] - -[[package]] -name = "wasmer" -version = "4.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce45cc009177ca345a6d041f9062305ad467d15e7d41494f5b81ab46d62d7a58" -dependencies = [ - "bytes", - "cfg-if 1.0.0", - "derivative", - "indexmap 1.9.3", - "js-sys", - "more-asserts", - "rustc-demangle", - "serde", - "serde-wasm-bindgen", - "shared-buffer", - "target-lexicon", - "thiserror", - "wasm-bindgen", - "wasmer-compiler", - "wasmer-compiler-cranelift", - "wasmer-derive", - "wasmer-types", - "wasmer-vm", - "wat", - "winapi", -] - -[[package]] -name = "wasmer-compiler" -version = "4.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e044f6140c844602b920deb4526aea3cc9c0d7cf23f00730bb9b2034669f522a" -dependencies = [ - "backtrace", - "bytes", - "cfg-if 1.0.0", - "enum-iterator", - "enumset", - "lazy_static", - "leb128", - "memmap2 0.5.10", - "more-asserts", - "region", - "rkyv", - "self_cell", - "shared-buffer", - "smallvec", - "thiserror", - "wasmer-types", - "wasmer-vm", - "wasmparser", - "winapi", -] - -[[package]] -name = "wasmer-compiler-cranelift" -version = "4.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ce02358eb44a149d791c1d6648fb7f8b2f99cd55e3c4eef0474653ec8cc889" -dependencies = [ - "cranelift-codegen", - "cranelift-entity", - "cranelift-frontend", - "gimli 0.26.2", - "more-asserts", - "rayon", - "smallvec", - "target-lexicon", - "tracing", - "wasmer-compiler", - "wasmer-types", -] - -[[package]] -name = "wasmer-derive" -version = "4.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c782d80401edb08e1eba206733f7859db6c997fc5a7f5fb44edc3ecd801468f6" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "wasmer-types" -version = "4.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd09e80d4d74bb9fd0ce6c3c106b1ceba1a050f9948db9d9b78ae53c172d6157" -dependencies = [ - "bytecheck", - "enum-iterator", - "enumset", - "indexmap 1.9.3", - "more-asserts", - "rkyv", - "target-lexicon", - "thiserror", -] - -[[package]] -name = "wasmer-vm" -version = "4.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdcd8a4fd36414a7b6a003dbfbd32393bce3e155d715dd877c05c1b7a41d224d" -dependencies = [ - "backtrace", - "cc", - "cfg-if 1.0.0", - "corosensei", - "crossbeam-queue", - "dashmap", - "derivative", - "enum-iterator", - "fnv", - "indexmap 1.9.3", - "lazy_static", - "libc", - "mach", - "memoffset", - "more-asserts", - "region", - "scopeguard", - "thiserror", - "wasmer-types", - "winapi", -] - -[[package]] -name = "wasmparser" -version = "0.95.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ea896273ea99b15132414be1da01ab0d8836415083298ecaffbe308eaac87a" -dependencies = [ - "indexmap 1.9.3", - "url", -] - -[[package]] -name = "wast" -version = "64.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a259b226fd6910225aa7baeba82f9d9933b6d00f2ce1b49b80fa4214328237cc" -dependencies = [ - "leb128", - "memchr", - "unicode-width", - "wasm-encoder", -] - -[[package]] -name = "wat" -version = "1.0.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53253d920ab413fca1c7dc2161d601c79b4fdf631d0ba51dd4343bf9b556c3f6" -dependencies = [ - "wast", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-sys" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43dbb096663629518eb1dfa72d80243ca5a6aca764cae62a2df70af760a9be75" -dependencies = [ - "windows_aarch64_msvc 0.33.0", - "windows_i686_gnu 0.33.0", - "windows_i686_msvc 0.33.0", - "windows_x86_64_gnu 0.33.0", - "windows_x86_64_msvc 0.33.0", + "windows-targets", ] [[package]] @@ -2617,22 +1087,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -2641,123 +1096,51 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" -[[package]] -name = "windows_aarch64_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" -[[package]] -name = "windows_i686_gnu" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" -[[package]] -name = "windows_i686_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" -[[package]] -name = "windows_x86_64_gnu" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" -[[package]] -name = "windows_x86_64_msvc" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.0" @@ -2765,10 +1148,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] -name = "wyz" -version = "0.5.1" +name = "wit-bindgen" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" diff --git a/fixture/Cargo.toml b/fixture/Cargo.toml index 0b7e544..b148984 100644 --- a/fixture/Cargo.toml +++ b/fixture/Cargo.toml @@ -6,11 +6,13 @@ opt-level = "z" [workspace] members = ["dnas/*/zomes/coordinator/*", "dnas/*/zomes/integrity/*"] +resolver = "2" [workspace.dependencies] -hdi = "=0.3.5" -hdk = "=0.2.5" -serde = "=1.0.166" +hdi = "=0.7.1" +hdk = "=0.6.1" +serde = "1.0" +holochain_serialized_bytes = "=0.0.57" [workspace.dependencies.fixture] path = "dnas/fixture/zomes/coordinator/fixture" diff --git a/fixture/dnas/fixture/workdir/dna.yaml b/fixture/dnas/fixture/workdir/dna.yaml index 7d99b93..9ae5812 100644 --- a/fixture/dnas/fixture/workdir/dna.yaml +++ b/fixture/dnas/fixture/workdir/dna.yaml @@ -1,21 +1,15 @@ --- -manifest_version: "1" +manifest_version: "0" name: fixture integrity: network_seed: ~ properties: ~ - origin_time: 1707241096195813 zomes: - name: fixture_integrity - hash: ~ - bundled: "../../../target/wasm32-unknown-unknown/release/fixture_integrity.wasm" - dependencies: ~ - dylib: ~ + path: "../../../target/wasm32-unknown-unknown/release/fixture_integrity.wasm" coordinator: zomes: - name: fixture - hash: ~ - bundled: "../../../target/wasm32-unknown-unknown/release/fixture.wasm" + path: "../../../target/wasm32-unknown-unknown/release/fixture.wasm" dependencies: - name: fixture_integrity - dylib: ~ diff --git a/fixture/dnas/fixture/zomes/coordinator/fixture/Cargo.toml b/fixture/dnas/fixture/zomes/coordinator/fixture/Cargo.toml index bcc2fcf..9777610 100644 --- a/fixture/dnas/fixture/zomes/coordinator/fixture/Cargo.toml +++ b/fixture/dnas/fixture/zomes/coordinator/fixture/Cargo.toml @@ -11,5 +11,6 @@ name = "fixture" hdk = { workspace = true } serde = { workspace = true } +holochain_serialized_bytes = { workspace = true } -fixture_integrity = { workspace = true } +fixture_integrity = { workspace = true } diff --git a/fixture/dnas/fixture/zomes/coordinator/fixture/src/all_fixtures.rs b/fixture/dnas/fixture/zomes/coordinator/fixture/src/all_fixtures.rs deleted file mode 100644 index 0432876..0000000 --- a/fixture/dnas/fixture/zomes/coordinator/fixture/src/all_fixtures.rs +++ /dev/null @@ -1,7 +0,0 @@ -use hdk::prelude::*; -use fixture_integrity::*; -#[hdk_extern] -pub fn get_all_fixtures(_: ()) -> ExternResult> { - let path = Path::from("all_fixtures"); - get_links(path.path_entry_hash()?, LinkTypes::AllFixtures, None) -} diff --git a/fixture/dnas/fixture/zomes/coordinator/fixture/src/fixture.rs b/fixture/dnas/fixture/zomes/coordinator/fixture/src/fixture.rs deleted file mode 100644 index c7336ca..0000000 --- a/fixture/dnas/fixture/zomes/coordinator/fixture/src/fixture.rs +++ /dev/null @@ -1,182 +0,0 @@ -use hdk::prelude::*; -use fixture_integrity::*; -#[hdk_extern] -pub fn create_fixture(fixture: Fixture) -> ExternResult { - let fixture_hash = create_entry(&EntryTypes::Fixture(fixture.clone()))?; - let record = get(fixture_hash.clone(), GetOptions::default())? - .ok_or( - wasm_error!( - WasmErrorInner::Guest(String::from("Could not find the newly created Fixture")) - ), - )?; - let path = Path::from("all_fixtures"); - create_link( - path.path_entry_hash()?, - fixture_hash.clone(), - LinkTypes::AllFixtures, - (), - )?; - Ok(record) -} -#[hdk_extern] -pub fn get_latest_fixture( - original_fixture_hash: ActionHash, -) -> ExternResult> { - let links = get_links( - original_fixture_hash.clone(), - LinkTypes::FixtureUpdates, - None, - )?; - let latest_link = links - .into_iter() - .max_by(|link_a, link_b| link_a.timestamp.cmp(&link_b.timestamp)); - let latest_fixture_hash = match latest_link { - Some(link) => { - link.target - .clone() - .into_action_hash() - .ok_or( - wasm_error!( - WasmErrorInner::Guest(String::from("No action hash associated with link")) - ), - )? - } - None => original_fixture_hash.clone(), - }; - get(latest_fixture_hash, GetOptions::default()) -} -#[hdk_extern] -pub fn get_original_fixture( - original_fixture_hash: ActionHash, -) -> ExternResult> { - let Some(details) = get_details(original_fixture_hash, GetOptions::default())? else { - return Ok(None); - }; - match details { - Details::Record(details) => Ok(Some(details.record)), - _ => { - Err( - wasm_error!( - WasmErrorInner::Guest(String::from("Malformed get details response")) - ), - ) - } - } -} -#[hdk_extern] -pub fn get_all_revisions_for_fixture( - original_fixture_hash: ActionHash, -) -> ExternResult> { - let Some(original_record) = get_original_fixture(original_fixture_hash.clone())? - else { - return Ok(vec![]); - }; - let links = get_links( - original_fixture_hash.clone(), - LinkTypes::FixtureUpdates, - None, - )?; - let get_input: Vec = links - .into_iter() - .map(|link| Ok( - GetInput::new( - link - .target - .into_action_hash() - .ok_or( - wasm_error!( - WasmErrorInner::Guest(String::from("No action hash associated with link")) - ), - )? - .into(), - GetOptions::default(), - ), - )) - .collect::>>()?; - let records = HDK.with(|hdk| hdk.borrow().get(get_input))?; - let mut records: Vec = records.into_iter().filter_map(|r| r).collect(); - records.insert(0, original_record); - Ok(records) -} -#[derive(Serialize, Deserialize, Debug)] -pub struct UpdateFixtureInput { - pub original_fixture_hash: ActionHash, - pub previous_fixture_hash: ActionHash, - pub updated_fixture: Fixture, -} -#[hdk_extern] -pub fn update_fixture(input: UpdateFixtureInput) -> ExternResult { - let updated_fixture_hash = update_entry( - input.previous_fixture_hash.clone(), - &input.updated_fixture, - )?; - create_link( - input.original_fixture_hash.clone(), - updated_fixture_hash.clone(), - LinkTypes::FixtureUpdates, - (), - )?; - let record = get(updated_fixture_hash.clone(), GetOptions::default())? - .ok_or( - wasm_error!( - WasmErrorInner::Guest(String::from("Could not find the newly updated Fixture")) - ), - )?; - Ok(record) -} -#[hdk_extern] -pub fn delete_fixture(original_fixture_hash: ActionHash) -> ExternResult { - let details = get_details(original_fixture_hash.clone(), GetOptions::default())? - .ok_or( - wasm_error!( - WasmErrorInner::Guest(String::from("{pascal_entry_def_name} not found")) - ), - )?; - match details { - Details::Record(details) => Ok(details.record), - _ => { - Err( - wasm_error!( - WasmErrorInner::Guest(String::from("Malformed get details response")) - ), - ) - } - }?; - let path = Path::from("all_fixtures"); - let links = get_links(path.path_entry_hash()?, LinkTypes::AllFixtures, None)?; - for link in links { - if let Some(hash) = link.target.into_action_hash() { - if hash.eq(&original_fixture_hash) { - delete_link(link.create_link_hash)?; - } - } - } - delete_entry(original_fixture_hash) -} -#[hdk_extern] -pub fn get_all_deletes_for_fixture( - original_fixture_hash: ActionHash, -) -> ExternResult>> { - let Some(details) = get_details(original_fixture_hash, GetOptions::default())? else { - return Ok(None); - }; - match details { - Details::Entry(_) => { - Err(wasm_error!(WasmErrorInner::Guest("Malformed details".into()))) - } - Details::Record(record_details) => Ok(Some(record_details.deletes)), - } -} -#[hdk_extern] -pub fn get_oldest_delete_for_fixture( - original_fixture_hash: ActionHash, -) -> ExternResult> { - let Some(mut deletes) = get_all_deletes_for_fixture(original_fixture_hash)? else { - return Ok(None); - }; - deletes - .sort_by(|delete_a, delete_b| { - delete_a.action().timestamp().cmp(&delete_b.action().timestamp()) - }); - Ok(deletes.first().cloned()) -} diff --git a/fixture/dnas/fixture/zomes/coordinator/fixture/src/lib.rs b/fixture/dnas/fixture/zomes/coordinator/fixture/src/lib.rs index b4c0e0c..37f607a 100644 --- a/fixture/dnas/fixture/zomes/coordinator/fixture/src/lib.rs +++ b/fixture/dnas/fixture/zomes/coordinator/fixture/src/lib.rs @@ -1,147 +1,55 @@ -pub mod all_fixtures; -pub mod fixture; -use hdk::prelude::*; use fixture_integrity::*; +use hdk::prelude::*; + +/// Create a `Fixture` entry, link it under the `all_fixtures` path, and return +/// the created `Record`. Exercises `Record`/`Action`/`Entry` decoding. +#[hdk_extern] +pub fn create_fixture(fixture: Fixture) -> ExternResult { + let action_hash = create_entry(&EntryTypes::Fixture(fixture.clone()))?; + let record = get(action_hash.clone(), GetOptions::default())?.ok_or(wasm_error!( + WasmErrorInner::Guest("could not find the newly created Fixture".into()) + ))?; + let path = Path::from("all_fixtures"); + create_link( + path.path_entry_hash()?, + action_hash, + LinkTypes::AllFixtures, + (), + )?; + Ok(record) +} + +/// Fetch a `Fixture` record by its action hash. #[hdk_extern] -pub fn init(_: ()) -> ExternResult { - Ok(InitCallbackResult::Pass) +pub fn get_fixture(action_hash: ActionHash) -> ExternResult> { + get(action_hash, GetOptions::default()) +} + +/// List all links from the `all_fixtures` path. +#[hdk_extern] +pub fn get_all_fixtures(_: ()) -> ExternResult> { + let path = Path::from("all_fixtures"); + get_links( + LinkQuery::try_new(path.path_entry_hash()?, LinkTypes::AllFixtures)?, + GetStrategy::default(), + ) } + +/// Echo the input back. The simplest possible round-trip. +#[hdk_extern] +pub fn echo(value: String) -> ExternResult { + Ok(value) +} + #[derive(Serialize, Deserialize, Debug)] #[serde(tag = "type")] pub enum Signal { - LinkCreated { action: SignedActionHashed, link_type: LinkTypes }, - LinkDeleted { - action: SignedActionHashed, - create_link_action: SignedActionHashed, - link_type: LinkTypes, - }, - EntryCreated { action: SignedActionHashed, app_entry: EntryTypes }, - EntryUpdated { - action: SignedActionHashed, - app_entry: EntryTypes, - original_app_entry: EntryTypes, - }, - EntryDeleted { action: SignedActionHashed, original_app_entry: EntryTypes }, -} -#[hdk_extern(infallible)] -pub fn post_commit(committed_actions: Vec) { - for action in committed_actions { - if let Err(err) = signal_action(action) { - error!("Error signaling new action: {:?}", err); - } - } + Echo { value: String }, } -fn signal_action(action: SignedActionHashed) -> ExternResult<()> { - match action.hashed.content.clone() { - Action::CreateLink(create_link) => { - if let Ok(Some(link_type)) = LinkTypes::from_type( - create_link.zome_index, - create_link.link_type, - ) { - emit_signal(Signal::LinkCreated { - action, - link_type, - })?; - } - Ok(()) - } - Action::DeleteLink(delete_link) => { - let record = get( - delete_link.link_add_address.clone(), - GetOptions::default(), - )? - .ok_or( - wasm_error!( - WasmErrorInner::Guest("Failed to fetch CreateLink action" - .to_string()) - ), - )?; - match record.action() { - Action::CreateLink(create_link) => { - if let Ok(Some(link_type)) = LinkTypes::from_type( - create_link.zome_index, - create_link.link_type, - ) { - emit_signal(Signal::LinkDeleted { - action, - link_type, - create_link_action: record.signed_action.clone(), - })?; - } - Ok(()) - } - _ => { - return Err( - wasm_error!( - WasmErrorInner::Guest("Create Link should exist".to_string()) - ), - ); - } - } - } - Action::Create(_create) => { - if let Ok(Some(app_entry)) = get_entry_for_action(&action.hashed.hash) { - emit_signal(Signal::EntryCreated { - action, - app_entry, - })?; - } - Ok(()) - } - Action::Update(update) => { - if let Ok(Some(app_entry)) = get_entry_for_action(&action.hashed.hash) { - if let Ok(Some(original_app_entry)) = get_entry_for_action( - &update.original_action_address, - ) { - emit_signal(Signal::EntryUpdated { - action, - app_entry, - original_app_entry, - })?; - } - } - Ok(()) - } - Action::Delete(delete) => { - if let Ok(Some(original_app_entry)) = get_entry_for_action( - &delete.deletes_address, - ) { - emit_signal(Signal::EntryDeleted { - action, - original_app_entry, - })?; - } - Ok(()) - } - _ => Ok(()), - } -} -fn get_entry_for_action(action_hash: &ActionHash) -> ExternResult> { - let record = match get_details(action_hash.clone(), GetOptions::default())? { - Some(Details::Record(record_details)) => record_details.record, - _ => { - return Ok(None); - } - }; - let entry = match record.entry().as_option() { - Some(entry) => entry, - None => { - return Ok(None); - } - }; - let (zome_index, entry_index) = match record.action().entry_type() { - Some(EntryType::App(AppEntryDef { zome_index, entry_index, .. })) => { - (zome_index, entry_index) - } - _ => { - return Ok(None); - } - }; - Ok( - EntryTypes::deserialize_from_type( - zome_index.clone(), - entry_index.clone(), - entry, - )?, - ) + +/// Emit an app signal so the client can exercise signal delivery. +#[hdk_extern] +pub fn emit(value: String) -> ExternResult<()> { + emit_signal(Signal::Echo { value })?; + Ok(()) } diff --git a/fixture/dnas/fixture/zomes/integrity/fixture/Cargo.toml b/fixture/dnas/fixture/zomes/integrity/fixture/Cargo.toml index 539d971..4dd468e 100644 --- a/fixture/dnas/fixture/zomes/integrity/fixture/Cargo.toml +++ b/fixture/dnas/fixture/zomes/integrity/fixture/Cargo.toml @@ -11,3 +11,4 @@ name = "fixture_integrity" hdi = { workspace = true } serde = { workspace = true } +holochain_serialized_bytes = { workspace = true } diff --git a/fixture/dnas/fixture/zomes/integrity/fixture/src/fixture.rs b/fixture/dnas/fixture/zomes/integrity/fixture/src/fixture.rs deleted file mode 100644 index 2a65e4a..0000000 --- a/fixture/dnas/fixture/zomes/integrity/fixture/src/fixture.rs +++ /dev/null @@ -1,119 +0,0 @@ -use hdi::prelude::*; -#[hdk_entry_helper] -#[derive(Clone, PartialEq)] -pub struct Fixture { - pub name: String, -} -pub fn validate_create_fixture( - _action: EntryCreationAction, - _fixture: Fixture, -) -> ExternResult { - Ok(ValidateCallbackResult::Valid) -} -pub fn validate_update_fixture( - _action: Update, - _fixture: Fixture, - _original_action: EntryCreationAction, - _original_fixture: Fixture, -) -> ExternResult { - Ok(ValidateCallbackResult::Valid) -} -pub fn validate_delete_fixture( - _action: Delete, - _original_action: EntryCreationAction, - _original_fixture: Fixture, -) -> ExternResult { - Ok(ValidateCallbackResult::Valid) -} -pub fn validate_create_link_fixture_updates( - _action: CreateLink, - base_address: AnyLinkableHash, - target_address: AnyLinkableHash, - _tag: LinkTag, -) -> ExternResult { - let action_hash = base_address - .into_action_hash() - .ok_or( - wasm_error!( - WasmErrorInner::Guest(String::from("No action hash associated with link")) - ), - )?; - let record = must_get_valid_record(action_hash)?; - let _fixture: crate::Fixture = record - .entry() - .to_app_option() - .map_err(|e| wasm_error!(e))? - .ok_or( - wasm_error!( - WasmErrorInner::Guest(String::from("Linked action must reference an entry")) - ), - )?; - let action_hash = target_address - .into_action_hash() - .ok_or( - wasm_error!( - WasmErrorInner::Guest(String::from("No action hash associated with link")) - ), - )?; - let record = must_get_valid_record(action_hash)?; - let _fixture: crate::Fixture = record - .entry() - .to_app_option() - .map_err(|e| wasm_error!(e))? - .ok_or( - wasm_error!( - WasmErrorInner::Guest(String::from("Linked action must reference an entry")) - ), - )?; - Ok(ValidateCallbackResult::Valid) -} -pub fn validate_delete_link_fixture_updates( - _action: DeleteLink, - _original_action: CreateLink, - _base: AnyLinkableHash, - _target: AnyLinkableHash, - _tag: LinkTag, -) -> ExternResult { - Ok( - ValidateCallbackResult::Invalid( - String::from("FixtureUpdates links cannot be deleted"), - ), - ) -} -pub fn validate_create_link_all_fixtures( - _action: CreateLink, - _base_address: AnyLinkableHash, - target_address: AnyLinkableHash, - _tag: LinkTag, -) -> ExternResult { - // Check the entry type for the given action hash - let action_hash = target_address - .into_action_hash() - .ok_or( - wasm_error!( - WasmErrorInner::Guest(String::from("No action hash associated with link")) - ), - )?; - let record = must_get_valid_record(action_hash)?; - let _fixture: crate::Fixture = record - .entry() - .to_app_option() - .map_err(|e| wasm_error!(e))? - .ok_or( - wasm_error!( - WasmErrorInner::Guest(String::from("Linked action must reference an entry")) - ), - )?; - // TODO: add the appropriate validation rules - Ok(ValidateCallbackResult::Valid) -} -pub fn validate_delete_link_all_fixtures( - _action: DeleteLink, - _original_action: CreateLink, - _base: AnyLinkableHash, - _target: AnyLinkableHash, - _tag: LinkTag, -) -> ExternResult { - // TODO: add the appropriate validation rules - Ok(ValidateCallbackResult::Valid) -} diff --git a/fixture/dnas/fixture/zomes/integrity/fixture/src/lib.rs b/fixture/dnas/fixture/zomes/integrity/fixture/src/lib.rs index 0719a1b..313f6e9 100644 --- a/fixture/dnas/fixture/zomes/integrity/fixture/src/lib.rs +++ b/fixture/dnas/fixture/zomes/integrity/fixture/src/lib.rs @@ -1,380 +1,32 @@ -pub mod fixture; -pub use fixture::*; use hdi::prelude::*; -#[derive(Serialize, Deserialize)] -#[serde(tag = "type")] -#[hdk_entry_defs] + +/// A trivial app entry used by the client test-suite to exercise zome calls +/// and `Record`/`Action`/`Entry` deserialization. +#[hdk_entry_helper] +#[derive(Clone, PartialEq)] +pub struct Fixture { + pub value: String, +} + +#[hdk_entry_types] #[unit_enum(UnitEntryTypes)] pub enum EntryTypes { + #[entry_type(visibility = "public")] Fixture(Fixture), } -#[derive(Serialize, Deserialize)] + #[hdk_link_types] pub enum LinkTypes { - FixtureUpdates, AllFixtures, + FixtureUpdates, } + #[hdk_extern] -pub fn genesis_self_check( - _data: GenesisSelfCheckData, -) -> ExternResult { - Ok(ValidateCallbackResult::Valid) -} -pub fn validate_agent_joining( - _agent_pub_key: AgentPubKey, - _membrane_proof: &Option, -) -> ExternResult { +pub fn genesis_self_check(_: GenesisSelfCheckData) -> ExternResult { Ok(ValidateCallbackResult::Valid) } + #[hdk_extern] -pub fn validate(op: Op) -> ExternResult { - match op.flattened::()? { - FlatOp::StoreEntry(store_entry) => { - match store_entry { - OpEntry::CreateEntry { app_entry, action } => { - match app_entry { - EntryTypes::Fixture(fixture) => { - validate_create_fixture( - EntryCreationAction::Create(action), - fixture, - ) - } - } - } - OpEntry::UpdateEntry { app_entry, action, .. } => { - match app_entry { - EntryTypes::Fixture(fixture) => { - validate_create_fixture( - EntryCreationAction::Update(action), - fixture, - ) - } - } - } - _ => Ok(ValidateCallbackResult::Valid), - } - } - FlatOp::RegisterUpdate(update_entry) => { - match update_entry { - OpUpdate::Entry { - original_action, - original_app_entry, - app_entry, - action, - } => { - match (app_entry, original_app_entry) { - ( - EntryTypes::Fixture(fixture), - EntryTypes::Fixture(original_fixture), - ) => { - validate_update_fixture( - action, - fixture, - original_action, - original_fixture, - ) - } - } - } - _ => Ok(ValidateCallbackResult::Valid), - } - } - FlatOp::RegisterDelete(delete_entry) => { - match delete_entry { - OpDelete::Entry { original_action, original_app_entry, action } => { - match original_app_entry { - EntryTypes::Fixture(fixture) => { - validate_delete_fixture(action, original_action, fixture) - } - } - } - _ => Ok(ValidateCallbackResult::Valid), - } - } - FlatOp::RegisterCreateLink { - link_type, - base_address, - target_address, - tag, - action, - } => { - match link_type { - LinkTypes::FixtureUpdates => { - validate_create_link_fixture_updates( - action, - base_address, - target_address, - tag, - ) - } - LinkTypes::AllFixtures => { - validate_create_link_all_fixtures( - action, - base_address, - target_address, - tag, - ) - } - } - } - FlatOp::RegisterDeleteLink { - link_type, - base_address, - target_address, - tag, - original_action, - action, - } => { - match link_type { - LinkTypes::FixtureUpdates => { - validate_delete_link_fixture_updates( - action, - original_action, - base_address, - target_address, - tag, - ) - } - LinkTypes::AllFixtures => { - validate_delete_link_all_fixtures( - action, - original_action, - base_address, - target_address, - tag, - ) - } - } - } - FlatOp::StoreRecord(store_record) => { - match store_record { - OpRecord::CreateEntry { app_entry, action } => { - match app_entry { - EntryTypes::Fixture(fixture) => { - validate_create_fixture( - EntryCreationAction::Create(action), - fixture, - ) - } - } - } - OpRecord::UpdateEntry { - original_action_hash, - app_entry, - action, - .. - } => { - let original_record = must_get_valid_record(original_action_hash)?; - let original_action = original_record.action().clone(); - let original_action = match original_action { - Action::Create(create) => EntryCreationAction::Create(create), - Action::Update(update) => EntryCreationAction::Update(update), - _ => { - return Ok( - ValidateCallbackResult::Invalid( - "Original action for an update must be a Create or Update action" - .to_string(), - ), - ); - } - }; - match app_entry { - EntryTypes::Fixture(fixture) => { - let result = validate_create_fixture( - EntryCreationAction::Update(action.clone()), - fixture.clone(), - )?; - if let ValidateCallbackResult::Valid = result { - let original_fixture: Option = original_record - .entry() - .to_app_option() - .map_err(|e| wasm_error!(e))?; - let original_fixture = match original_fixture { - Some(fixture) => fixture, - None => { - return Ok( - ValidateCallbackResult::Invalid( - "The updated entry type must be the same as the original entry type" - .to_string(), - ), - ); - } - }; - validate_update_fixture( - action, - fixture, - original_action, - original_fixture, - ) - } else { - Ok(result) - } - } - } - } - OpRecord::DeleteEntry { original_action_hash, action, .. } => { - let original_record = must_get_valid_record(original_action_hash)?; - let original_action = original_record.action().clone(); - let original_action = match original_action { - Action::Create(create) => EntryCreationAction::Create(create), - Action::Update(update) => EntryCreationAction::Update(update), - _ => { - return Ok( - ValidateCallbackResult::Invalid( - "Original action for a delete must be a Create or Update action" - .to_string(), - ), - ); - } - }; - let app_entry_type = match original_action.entry_type() { - EntryType::App(app_entry_type) => app_entry_type, - _ => { - return Ok(ValidateCallbackResult::Valid); - } - }; - let entry = match original_record.entry().as_option() { - Some(entry) => entry, - None => { - if original_action.entry_type().visibility().is_public() { - return Ok( - ValidateCallbackResult::Invalid( - "Original record for a delete of a public entry must contain an entry" - .to_string(), - ), - ); - } else { - return Ok(ValidateCallbackResult::Valid); - } - } - }; - let original_app_entry = match EntryTypes::deserialize_from_type( - app_entry_type.zome_index.clone(), - app_entry_type.entry_index.clone(), - &entry, - )? { - Some(app_entry) => app_entry, - None => { - return Ok( - ValidateCallbackResult::Invalid( - "Original app entry must be one of the defined entry types for this zome" - .to_string(), - ), - ); - } - }; - match original_app_entry { - EntryTypes::Fixture(original_fixture) => { - validate_delete_fixture( - action, - original_action, - original_fixture, - ) - } - } - } - OpRecord::CreateLink { - base_address, - target_address, - tag, - link_type, - action, - } => { - match link_type { - LinkTypes::FixtureUpdates => { - validate_create_link_fixture_updates( - action, - base_address, - target_address, - tag, - ) - } - LinkTypes::AllFixtures => { - validate_create_link_all_fixtures( - action, - base_address, - target_address, - tag, - ) - } - } - } - OpRecord::DeleteLink { original_action_hash, base_address, action } => { - let record = must_get_valid_record(original_action_hash)?; - let create_link = match record.action() { - Action::CreateLink(create_link) => create_link.clone(), - _ => { - return Ok( - ValidateCallbackResult::Invalid( - "The action that a DeleteLink deletes must be a CreateLink" - .to_string(), - ), - ); - } - }; - let link_type = match LinkTypes::from_type( - create_link.zome_index.clone(), - create_link.link_type.clone(), - )? { - Some(lt) => lt, - None => { - return Ok(ValidateCallbackResult::Valid); - } - }; - match link_type { - LinkTypes::FixtureUpdates => { - validate_delete_link_fixture_updates( - action, - create_link.clone(), - base_address, - create_link.target_address, - create_link.tag, - ) - } - LinkTypes::AllFixtures => { - validate_delete_link_all_fixtures( - action, - create_link.clone(), - base_address, - create_link.target_address, - create_link.tag, - ) - } - } - } - OpRecord::CreatePrivateEntry { .. } => Ok(ValidateCallbackResult::Valid), - OpRecord::UpdatePrivateEntry { .. } => Ok(ValidateCallbackResult::Valid), - OpRecord::CreateCapClaim { .. } => Ok(ValidateCallbackResult::Valid), - OpRecord::CreateCapGrant { .. } => Ok(ValidateCallbackResult::Valid), - OpRecord::UpdateCapClaim { .. } => Ok(ValidateCallbackResult::Valid), - OpRecord::UpdateCapGrant { .. } => Ok(ValidateCallbackResult::Valid), - OpRecord::Dna { .. } => Ok(ValidateCallbackResult::Valid), - OpRecord::OpenChain { .. } => Ok(ValidateCallbackResult::Valid), - OpRecord::CloseChain { .. } => Ok(ValidateCallbackResult::Valid), - OpRecord::InitZomesComplete { .. } => Ok(ValidateCallbackResult::Valid), - _ => Ok(ValidateCallbackResult::Valid), - } - } - FlatOp::RegisterAgentActivity(agent_activity) => { - match agent_activity { - OpActivity::CreateAgent { agent, action } => { - let previous_action = must_get_action(action.prev_action)?; - match previous_action.action() { - Action::AgentValidationPkg( - AgentValidationPkg { membrane_proof, .. }, - ) => validate_agent_joining(agent, membrane_proof), - _ => { - Ok( - ValidateCallbackResult::Invalid( - "The previous action for a `CreateAgent` action must be an `AgentValidationPkg`" - .to_string(), - ), - ) - } - } - } - _ => Ok(ValidateCallbackResult::Valid), - } - } - } +pub fn validate(_op: Op) -> ExternResult { + Ok(ValidateCallbackResult::Valid) } diff --git a/fixture/package.json b/fixture/package.json index af8eb4e..512357b 100644 --- a/fixture/package.json +++ b/fixture/package.json @@ -3,7 +3,7 @@ "private": true, "scripts": { "build:happ": "npm run build:zomes && hc app pack workdir --recursive", - "build:zomes": "RUSTFLAGS='' CARGO_TARGET_DIR=target cargo build --release --target wasm32-unknown-unknown" + "build:zomes": "RUSTFLAGS='--cfg getrandom_backend=\"custom\"' CARGO_TARGET_DIR=target cargo build --release --target wasm32-unknown-unknown" }, "engines": { "npm": ">=7.0.0" diff --git a/fixture/workdir/happ.yaml b/fixture/workdir/happ.yaml index 88750dd..bbf6484 100644 --- a/fixture/workdir/happ.yaml +++ b/fixture/workdir/happ.yaml @@ -1,5 +1,5 @@ --- -manifest_version: "1" +manifest_version: "0" name: fixture description: ~ roles: @@ -8,11 +8,9 @@ roles: strategy: create deferred: false dna: - bundled: "../dnas/fixture/workdir/fixture.dna" + path: "../dnas/fixture/workdir/fixture.dna" modifiers: network_seed: ~ properties: ~ - origin_time: ~ - quantum_time: ~ installed_hash: ~ clone_limit: 0 diff --git a/fixture/workdir/web-happ.yaml b/fixture/workdir/web-happ.yaml deleted file mode 100644 index de58ce8..0000000 --- a/fixture/workdir/web-happ.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -manifest_version: "1" -name: fixture -ui: - bundled: "../ui/dist.zip" -happ_manifest: - bundled: "./fixture.happ" diff --git a/flake.lock b/flake.lock index 2c6f447..84d2606 100644 --- a/flake.lock +++ b/flake.lock @@ -1,52 +1,12 @@ { "nodes": { - "cargo-chef": { - "flake": false, - "locked": { - "lastModified": 1695999026, - "narHash": "sha256-UtLoZd7YBRSF9uXStfC3geEFqSqZXFh1rLHaP8hre0Y=", - "owner": "LukeMathWalker", - "repo": "cargo-chef", - "rev": "6e96ae5cd023b718ae40d608981e50a6e7d7facf", - "type": "github" - }, - "original": { - "owner": "LukeMathWalker", - "ref": "main", - "repo": "cargo-chef", - "type": "github" - } - }, - "cargo-rdme": { - "flake": false, - "locked": { - "lastModified": 1675118998, - "narHash": "sha256-lrYWqu3h88fr8gG3Yo5GbFGYaq5/1Os7UtM+Af0Bg4k=", - "owner": "orium", - "repo": "cargo-rdme", - "rev": "f9dbb6bccc078f4869f45ae270a2890ac9a75877", - "type": "github" - }, - "original": { - "owner": "orium", - "ref": "v1.1.0", - "repo": "cargo-rdme", - "type": "github" - } - }, "crane": { - "inputs": { - "nixpkgs": [ - "holochain", - "nixpkgs" - ] - }, "locked": { - "lastModified": 1707363936, - "narHash": "sha256-QbqyvGFYt84QNOQLOOTWplZZkzkyDhYrAl/N/9H0vFM=", + "lastModified": 1778106249, + "narHash": "sha256-cM/AuKy5tMhwOOQIbha8ZRRMHVfNf7cv2aljIw+qoCg=", "owner": "ipetkov", "repo": "crane", - "rev": "9107434eda6991e9388ad87b815dafa337446d16", + "rev": "6d015ea29630b7ad2402841386da2cb617a470a7", "type": "github" }, "original": { @@ -55,288 +15,175 @@ "type": "github" } }, - "crate2nix": { - "flake": false, - "locked": { - "lastModified": 1706909251, - "narHash": "sha256-T7G9Uhh77P0kKri/u+Mwa/4YnXwdPsJSwYCiJCCW+fs=", - "owner": "kolloch", - "repo": "crate2nix", - "rev": "15656bb6cb15f55ee3344bf4362e6489feb93db6", - "type": "github" - }, - "original": { - "owner": "kolloch", - "repo": "crate2nix", - "type": "github" - } - }, - "empty": { - "flake": false, - "locked": { - "lastModified": 1683792623, - "narHash": "sha256-pQpattmS9VmO3ZIQUFn66az8GSmB4IvYhTTCFn6SUmo=", - "owner": "steveej", - "repo": "empty", - "rev": "8e328e450e4cd32e072eba9e99fe92cf2a1ef5cf", - "type": "github" - }, - "original": { - "owner": "steveej", - "repo": "empty", - "type": "github" - } - }, - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1706830856, - "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", + "lastModified": 1778716662, + "narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", - "type": "github" - }, - "original": { - "id": "flake-parts", - "type": "indirect" - } - }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1705309234, - "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb", "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "hercules-ci", + "repo": "flake-parts", "type": "github" } }, - "holochain": { - "inputs": { - "cargo-chef": "cargo-chef", - "cargo-rdme": "cargo-rdme", - "crane": "crane", - "crate2nix": "crate2nix", - "empty": "empty", - "flake-compat": "flake-compat", - "flake-parts": "flake-parts", - "holochain": [ - "holochain", - "empty" - ], - "lair": [ - "holochain", - "empty" - ], - "launcher": [ - "holochain", - "empty" - ], - "nix-filter": "nix-filter", - "nixpkgs": "nixpkgs", - "pre-commit-hooks-nix": "pre-commit-hooks-nix", - "repo-git": "repo-git", - "rust-overlay": "rust-overlay", - "scaffolding": [ - "holochain", - "empty" - ], - "versions": [ - "versions" - ] - }, + "hc-scaffold": { + "flake": false, "locked": { - "lastModified": 1707719563, - "narHash": "sha256-bECQ40ecjglVRKuYJwyr775ZsFJYh7a6kZN3eozJLMc=", + "lastModified": 1778884165, + "narHash": "sha256-3ANE+PjAGqF5SE59L8ZWLBwD0WxEBik/nWct9c9rSvE=", "owner": "holochain", - "repo": "holochain", - "rev": "76f48f1964100ecd10372f6ad57e4698354bc80f", + "repo": "scaffolding", + "rev": "c51df16be0498bd499fd0c82445d6cadadd7ccb4", "type": "github" }, "original": { "owner": "holochain", - "repo": "holochain", + "ref": "v0.601.1", + "repo": "scaffolding", "type": "github" } }, - "holochain_2": { + "holochain": { "flake": false, "locked": { - "lastModified": 1707245081, - "narHash": "sha256-l9WHMlD9IDuEv/N/3WDCsP3XLUUnZFrOBEZjbWnC+/Y=", + "lastModified": 1778596880, + "narHash": "sha256-uUJZkeqBAZgXtDf0juCRpKOUO3yY1gWEKWGqatWcVjY=", "owner": "holochain", "repo": "holochain", - "rev": "0a3b2408b2d6482b913b9f0faf58a39b567f763a", + "rev": "3bdeaccd1c54fa351e76f7347601dfbc061d5bd4", "type": "github" }, "original": { "owner": "holochain", - "ref": "holochain-0.2.6", + "ref": "holochain-0.6.1", "repo": "holochain", "type": "github" } }, - "lair": { - "flake": false, + "holonix": { + "inputs": { + "crane": "crane", + "flake-parts": "flake-parts", + "hc-scaffold": "hc-scaffold", + "holochain": "holochain", + "kitsune2": "kitsune2", + "lair-keystore": "lair-keystore", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + }, "locked": { - "lastModified": 1706550351, - "narHash": "sha256-psVjtb+zj0pZnHTj1xNP2pGBd5Ua1cSwdOAdYdUe3yQ=", + "lastModified": 1778884233, + "narHash": "sha256-qDQMAQwQm6ZBL+hYejJU0p0GO7aP/UwVwto61jC5QYc=", "owner": "holochain", - "repo": "lair", - "rev": "b11e65eff11c8ac3bf938607946f5c7201298a65", + "repo": "holonix", + "rev": "5e41693a5f4451e93be1c347a5dd9dafd5e216e5", "type": "github" }, "original": { "owner": "holochain", - "ref": "lair_keystore-v0.4.2", - "repo": "lair", + "ref": "main-0.6", + "repo": "holonix", "type": "github" } }, - "launcher": { + "kitsune2": { "flake": false, "locked": { - "lastModified": 1684183666, - "narHash": "sha256-rOE/W/BBYyZKOyypKb8X9Vpc4ty1TNRoI/fV5+01JPw=", + "lastModified": 1778073766, + "narHash": "sha256-/hG+/9VlZiC1u/UASbIOH0oeiUmKlzqvur++CWSVAbQ=", "owner": "holochain", - "repo": "launcher", - "rev": "75ecdd0aa191ed830cc209a984a6030e656042ff", + "repo": "kitsune2", + "rev": "b33fa55606f8bd5020cffacf1ffae41f92d4d296", "type": "github" }, "original": { "owner": "holochain", - "ref": "holochain-0.2", - "repo": "launcher", + "ref": "v0.4.1", + "repo": "kitsune2", "type": "github" } }, - "nix-filter": { + "lair-keystore": { + "flake": false, "locked": { - "lastModified": 1705332318, - "narHash": "sha256-kcw1yFeJe9N4PjQji9ZeX47jg0p9A0DuU4djKvg1a7I=", - "owner": "numtide", - "repo": "nix-filter", - "rev": "3449dc925982ad46246cfc36469baf66e1b64f17", + "lastModified": 1759147598, + "narHash": "sha256-K8TkVlKAwS7uuSZC46oSHddtZTM2XGWUTNnjjYdC1bE=", + "owner": "holochain", + "repo": "lair", + "rev": "8aa9ab1468e82a8bfb9bf1e65762efc51659d306", "type": "github" }, "original": { - "owner": "numtide", - "repo": "nix-filter", + "owner": "holochain", + "ref": "v0.6.3", + "repo": "lair", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1707268954, - "narHash": "sha256-2en1kvde3cJVc3ZnTy8QeD2oKcseLFjYPLKhIGDanQ0=", - "owner": "NixOS", + "lastModified": 1778430510, + "narHash": "sha256-Ti+ZBvW6yrWWAg2szExVTwCd4qOJ3KlVr1tFHfyfi8Q=", + "owner": "nixos", "repo": "nixpkgs", - "rev": "f8e2ebd66d097614d51a56a755450d4ae1632df1", + "rev": "8fd9daa3db09ced9700431c5b7ad0e8ba199b575", "type": "github" }, "original": { - "id": "nixpkgs", - "ref": "nixos-unstable", - "type": "indirect" - } - }, - "nixpkgs-lib": { - "locked": { - "dir": "lib", - "lastModified": 1706550542, - "narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "97b17f32362e475016f942bbdfda4a4a72a8a652", - "type": "github" - }, - "original": { - "dir": "lib", - "owner": "NixOS", - "ref": "nixos-unstable", + "owner": "nixos", + "ref": "nixos-25.11", "repo": "nixpkgs", "type": "github" } }, - "pre-commit-hooks-nix": { - "flake": false, + "nixpkgs-lib": { "locked": { - "lastModified": 1707297608, - "narHash": "sha256-ADjo/5VySGlvtCW3qR+vdFF4xM9kJFlRDqcC9ZGI8EA=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "0db2e67ee49910adfa13010e7f012149660af7f0", + "lastModified": 1777168982, + "narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "f5901329dade4a6ea039af1433fb087bd9c1fe14", "type": "github" }, "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", + "owner": "nix-community", + "repo": "nixpkgs.lib", "type": "github" } }, - "repo-git": { - "flake": false, - "locked": { - "narHash": "sha256-d6xi4mKdjkX2JFicDIv5niSzpyI0m/Hnm8GGAIU04kY=", - "type": "file", - "url": "file:/dev/null" - }, - "original": { - "type": "file", - "url": "file:/dev/null" - } - }, "root": { "inputs": { - "holochain": "holochain", + "flake-parts": [ + "holonix", + "flake-parts" + ], + "holonix": "holonix", "nixpkgs": [ - "holochain", + "holonix", "nixpkgs" - ], - "versions": "versions" + ] } }, "rust-overlay": { "inputs": { - "flake-utils": "flake-utils", "nixpkgs": [ - "holochain", + "holonix", "nixpkgs" ] }, "locked": { - "lastModified": 1707703915, - "narHash": "sha256-Vej69igzNr3eVDca6+32uO+TXjVWx6ZUwwy3iZuzhJ4=", + "lastModified": 1778728594, + "narHash": "sha256-+gIOsOzqWNfn+ThCXBQGcLHVEnaGQW59XjghE9JUIYk=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "e6679d2ff9136d00b3a7168d2bf1dff9e84c5758", + "rev": "83a17ebffcfacb17b49e1b5e9dc15eed07936648", "type": "github" }, "original": { @@ -344,61 +191,6 @@ "repo": "rust-overlay", "type": "github" } - }, - "scaffolding": { - "flake": false, - "locked": { - "lastModified": 1705076186, - "narHash": "sha256-O914XeBuFulky5RqTOvsog+fbO12v0ppW+mIdO6lvKU=", - "owner": "holochain", - "repo": "scaffolding", - "rev": "42e025fe23ac0b4cd659d95e6752d2d300a8d076", - "type": "github" - }, - "original": { - "owner": "holochain", - "ref": "holochain-0.2", - "repo": "scaffolding", - "type": "github" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "versions": { - "inputs": { - "holochain": "holochain_2", - "lair": "lair", - "launcher": "launcher", - "scaffolding": "scaffolding" - }, - "locked": { - "dir": "versions/0_2", - "lastModified": 1707719563, - "narHash": "sha256-bECQ40ecjglVRKuYJwyr775ZsFJYh7a6kZN3eozJLMc=", - "owner": "holochain", - "repo": "holochain", - "rev": "76f48f1964100ecd10372f6ad57e4698354bc80f", - "type": "github" - }, - "original": { - "dir": "versions/0_2", - "owner": "holochain", - "repo": "holochain", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 24ad91b..a9e3f70 100644 --- a/flake.nix +++ b/flake.nix @@ -1,36 +1,29 @@ { - description = "Flake for Holochain client development"; + description = "Flake for Holochain client development (Holochain 0.6)"; inputs = { - versions.url = "github:holochain/holochain?dir=versions/0_2"; + holonix.url = "github:holochain/holonix?ref=main-0.6"; - versions.inputs.holochain.url = "github:holochain/holochain/holochain-0.2.6"; - - holochain = { - url = "github:holochain/holochain"; - inputs.versions.follows = "versions"; - }; - - nixpkgs.follows = "holochain/nixpkgs"; + nixpkgs.follows = "holonix/nixpkgs"; + flake-parts.follows = "holonix/flake-parts"; }; - outputs = inputs @ { ... }: - inputs.holochain.inputs.flake-parts.lib.mkFlake { inherit inputs; } + outputs = inputs @ { flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { - systems = builtins.attrNames inputs.holochain.devShells; - perSystem = { config, pkgs, system, ... }: { + systems = builtins.attrNames inputs.holonix.devShells; + perSystem = { inputs', pkgs, system, ... }: { devShells.default = pkgs.mkShell { - inputsFrom = [ - inputs.holochain.devShells.${system}.holonix - ]; - packages = [ - (pkgs.python3.withPackages (python-pkgs: [ - python-pkgs.pip - python-pkgs.poetry-core - ])) - pkgs.poetry - pkgs.nodejs_20 - ]; + packages = (with inputs'.holonix.packages; [ + holochain + hc + lair-keystore + rust # Rust toolchain with the wasm32 target for building zomes + ]) ++ (with pkgs; [ + (python3.withPackages (ps: [ ps.pip ps.poetry-core ])) + poetry + nodejs_20 + ]); }; }; }; diff --git a/holochain_client/__init__.py b/holochain_client/__init__.py index e69de29..36ddef1 100644 --- a/holochain_client/__init__.py +++ b/holochain_client/__init__.py @@ -0,0 +1,59 @@ +"""A Python client for the Holochain Conductor API (Holochain 0.6).""" + +from __future__ import annotations + +from .admin import AdminClient +from .app import AppClient, AppSignal +from ._transport import ConductorError +from .hash import ( + ActionHash, + AgentPubKey, + DnaHash, + EntryHash, + ExternalHash, + HoloHash, + WasmHash, +) +from .signing import SigningCredentials, SigningKeyPair +from .types.app import AppInfo, AppStatus +from .types.cell import CellId, ClonedCell, DnaModifiers, ProvisionedCell +from .types.record import ( + Action, + Entry, + Record, + RecordEntry, + decode_record, + decode_records, +) +from .admin.types import AppBundleBytes, AppBundlePath, AppBundleSource + +__all__ = [ + "AdminClient", + "AppClient", + "AppSignal", + "AppInfo", + "AppStatus", + "AppBundleBytes", + "AppBundlePath", + "AppBundleSource", + "Action", + "CellId", + "ClonedCell", + "ConductorError", + "DnaModifiers", + "Entry", + "ProvisionedCell", + "Record", + "RecordEntry", + "decode_record", + "decode_records", + "SigningCredentials", + "SigningKeyPair", + "ActionHash", + "AgentPubKey", + "DnaHash", + "EntryHash", + "ExternalHash", + "HoloHash", + "WasmHash", +] diff --git a/holochain_client/_api.py b/holochain_client/_api.py new file mode 100644 index 0000000..d53b429 --- /dev/null +++ b/holochain_client/_api.py @@ -0,0 +1,52 @@ +"""Shared request/response envelope handling for the admin and app clients.""" + +from __future__ import annotations + +from typing import Any, Optional + +from ._transport import ConductorError, WebsocketConnection +from .serde import decode, encode + + +class ApiClient: + """Wraps a :class:`WebsocketConnection` with the conductor's request and + response envelope (``{"type": , "value": }``) and turns + ``error`` responses into :class:`ConductorError`.""" + + def __init__(self, connection: WebsocketConnection): + self._connection = connection + + async def _send( + self, + request_type: str, + value: Any = None, + *, + response_type: Any = None, + expect: Optional[str] = None, + ) -> Any: + request: dict[str, Any] = {"type": request_type} + if value is not None: + request["value"] = encode(value) + response = await self._connection.request(request) + + kind = response.get("type") + content = response.get("value") + if kind == "error": + raise _to_error(content) + if expect is not None and kind != expect: + raise ConductorError( + f"unexpected response {kind!r} to {request_type!r}: {content!r}" + ) + return decode(content, response_type) + + async def close(self) -> None: + await self._connection.close() + + +def _to_error(content: Any) -> ConductorError: + """Turn an ``ExternalApiWireError`` payload into an exception.""" + if isinstance(content, dict) and "type" in content: + kind = content["type"] + message = content.get("value") + return ConductorError(f"{kind}: {message}") + return ConductorError(str(content)) diff --git a/holochain_client/_transport.py b/holochain_client/_transport.py new file mode 100644 index 0000000..b47ff89 --- /dev/null +++ b/holochain_client/_transport.py @@ -0,0 +1,116 @@ +"""Low-level WebSocket transport for the Holochain conductor API. + +Handles the ``holochain_websocket`` wire framing — an outer msgpack map +``{"type": "request"|"response"|"signal"|"authenticate", "id"?, "data": }`` +where ``data`` is the msgpack-encoded inner payload — plus request/response +correlation by id and dispatch of unsolicited signals. +""" + +from __future__ import annotations + +import asyncio +import itertools +from typing import Any, Callable, Optional + +import msgpack +from websockets.asyncio.client import connect as ws_connect +from websockets.exceptions import ConnectionClosed + +# Conductor websockets reject connections without an Origin header. The value is +# only meaningful when the interface restricts allowed origins. +DEFAULT_ORIGIN = "holochain-client-python" + +SignalHandler = Callable[[bytes], Any] + + +class ConductorError(Exception): + """An ``error`` response returned by the conductor.""" + + +class WebsocketClosed(Exception): + """Raised for in-flight requests when the connection closes.""" + + +def _pack(obj: Any) -> bytes: + return msgpack.packb(obj, use_bin_type=True) + + +def _unpack(data: bytes) -> Any: + return msgpack.unpackb(data, raw=False) + + +class WebsocketConnection: + """A single conductor WebSocket connection.""" + + def __init__(self, ws, origin: str = DEFAULT_ORIGIN): + self._ws = ws + self._origin = origin + self._ids = itertools.count(1) + self._pending: dict[int, asyncio.Future] = {} + self._signal_handlers: list[SignalHandler] = [] + self._reader: Optional[asyncio.Task] = None + + @classmethod + async def connect( + cls, url: str, origin: str = DEFAULT_ORIGIN + ) -> "WebsocketConnection": + ws = await ws_connect(url, additional_headers={"Origin": origin}) + self = cls(ws, origin) + self._reader = asyncio.create_task(self._read_loop()) + return self + + def on_signal(self, handler: SignalHandler) -> None: + self._signal_handlers.append(handler) + + async def authenticate(self, payload: Any) -> None: + """Send an ``authenticate`` frame (no response is returned).""" + frame = _pack({"type": "authenticate", "data": _pack(payload)}) + await self._ws.send(frame) + + async def request(self, payload: Any) -> Any: + """Send a ``request`` frame and await the decoded inner response.""" + req_id = next(self._ids) + loop = asyncio.get_event_loop() + fut: asyncio.Future = loop.create_future() + self._pending[req_id] = fut + frame = _pack({"type": "request", "id": req_id, "data": _pack(payload)}) + try: + await self._ws.send(frame) + return await fut + finally: + self._pending.pop(req_id, None) + + async def _read_loop(self) -> None: + try: + async for raw in self._ws: + self._dispatch(raw) + except ConnectionClosed: + pass + finally: + closed = WebsocketClosed("connection closed") + for fut in self._pending.values(): + if not fut.done(): + fut.set_exception(closed) + self._pending.clear() + + def _dispatch(self, raw: bytes) -> None: + wire = _unpack(raw) + kind = wire.get("type") + if kind == "response": + fut = self._pending.get(wire["id"]) + if fut is None or fut.done(): + return + data = wire.get("data") + inner = _unpack(data) if data is not None else None + fut.set_result(inner) + elif kind == "signal": + data = wire.get("data") + if data is None: + return + for handler in self._signal_handlers: + handler(data) + + async def close(self) -> None: + if self._reader is not None: + self._reader.cancel() + await self._ws.close() diff --git a/holochain_client/admin/__init__.py b/holochain_client/admin/__init__.py new file mode 100644 index 0000000..70a1e25 --- /dev/null +++ b/holochain_client/admin/__init__.py @@ -0,0 +1,3 @@ +from .client import AdminClient + +__all__ = ["AdminClient"] diff --git a/holochain_client/admin/client.py b/holochain_client/admin/client.py new file mode 100644 index 0000000..da56319 --- /dev/null +++ b/holochain_client/admin/client.py @@ -0,0 +1,286 @@ +"""The admin interface client.""" + +from __future__ import annotations + +from typing import Any, List, Optional + +from .._api import ApiClient +from .._transport import DEFAULT_ORIGIN, WebsocketConnection +from ..hash import ActionHash, AgentPubKey, DnaHash +from ..signing import ( + SigningCredentials, + SigningKeyPair, + CredentialStore, + random_cap_secret, +) +from ..types.app import AppInfo, AppInterfaceInfo +from ..types.capability import ( + CapAccessAssigned, + GrantedFunctions, + GrantedFunctionsAll, + GrantZomeCallCapabilityPayload, + ZomeCallCapGrant, +) +from ..types.cell import CellId +from ..types.common import AppAuthenticationToken, InstalledAppId +from .types import ( + AppAuthenticationTokenIssued, + AppBundleSource, + DeleteCloneCellPayload, + InstallAppPayload, +) + + +class AdminClient(ApiClient): + """Client for a conductor admin websocket interface.""" + + def __init__(self, connection: WebsocketConnection): + super().__init__(connection) + self.credentials = CredentialStore() + + @classmethod + async def connect(cls, url: str, *, origin: str = DEFAULT_ORIGIN) -> "AdminClient": + connection = await WebsocketConnection.connect(url, origin=origin) + return cls(connection) + + # -- DNA / cell introspection ------------------------------------------ # + async def list_dnas(self) -> List[DnaHash]: + return await self._send( + "list_dnas", response_type=List[DnaHash], expect="dnas_listed" + ) + + async def list_cell_ids(self) -> List[CellId]: + return await self._send( + "list_cell_ids", response_type=List[CellId], expect="cell_ids_listed" + ) + + async def get_dna_definition(self, cell_id: CellId) -> Any: + # ``DnaDef`` is returned as a decoded structure (pass-through). + return await self._send( + "get_dna_definition", cell_id, expect="dna_definition_returned" + ) + + async def generate_agent_pub_key(self) -> AgentPubKey: + return await self._send( + "generate_agent_pub_key", + response_type=AgentPubKey, + expect="agent_pub_key_generated", + ) + + # -- App lifecycle ------------------------------------------------------ # + async def install_app( + self, + source: AppBundleSource, + *, + agent_key: Optional[AgentPubKey] = None, + installed_app_id: Optional[InstalledAppId] = None, + network_seed: Optional[str] = None, + roles_settings: Optional[Any] = None, + ignore_genesis_failure: bool = False, + ) -> AppInfo: + payload = InstallAppPayload( + source=source, + agent_key=agent_key, + installed_app_id=installed_app_id, + network_seed=network_seed, + roles_settings=roles_settings, + ignore_genesis_failure=ignore_genesis_failure, + ) + return await self._send( + "install_app", payload, response_type=AppInfo, expect="app_installed" + ) + + async def uninstall_app( + self, installed_app_id: InstalledAppId, *, force: bool = False + ) -> None: + await self._send( + "uninstall_app", + {"installed_app_id": installed_app_id, "force": force}, + expect="app_uninstalled", + ) + + async def enable_app(self, installed_app_id: InstalledAppId) -> AppInfo: + return await self._send( + "enable_app", + {"installed_app_id": installed_app_id}, + response_type=AppInfo, + expect="app_enabled", + ) + + async def disable_app(self, installed_app_id: InstalledAppId) -> None: + await self._send( + "disable_app", + {"installed_app_id": installed_app_id}, + expect="app_disabled", + ) + + async def list_apps(self, *, status_filter: Optional[str] = None) -> List[AppInfo]: + return await self._send( + "list_apps", + {"status_filter": status_filter}, + response_type=List[AppInfo], + expect="apps_listed", + ) + + async def delete_clone_cell(self, payload: DeleteCloneCellPayload) -> None: + await self._send("delete_clone_cell", payload, expect="clone_cell_deleted") + + # -- App interfaces & auth tokens -------------------------------------- # + async def attach_app_interface( + self, + *, + port: Optional[int] = None, + allowed_origins: str = "*", + installed_app_id: Optional[InstalledAppId] = None, + ) -> int: + result = await self._send( + "attach_app_interface", + { + "port": port, + "danger_bind_addr": None, + "allowed_origins": allowed_origins, + "installed_app_id": installed_app_id, + }, + expect="app_interface_attached", + ) + return result["port"] + + async def list_app_interfaces(self) -> List[AppInterfaceInfo]: + return await self._send( + "list_app_interfaces", + response_type=List[AppInterfaceInfo], + expect="app_interfaces_listed", + ) + + async def issue_app_authentication_token( + self, + installed_app_id: InstalledAppId, + *, + expiry_seconds: int = 30, + single_use: bool = True, + ) -> AppAuthenticationTokenIssued: + return await self._send( + "issue_app_authentication_token", + { + "installed_app_id": installed_app_id, + "expiry_seconds": expiry_seconds, + "single_use": single_use, + }, + response_type=AppAuthenticationTokenIssued, + expect="app_authentication_token_issued", + ) + + async def revoke_app_authentication_token( + self, token: AppAuthenticationToken + ) -> None: + await self._send( + "revoke_app_authentication_token", + token, + expect="app_authentication_token_revoked", + ) + + # -- Agent info -------------------------------------------------------- # + async def add_agent_info(self, agent_infos: List[str]) -> None: + await self._send( + "add_agent_info", {"agent_infos": agent_infos}, expect="agent_info_added" + ) + + async def agent_info( + self, *, dna_hashes: Optional[List[DnaHash]] = None + ) -> List[str]: + return await self._send( + "agent_info", + {"dna_hashes": dna_hashes}, + response_type=List[str], + expect="agent_info", + ) + + # -- Capabilities ------------------------------------------------------ # + async def grant_zome_call_capability( + self, payload: GrantZomeCallCapabilityPayload + ) -> ActionHash: + return await self._send( + "grant_zome_call_capability", + payload, + response_type=ActionHash, + expect="zome_call_capability_granted", + ) + + async def revoke_zome_call_capability( + self, action_hash: ActionHash, cell_id: CellId + ) -> None: + await self._send( + "revoke_zome_call_capability", + {"action_hash": action_hash, "cell_id": cell_id}, + expect="zome_call_capability_revoked", + ) + + async def list_capability_grants( + self, installed_app_id: InstalledAppId, *, include_revoked: bool = False + ) -> Any: + return await self._send( + "list_capability_grants", + {"installed_app_id": installed_app_id, "include_revoked": include_revoked}, + expect="capability_grants_info", + ) + + async def authorize_signing_credentials( + self, + cell_id: CellId, + *, + functions: Optional[GrantedFunctions] = None, + ) -> SigningCredentials: + """Generate a signing keypair, grant it a zome-call capability on + ``cell_id``, store the resulting credentials, and return them.""" + keypair = SigningKeyPair() + cap_secret = random_cap_secret() + grant = GrantZomeCallCapabilityPayload( + cell_id=cell_id, + cap_grant=ZomeCallCapGrant( + tag="zome-call-signing-key", + access=CapAccessAssigned( + secret=cap_secret, assignees=[keypair.agent_pub_key] + ), + functions=functions or GrantedFunctionsAll(), + ), + ) + await self.grant_zome_call_capability(grant) + credentials = SigningCredentials(cap_secret=cap_secret, signing_key=keypair) + self.credentials.set(cell_id, credentials) + return credentials + + # -- Introspection dumps (pass-through) -------------------------------- # + async def dump_state(self, cell_id: CellId) -> str: + return await self._send( + "dump_state", {"cell_id": cell_id}, expect="state_dumped" + ) + + async def dump_conductor_state(self) -> str: + return await self._send("dump_conductor_state", expect="conductor_state_dumped") + + async def dump_full_state( + self, cell_id: CellId, *, dht_ops_cursor: Optional[int] = None + ) -> Any: + return await self._send( + "dump_full_state", + {"cell_id": cell_id, "dht_ops_cursor": dht_ops_cursor}, + expect="full_state_dumped", + ) + + async def dump_network_metrics( + self, + *, + dna_hash: Optional[DnaHash] = None, + include_dht_summary: bool = False, + ) -> Any: + return await self._send( + "dump_network_metrics", + {"dna_hash": dna_hash, "include_dht_summary": include_dht_summary}, + expect="network_metrics_dumped", + ) + + async def dump_network_stats(self) -> Any: + return await self._send("dump_network_stats", expect="network_stats_dumped") + + async def storage_info(self) -> Any: + return await self._send("storage_info", expect="storage_info") diff --git a/holochain_client/admin/types.py b/holochain_client/admin/types.py new file mode 100644 index 0000000..f6d531b --- /dev/null +++ b/holochain_client/admin/types.py @@ -0,0 +1,96 @@ +"""Request/response payload types specific to the admin interface.""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Any, Optional + +from ..hash import AgentPubKey, DnaHash +from ..serde import TaggedUnion, variant +from ..types.cell import CloneId +from ..types.common import ( + AppAuthenticationToken, + InstalledAppId, + NetworkSeed, + Timestamp, +) + + +class AppBundleSource(TaggedUnion): + """Where to find an app bundle when installing.""" + + +@variant(AppBundleSource, "bytes") +@dataclass +class AppBundleBytes: + """Raw ``.happ`` bundle bytes.""" + + bundle: bytes = field(metadata={"newtype": True}) + + +@variant(AppBundleSource, "path") +@dataclass +class AppBundlePath: + """A filesystem path to a ``.happ`` bundle, read by the conductor.""" + + path: str = field(metadata={"newtype": True}) + + +@dataclass +class DnaModifiersOpt: + """Optional DNA modifier overrides.""" + + network_seed: Optional[NetworkSeed] = None + properties: Optional[Any] = None + + +@dataclass +class InstallAppPayload: + source: AppBundleSource + agent_key: Optional[AgentPubKey] = None + installed_app_id: Optional[InstalledAppId] = None + network_seed: Optional[NetworkSeed] = None + roles_settings: Optional[Any] = None + ignore_genesis_failure: bool = False + + def __to_wire__(self) -> dict: + # ``source`` is a tagged union; everything else is plain. Optional fields + # are sent as nil, which the conductor's serde accepts. + from ..serde import encode + + return { + "source": encode(self.source), + "agent_key": encode(self.agent_key), + "installed_app_id": self.installed_app_id, + "network_seed": self.network_seed, + "roles_settings": encode(self.roles_settings), + "ignore_genesis_failure": self.ignore_genesis_failure, + } + + +class CloneCellId(TaggedUnion): + """Identifies a clone cell, by clone id or by DNA hash.""" + + +@variant(CloneCellId, "clone_id") +@dataclass +class CloneCellByCloneId: + clone_id: CloneId = field(metadata={"newtype": True}) + + +@variant(CloneCellId, "dna_hash") +@dataclass +class CloneCellByDnaHash: + dna_hash: DnaHash = field(metadata={"newtype": True}) + + +@dataclass +class DeleteCloneCellPayload: + app_id: InstalledAppId + clone_cell_id: CloneCellId + + +@dataclass +class AppAuthenticationTokenIssued: + token: AppAuthenticationToken + expires_at: Optional[Timestamp] = None diff --git a/holochain_client/api/admin/client.py b/holochain_client/api/admin/client.py deleted file mode 100644 index 215c58e..0000000 --- a/holochain_client/api/admin/client.py +++ /dev/null @@ -1,166 +0,0 @@ -from typing import Any, List -import asyncio -import websockets -from holochain_client.api.admin.types import ( - AddAdminInterface, - AppEnabled, - AppInfo, - AppInterfaceAttached, - AttachAppInterface, - DisableApp, - DumpNetworkStats, - EnableApp, - GenerateAgentPubKey, - GrantZomeCallCapability, - InstallApp, - ListAppInterfaces, - ListApps, - ListCellIds, - ListDnas, - RegisterDnaPayload, - UninstallApp, -) -import json -from holochain_client.api.common.pending_request_pool import PendingRequestPool -from holochain_client.api.common.request import ( - create_wire_message_request, - tag_from_type, -) -from holochain_client.api.common.types import AgentPubKey, CellId, DnaHash -import inspect - - -class AdminClient: - url: str - defaultTimeout: int - - client: websockets.WebSocketClientProtocol - requestId: int = 0 - pendingRequestPool: PendingRequestPool - - def __init__(self, url: str, defaultTimeout: int = 60): - self.url = url - self.defaultTimeout = defaultTimeout - - @classmethod - async def create(cls, url: str, defaultTimeout: int = 60): - """The recommended way to create an AdminClient""" - self = cls(url, defaultTimeout) - await self.connect() - return self - - def connect_sync(self, event_loop): - return event_loop.run_until_complete(self.connect()) - - async def connect(self): - self.client = await websockets.connect(self.url) - self.pendingRequestPool = PendingRequestPool(self.client) - - async def add_admin_interfaces(self, request: List[AddAdminInterface]): - response = await self._exchange( - request, tag=inspect.currentframe().f_code.co_name - ) - assert response["type"] == "admin_interfaces_added", f"response was: {response}" - - async def register_dna(self, request: RegisterDnaPayload) -> DnaHash: - response = await self._exchange( - request, tag=inspect.currentframe().f_code.co_name - ) - assert response["type"] == "dna_registered", f"response was: {response}" - return response["data"] - - async def get_dna_definition(self, dna_hash: DnaHash) -> object: - response = await self._exchange( - dna_hash, tag=inspect.currentframe().f_code.co_name - ) - assert ( - response["type"] == "dna_definition_returned" - ), f"response was: {response}" - return response["data"] - - async def install_app(self, request: InstallApp) -> AppInfo: - response = await self._exchange(request, tag_from_type(request)) - assert response["type"] == "app_installed", f"response was: {response}" - return AppInfo(**response["data"]) - - async def uninstall_app(self, request: UninstallApp): - response = await self._exchange( - request, tag=inspect.currentframe().f_code.co_name - ) - assert response["type"] == "app_uninstalled", f"response was: {response}" - - async def list_dnas(self, request: ListDnas = ListDnas()) -> List[DnaHash]: - response = await self._exchange(request, tag_from_type(request)) - assert response["type"] == "dnas_listed", f"response was: {response}" - return response["data"] - - async def generate_agent_pub_key( - self, request: GenerateAgentPubKey = GenerateAgentPubKey() - ) -> AgentPubKey: - response = await self._exchange(request, tag_from_type(request)) - assert ( - response["type"] == "agent_pub_key_generated" - ), f"response was: {response}" - return response["data"] - - async def list_cell_ids(self, request: ListCellIds = ListCellIds()) -> List[CellId]: - response = await self._exchange(request, tag_from_type(request)) - assert response["type"] == "cell_ids_listed", f"response was: {response}" - print(response["data"]) - return response["data"] - - async def list_apps(self, request: ListApps = ListApps()) -> List[AppInfo]: - response = await self._exchange(request, tag_from_type(request)) - assert response["type"] == "apps_listed", f"response was: {response}" - return [AppInfo(**x) for x in response["data"]] - - async def enable_app(self, request: EnableApp) -> AppEnabled: - response = await self._exchange(request, tag_from_type(request)) - assert response["type"] == "app_enabled", f"response was: {response}" - return AppEnabled(*response["data"]) - - async def disable_app(self, request: DisableApp): - response = await self._exchange(request, tag_from_type(request)) - assert response["type"] == "app_disabled", f"response was: {response}" - - async def attach_app_interface( - self, request: AttachAppInterface = AttachAppInterface() - ) -> AppInterfaceAttached: - response = await self._exchange(request, tag_from_type(request)) - assert response["type"] == "app_interface_attached", f"response was: {response}" - return AppInterfaceAttached(port=int(response["data"]["port"])) - - async def list_app_interfaces( - self, request: ListAppInterfaces = ListAppInterfaces() - ) -> List[int]: - response = await self._exchange(request, tag_from_type(request)) - assert response["type"] == "app_interfaces_listed", f"response was: {response}" - return response["data"] - - async def dump_network_stats( - self, request: DumpNetworkStats = DumpNetworkStats() - ) -> object: - response = await self._exchange(request, tag_from_type(request)) - assert response["type"] == "network_stats_dumped", f"response was: {response}" - return json.loads(response["data"]) - - async def grant_zome_call_capability(self, request: GrantZomeCallCapability): - response = await self._exchange(request, tag_from_type(request)) - assert ( - response["type"] == "zome_call_capability_granted" - ), f"response was: {response}" - - async def close(self): - await self.client.close() - - async def _exchange(self, request: Any, tag: str) -> Any: - requestId = self.requestId - self.requestId += 1 - req = create_wire_message_request(request, tag, requestId) - await self.client.send(req) - - completed = asyncio.Event() - self.pendingRequestPool.add(requestId, completed) - await asyncio.wait_for(completed.wait(), self.defaultTimeout) - - return self.pendingRequestPool.take_response(requestId) diff --git a/holochain_client/api/admin/types.py b/holochain_client/api/admin/types.py deleted file mode 100644 index 4712f63..0000000 --- a/holochain_client/api/admin/types.py +++ /dev/null @@ -1,282 +0,0 @@ -from dataclasses import dataclass, field -from enum import Enum -from typing import Any, Dict, List, Optional, Tuple, Union -from holochain_client.api.common.types import ( - AgentPubKey, - CellId, - DnaHash, - MembraneProof, - NetworkSeed, - RoleName, -) - - -@dataclass -class WireMessageRequest: - id: int - data: List[int] - type: str = "request" - - -@dataclass -class AdminRequest: - type: str - data: Dict - - -@dataclass -class CellProvisioningCreate: - deferred: bool - strategy: str = "Create" - - -@dataclass -class CellProvisioningCloneOnly: - strategy: str = "CloneOnly" - - -@dataclass -class Duration: - secs: int - nanos: int - - -@dataclass -class DnaModifiers: - network_seed: Optional[NetworkSeed] = None - properties: Optional[Dict] = None - origin_time: Optional[int] = None - quantum_time: Optional[Duration] = None - - -@dataclass -class AppRoleDnaManifest: - bundled: str - modifiers: Dict - installed_hash: Optional[str] = None - clone_limit: int = 0 - - -@dataclass -class AppRoleManifest: - name: RoleName - dna: AppRoleDnaManifest - provisioning: Optional[ - Union[CellProvisioningCreate, CellProvisioningCloneOnly] - ] = None - - -@dataclass -class AppManifest: - manifest_version: str - name: str - description: Optional[str] - roles: List[AppRoleManifest] - - -@dataclass -class AppBundleSourceBundle: - manifest: AppManifest - resources: Dict[str, bytes] - - -@dataclass -class InstallApp: - agent_key: AgentPubKey - - # Exactly one of these must be set - bundle: Optional[AppBundleSourceBundle] = None - path: Optional[str] = None - - installed_app_id: Optional[str] = None - membrane_proofs: Dict[RoleName, MembraneProof] = field(default_factory=dict) - network_seed: Optional[NetworkSeed] = None - - -@dataclass -class GenerateAgentPubKey: - pass - - -AppStatusFilter = Enum( - "AppStatusFilter", ["Enabled", "Disabled", "Running", "Stopped", "Paused"] -) - - -@dataclass -class ListApps: - status_filter: Optional[AppStatusFilter] = None - - -@dataclass -class DumpNetworkStats: - pass - - -@dataclass -class CellInfoProvisioned: - cell_id: CellId - dna_modifiers: DnaModifiers - name: str - - -@dataclass -class CellInfoCloned: - cell_id: CellId - clone_id: str - original_dna_hash: DnaHash - dna_modifiers: DnaModifiers - name: str - enabled: bool - - -@dataclass -class CellInfoStem: - original_dna_hash: DnaHash - dna_modifiers: DnaModifiers - name: Optional[str] - - -@dataclass -class AppinfoStatusPaused: - reason: Any # TODO - type: str = "Paused" - - -@dataclass -class AppInfoStatusDisabled: - reason: Any # TODO - type: str = "Disabled" - - -@dataclass -class AppinfoStatusRunning: - type: str = "Running" - - -CellInfoKind = Enum("CellInfoKind", ["provisioned", "cloned", "stem"]) - - -@dataclass -class AppInfo: - installed_app_id: str - cell_info: Dict[ - RoleName, - List[ - Dict[CellInfoKind, Union[CellInfoProvisioned, CellInfoCloned, CellInfoStem]] - ], - ] - status: Union[AppinfoStatusPaused, AppInfoStatusDisabled, AppinfoStatusRunning] - agent_pub_key: AgentPubKey - manifest: AppManifest - - -@dataclass -class CapAccessUnrestricted: - type: str = "Unrestricted" - - -@dataclass -class CapAccessTransferable: - secret: bytes - type: str = "Transferable" - - -@dataclass -class CapAccessAssigned: - secret: bytes - assignees: List[AgentPubKey] - - -GrantedFunctionKind = Enum("GrantedFunctionKind", ["All", "Listed"]) -GrantedFunctions = Dict[GrantedFunctionKind, Union[None, List[str]]] - -CapAccessKind = Enum("CapAccessKind", ["Unrestricted", "Transferable", "Assigned"]) - - -@dataclass -class ZomeCallCapGrant: - tag: str - access: Dict[ - CapAccessAssigned, - Union[CapAccessUnrestricted, CapAccessTransferable, CapAccessAssigned], - ] - functions: GrantedFunctions - - -@dataclass -class GrantZomeCallCapability: - cell_id: CellId - cap_grant: ZomeCallCapGrant - - -@dataclass -class EnableApp: - installed_app_id: str - - -@dataclass -class AppEnabled: - app: AppInfo - errors: List[Tuple[CellId, str]] - - -@dataclass -class AttachAppInterface: - port: Optional[int] = None - - -@dataclass -class AppInterfaceAttached: - port: int - - -@dataclass -class ListAppInterfaces: - pass - - -@dataclass -class InterfaceDriverWebsocket: - port: int - type: str = "websocket" - - -@dataclass -class AddAdminInterface: - driver: Union[InterfaceDriverWebsocket] - - -@dataclass -class RegisterDnaPayloadPath: - path: str - modifiers: Optional[DnaModifiers] = None - - -@dataclass -class RegisterDnaPayloadHash: - hash: DnaHash - modifiers: DnaModifiers - - -RegisterDnaPayload = Union[RegisterDnaPayloadPath, RegisterDnaPayloadHash] - - -@dataclass -class UninstallApp: - installed_app_id: str - - -@dataclass -class ListDnas: - pass - - -@dataclass -class ListCellIds: - pass - - -@dataclass -class DisableApp: - installed_app_id: str diff --git a/holochain_client/api/app/client.py b/holochain_client/api/app/client.py deleted file mode 100644 index e56fe8e..0000000 --- a/holochain_client/api/app/client.py +++ /dev/null @@ -1,101 +0,0 @@ -from typing import Any -import asyncio -import websockets -from holochain_client.api.app.types import CallZome, ZomeCallUnsigned -from holochain_client.api.common.pending_request_pool import PendingRequestPool -from holochain_client.api.common.request import ( - create_wire_message_request, - tag_from_type, -) -from holochain_client.api.common.signing import get_from_creds_store -import os -from datetime import datetime -from holochain_serialization import ZomeCallUnsignedPy, get_data_to_sign - - -class AppClient: - url: str - defaultTimeout: int - - client: websockets.WebSocketClientProtocol - requestId: int = 0 - pendingRequestPool: PendingRequestPool - - def __init__(self, url: str, defaultTimeout: int = 60) -> None: - self.url = url - self.defaultTimeout = defaultTimeout - - @classmethod - async def create(cls, url: str, defaultTimeout: int = 60): - """The recommended way to create an AppClient""" - self = cls(url, defaultTimeout) - await self.connect() - return self - - def connect_sync(self, event_loop): - event_loop.run_until_complete(self.connect()) - - async def connect(self): - self.client = await websockets.connect(self.url) - self.pendingRequestPool = PendingRequestPool(self.client) - return self - - async def call_zome(self, request: ZomeCallUnsigned) -> bytes: - """ - :return: The literal response from the zome call - """ - signing_credentials = get_from_creds_store(request.cell_id) - if signing_credentials is None: - raise Exception( - f"No signing credentials have been authorized for cell_id: {request.cell_id}" - ) - - provenance = signing_credentials.signing_key.identity # Request is actually made on behalf of the siging credentials, not the current agent! - nonce = os.urandom(32) - expires_at = int((datetime.now().timestamp() + 5 * 60) * 1e6) - cap_secret = signing_credentials.cap_secret - - zome_call_py = ZomeCallUnsignedPy( - provenance, - request.cell_id[0], - request.cell_id[1], - request.zome_name, - request.fn_name, - request.payload, - nonce, - expires_at, - cap_secret=cap_secret, - ) - data_to_sign = bytes(get_data_to_sign(zome_call_py)) - signature = signing_credentials.signing_key.sign(data_to_sign) - - request = CallZome( - cell_id=request.cell_id, - zome_name=request.zome_name, - fn_name=request.fn_name, - payload=request.payload, - provenance=provenance, - signature=signature, - nonce=nonce, - expires_at=expires_at, - cap_secret=cap_secret, - ) - - response = await self._exchange(request, tag_from_type(request)) - assert response["type"] == "zome_called", f"response was: {response}" - return response["data"] - - async def close(self): - await self.client.close() - - async def _exchange(self, request: Any, tag: str) -> Any: - requestId = self.requestId - self.requestId += 1 - req = create_wire_message_request(request, tag, requestId) - await self.client.send(req) - - completed = asyncio.Event() - self.pendingRequestPool.add(requestId, completed) - await asyncio.wait_for(completed.wait(), self.defaultTimeout) - - return self.pendingRequestPool.take_response(requestId) diff --git a/holochain_client/api/app/types.py b/holochain_client/api/app/types.py deleted file mode 100644 index b53fac9..0000000 --- a/holochain_client/api/app/types.py +++ /dev/null @@ -1,31 +0,0 @@ -import dataclasses -from typing import Any - -from holochain_client.api.common.types import ( - AgentPubKey, - CellId, - FunctionName, - ZomeName, -) - - -@dataclasses.dataclass -class ZomeCallUnsigned: - cell_id: CellId - zome_name: ZomeName - fn_name: FunctionName - payload: Any - - -@dataclasses.dataclass -class CallZome: - cell_id: CellId - zome_name: ZomeName - fn_name: FunctionName - payload: bytes - provenance: AgentPubKey - signature: bytes - nonce: bytes - """Microseconds from unix epoch""" - expires_at: int - cap_secret: bytes diff --git a/holochain_client/api/common/__init__.py b/holochain_client/api/common/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/holochain_client/api/common/pending_request_pool.py b/holochain_client/api/common/pending_request_pool.py deleted file mode 100644 index b6d45e4..0000000 --- a/holochain_client/api/common/pending_request_pool.py +++ /dev/null @@ -1,39 +0,0 @@ -from typing import Any, Dict -import asyncio -import websockets -import msgpack -import dataclasses - - -@dataclasses.dataclass -class PendingRequest: - id: int - trigger: asyncio.Event - - -class PendingRequestPool: - _requests: Dict[int, PendingRequest] = {} - _responses: Dict[int, Any] = {} - - def __init__(self, client: websockets.WebSocketClientProtocol): - asyncio.create_task(self._handle_responses(client)) - - def add(self, id: int, trigger: asyncio.Event): - self._requests[id] = PendingRequest(id, trigger) - - async def _handle_responses(self, client: websockets.WebSocketClientProtocol): - async for message in client: - data = msgpack.unpackb(message) - if "id" in data and "type" in data and data["type"] == "response": - id = data["id"] - if id in self._requests: - data["data"] = msgpack.unpackb(data["data"]) - self._responses[id] = data - self._requests[id].trigger.set() - del self._requests[id] - else: - # TODO logging - print(f"Received response for unknown request id: {id}") - - def take_response(self, id: int) -> Any: - return self._responses.pop(id)["data"] diff --git a/holochain_client/api/common/request.py b/holochain_client/api/common/request.py deleted file mode 100644 index 1b0764a..0000000 --- a/holochain_client/api/common/request.py +++ /dev/null @@ -1,38 +0,0 @@ -from typing import Any -from holochain_client.api.admin.types import ( - AdminRequest, - WireMessageRequest, -) -import msgpack -import dataclasses -import re - - -def tag_from_type(req: Any) -> str: - # TODO compile - return re.sub(r"(? bytes: - if isinstance(req, list): - data = [dataclasses.asdict(x) for x in req] - elif isinstance(req, bytes): - data = req - else: - data = dataclasses.asdict(req) - - if len(data) == 0: - # If there is no data, we should send None instead of an empty dict - data = None - elif isinstance(data, dict): - # Remove any None values from the data, no need to send fields without values - data = {k: v for k, v in data.items() if v is not None} - req = AdminRequest(tag, data) - # print("Sending request: ", req) # TODO logging - return msgpack.packb(dataclasses.asdict(req)) - - -def create_wire_message_request(req: Any, tag: str, requestId: int) -> bytes: - data = _create_request(req, tag) - msg = WireMessageRequest(id=requestId, data=[x for x in data]) - return msgpack.packb(dataclasses.asdict(msg)) diff --git a/holochain_client/api/common/signing.py b/holochain_client/api/common/signing.py deleted file mode 100644 index be30ed6..0000000 --- a/holochain_client/api/common/signing.py +++ /dev/null @@ -1,90 +0,0 @@ -import dataclasses -from typing import Dict, Optional -from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey -from holochain_client.api.admin.client import AdminClient -from holochain_client.api.admin.types import ( - CapAccessAssigned, - GrantZomeCallCapability, - GrantedFunctions, - ZomeCallCapGrant, -) -from holochain_client.api.common.types import AgentPubKey, CellId -import os -import base64 - - -class SigningKeyWithIdentity: - _signing_key: Ed25519PrivateKey - - """This will be recognised by Holochain as an agent public key but just contains the generated signing key associated with the signing keypair above.""" - identity: AgentPubKey - - def __init__(self, signing_key: Ed25519PrivateKey): - self._signing_key = signing_key - - # The first 3 bytes are the constant AGENT_PREFIX from holochain/holochain and the last 4 bytes are the location bytes which - # are deliberately set to [0, 0, 0, 0] since this agent identity is only used for signing and so its location is not relevant. - self.identity = bytes( - [ - 132, - 32, - 36, - *signing_key.public_key().public_bytes_raw(), - 0, - 0, - 0, - 0, - ] - ) - assert len(self.identity) == 39 - - def sign(self, data: bytes) -> bytes: - return self._signing_key.sign(data) - - -@dataclasses.dataclass -class SigningCredentials: - cap_secret: bytes - signing_key: SigningKeyWithIdentity - - -def generate_signing_keypair() -> SigningKeyWithIdentity: - signing_key = Ed25519PrivateKey.generate() - - return SigningKeyWithIdentity(signing_key) - - -CREDS_STORE: Dict[str, SigningCredentials] = {} - - -def _add_to_creds_store(cell_id: CellId, creds: SigningCredentials): - CREDS_STORE[base64.b64encode(bytes([*cell_id[0], *cell_id[1]]))] = creds - - -def get_from_creds_store(cell_id: CellId) -> Optional[SigningCredentials]: - return CREDS_STORE.get(base64.b64encode(bytes([*cell_id[0], *cell_id[1]]))) - - -async def authorize_signing_credentials( - admin_client: AdminClient, - cell_id: CellId, - functions: Optional[GrantedFunctions] = None, -): - signing_keypair = generate_signing_keypair() - cap_secret = os.urandom(64) - await admin_client.grant_zome_call_capability( - GrantZomeCallCapability( - cell_id=cell_id, - cap_grant=ZomeCallCapGrant( - tag="zome-call-signing-key", - functions=functions if functions else {"All": None}, - access={ - "Assigned": CapAccessAssigned( - secret=cap_secret, assignees=[signing_keypair.identity] - ) - }, - ), - ) - ) - - _add_to_creds_store(cell_id, SigningCredentials(cap_secret, signing_keypair)) diff --git a/holochain_client/api/common/types.py b/holochain_client/api/common/types.py deleted file mode 100644 index 84afd9e..0000000 --- a/holochain_client/api/common/types.py +++ /dev/null @@ -1,8 +0,0 @@ -DnaHash = bytes -AgentPubKey = bytes -CellId = [DnaHash, AgentPubKey] -ZomeName = str -FunctionName = str -RoleName = str -MembraneProof = bytes -NetworkSeed = str diff --git a/holochain_client/app/__init__.py b/holochain_client/app/__init__.py new file mode 100644 index 0000000..79ae909 --- /dev/null +++ b/holochain_client/app/__init__.py @@ -0,0 +1,4 @@ +from .client import AppClient +from .types import AppSignal + +__all__ = ["AppClient", "AppSignal"] diff --git a/holochain_client/app/client.py b/holochain_client/app/client.py new file mode 100644 index 0000000..549b6db --- /dev/null +++ b/holochain_client/app/client.py @@ -0,0 +1,128 @@ +"""The app interface client.""" + +from __future__ import annotations + +from typing import Any, Callable, List, Optional, Union + +import msgpack + +from .._api import ApiClient +from .._transport import ConductorError, DEFAULT_ORIGIN, WebsocketConnection +from ..signing import CredentialStore, SigningCredentials, sign_zome_call +from ..types.app import AppInfo +from ..types.cell import CellId +from ..types.common import AppAuthenticationToken, FunctionName, ZomeName +from .types import AppSignal, SystemSignal + +SignalCallback = Callable[[Union[AppSignal, SystemSignal]], Any] + + +class AppClient(ApiClient): + """Client for a conductor app websocket interface. + + An app websocket must be authenticated with a token issued by the admin + interface (:meth:`AdminClient.issue_app_authentication_token`) before any + request will be answered. + """ + + def __init__(self, connection: WebsocketConnection): + super().__init__(connection) + self.credentials = CredentialStore() + self._signal_callbacks: List[SignalCallback] = [] + connection.on_signal(self._handle_signal) + + @classmethod + async def connect( + cls, + url: str, + token: AppAuthenticationToken, + *, + origin: str = DEFAULT_ORIGIN, + ) -> "AppClient": + connection = await WebsocketConnection.connect(url, origin=origin) + client = cls(connection) + await connection.authenticate({"token": token}) + return client + + # -- Signals ----------------------------------------------------------- # + def on_signal(self, callback: SignalCallback) -> None: + self._signal_callbacks.append(callback) + + def _handle_signal(self, data: bytes) -> None: + signal = msgpack.unpackb(data, raw=False) + kind = signal.get("type") + value = signal.get("value") + if kind == "app": + decoded = AppSignal( + cell_id=CellId.__from_wire__(value["cell_id"]), + zome_name=value["zome_name"], + payload=msgpack.unpackb(value["signal"], raw=False), + ) + elif kind == "system": + decoded = SystemSignal(value=value) + else: + return + for callback in self._signal_callbacks: + callback(decoded) + + # -- App info ---------------------------------------------------------- # + async def app_info(self) -> Optional[AppInfo]: + return await self._send( + "app_info", response_type=Optional[AppInfo], expect="app_info" + ) + + # -- Zome calls -------------------------------------------------------- # + async def call_zome( + self, + cell_id: CellId, + zome_name: ZomeName, + fn_name: FunctionName, + payload: Any = None, + *, + credentials: Optional[SigningCredentials] = None, + ) -> Any: + """Sign and make a zome call, returning the MessagePack-decoded result.""" + credentials = credentials or self.credentials.get(cell_id) + if credentials is None: + raise ConductorError( + f"no signing credentials authorized for cell {cell_id!r}" + ) + signed = sign_zome_call( + cell_id=cell_id, + zome_name=zome_name, + fn_name=fn_name, + payload=payload, + provenance=credentials.signing_key.agent_pub_key, + signing_key=credentials.signing_key, + cap_secret=credentials.cap_secret, + ) + result = await self._send( + "call_zome", + {"bytes": signed.bytes, "signature": signed.signature}, + expect="zome_called", + ) + # The result is the zome's return value as MessagePack bytes. + return msgpack.unpackb(result, raw=False) + + # -- Network introspection (pass-through) ------------------------------ # + async def dump_network_metrics( + self, + *, + dna_hash: Optional[Any] = None, + include_dht_summary: bool = False, + ) -> Any: + return await self._send( + "dump_network_metrics", + {"dna_hash": dna_hash, "include_dht_summary": include_dht_summary}, + expect="network_metrics_dumped", + ) + + async def dump_network_stats(self) -> Any: + return await self._send("dump_network_stats", expect="network_stats_dumped") + + async def list_wasm_host_functions(self) -> List[str]: + return await self._send( + "list_wasm_host_functions", + response_type=List[str], + expect="list_wasm_host_functions", + ) diff --git a/holochain_client/app/types.py b/holochain_client/app/types.py new file mode 100644 index 0000000..5d8325f --- /dev/null +++ b/holochain_client/app/types.py @@ -0,0 +1,26 @@ +"""Types specific to the app interface.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +from ..types.cell import CellId +from ..types.common import ZomeName + + +@dataclass +class AppSignal: + """An application signal emitted by a cell via ``emit_signal``.""" + + cell_id: CellId + zome_name: ZomeName + # The emitted payload, MessagePack-decoded. + payload: Any + + +@dataclass +class SystemSignal: + """A system-emitted signal.""" + + value: Any diff --git a/holochain_client/hash.py b/holochain_client/hash.py new file mode 100644 index 0000000..a526117 --- /dev/null +++ b/holochain_client/hash.py @@ -0,0 +1,129 @@ +"""Holochain ``HoloHash`` types. + +A HoloHash is 39 bytes: a 3-byte multihash type prefix, a 32-byte hash +("core"), and a 4-byte DHT location. They are modelled here as subclasses of +``bytes`` so they serialize transparently as msgpack ``bin`` while still +carrying type information and validation. + +The human readable form is multibase base64url (no padding) with a leading +``u``, e.g. ``uhCkk...`` for an :class:`ActionHash`. +""" + +from __future__ import annotations + +import base64 + +# 3-byte multihash prefixes, from holo_hash/src/hash_type/primitive.rs +AGENT_PREFIX = bytes([0x84, 0x20, 0x24]) +ENTRY_PREFIX = bytes([0x84, 0x21, 0x24]) +DHTOP_PREFIX = bytes([0x84, 0x24, 0x24]) +WARRANT_PREFIX = bytes([0x84, 0x2C, 0x24]) +DNA_PREFIX = bytes([0x84, 0x2D, 0x24]) +ACTION_PREFIX = bytes([0x84, 0x29, 0x24]) +WASM_PREFIX = bytes([0x84, 0x2A, 0x24]) +EXTERNAL_PREFIX = bytes([0x84, 0x2F, 0x24]) + +HASH_LENGTH = 39 +PREFIX_LENGTH = 3 +CORE_LENGTH = 32 +LOCATION_LENGTH = 4 + + +class HoloHash(bytes): + """Base class for all 39-byte Holochain hashes. + + Subclasses pin a specific 3-byte ``prefix``. ``HoloHash`` itself accepts any + valid 39-byte hash regardless of prefix. + """ + + prefix: bytes | None = None + + def __new__(cls, value: bytes): + if len(value) != HASH_LENGTH: + raise ValueError( + f"{cls.__name__} must be {HASH_LENGTH} bytes, got {len(value)}" + ) + if cls.prefix is not None and bytes(value[:PREFIX_LENGTH]) != cls.prefix: + raise ValueError( + f"{cls.__name__} expects prefix {list(cls.prefix)}, " + f"got {list(value[:PREFIX_LENGTH])}" + ) + return super().__new__(cls, value) + + @property + def core(self) -> bytes: + """The 32-byte hash, without prefix or location.""" + return bytes(self[PREFIX_LENGTH : PREFIX_LENGTH + CORE_LENGTH]) + + @property + def location(self) -> bytes: + """The 4-byte DHT location suffix.""" + return bytes(self[-LOCATION_LENGTH:]) + + def to_b64(self) -> str: + """Multibase base64url (no padding) form with leading ``u``.""" + return "u" + base64.urlsafe_b64encode(self).decode("ascii").rstrip("=") + + @classmethod + def from_b64(cls, value: str): + if value.startswith("u"): + value = value[1:] + padding = "=" * (-len(value) % 4) + return cls(base64.urlsafe_b64decode(value + padding)) + + def __repr__(self) -> str: + return f"{type(self).__name__}({self.to_b64()})" + + +class AgentPubKey(HoloHash): + prefix = AGENT_PREFIX + + +class EntryHash(HoloHash): + prefix = ENTRY_PREFIX + + +class DhtOpHash(HoloHash): + prefix = DHTOP_PREFIX + + +class WarrantHash(HoloHash): + prefix = WARRANT_PREFIX + + +class DnaHash(HoloHash): + prefix = DNA_PREFIX + + +class ActionHash(HoloHash): + prefix = ACTION_PREFIX + + +class WasmHash(HoloHash): + prefix = WASM_PREFIX + + +class ExternalHash(HoloHash): + prefix = EXTERNAL_PREFIX + + +# AnyDhtHash can be an Entry or Action hash; AnyLinkableHash adds External. +AnyDhtHash = HoloHash +AnyLinkableHash = HoloHash + +_PREFIX_TO_CLASS = { + AGENT_PREFIX: AgentPubKey, + ENTRY_PREFIX: EntryHash, + DHTOP_PREFIX: DhtOpHash, + WARRANT_PREFIX: WarrantHash, + DNA_PREFIX: DnaHash, + ACTION_PREFIX: ActionHash, + WASM_PREFIX: WasmHash, + EXTERNAL_PREFIX: ExternalHash, +} + + +def hash_from_raw(value: bytes) -> HoloHash: + """Build the most specific ``HoloHash`` subclass for ``value`` by prefix.""" + cls = _PREFIX_TO_CLASS.get(bytes(value[:PREFIX_LENGTH]), HoloHash) + return cls(value) diff --git a/holochain_client/serde.py b/holochain_client/serde.py new file mode 100644 index 0000000..09f4650 --- /dev/null +++ b/holochain_client/serde.py @@ -0,0 +1,261 @@ +"""Type-driven (de)serialization between Python models and Holochain's wire format. + +Holochain serializes with MessagePack via ``serde``. The conventions we mirror: + +* structs become maps keyed by ``snake_case`` field names; +* enums are *adjacently tagged* as ``{"type": , "value": }`` + (the ``value`` key is absent for unit variants); +* byte sequences (including ``HoloHash``) are msgpack ``bin``; +* ``Option`` is either the encoded ``T`` or ``nil``. + +Encoding (:func:`encode`) walks a Python object and produces msgpack-ready +primitives. Decoding (:func:`decode`) is *target-typed*: the caller supplies the +expected type, so we never have to guess a structure from its shape — the +failure mode that made the previous client unreliable. +""" + +from __future__ import annotations + +import dataclasses +import enum +import typing +from typing import Any, Union, get_args, get_origin + +from .hash import HoloHash + +NoneType = type(None) + + +# --------------------------------------------------------------------------- # +# Protocol hooks +# --------------------------------------------------------------------------- # +# A type may implement custom wire behaviour with: +# * instance method ``__to_wire__(self) -> Any`` +# * classmethod ``__from_wire__(cls, data: Any) -> instance`` +# These take precedence over the generic dataclass handling below. + + +def encode(obj: Any) -> Any: + """Convert ``obj`` into msgpack-ready primitives.""" + if obj is None: + return None + if isinstance(obj, HoloHash): + return bytes(obj) + if isinstance(obj, (bytes, bytearray)): + return bytes(obj) + if isinstance(obj, bool) or isinstance(obj, (int, float, str)): + return obj + if hasattr(obj, "__to_wire__"): + return obj.__to_wire__() + if isinstance(obj, enum.Enum): + # Plain C-like enums serialize as their snake_case name. + return obj.value if isinstance(obj.value, str) else obj.name + if dataclasses.is_dataclass(obj) and not isinstance(obj, type): + out: dict[str, Any] = {} + for f in dataclasses.fields(obj): + if f.metadata.get("skip"): + continue + wire_name = f.metadata.get("rename", f.name) + value = getattr(obj, f.name) + if value is None and f.metadata.get("omit_none"): + continue + out[wire_name] = encode(value) + return out + if isinstance(obj, dict): + return {k: encode(v) for k, v in obj.items()} + if isinstance(obj, (list, tuple)): + return [encode(v) for v in obj] + raise TypeError(f"Cannot encode object of type {type(obj)!r}") + + +def decode(data: Any, typ: Any) -> Any: + """Decode msgpack-decoded ``data`` into the expected ``typ``.""" + if typ is None or typ is Any or typ is NoneType: + return data + + # Custom hook. + from_wire = getattr(typ, "__from_wire__", None) + if from_wire is not None and isinstance(typ, type): + return from_wire(data) + + origin = get_origin(typ) + + # Optional / Union. + if origin is Union: + args = [a for a in get_args(typ) if a is not NoneType] + if data is None: + return None + if len(args) == 1: + return decode(data, args[0]) + # Best-effort for genuine unions: try each until one fits. + for arg in args: + try: + return decode(data, arg) + except Exception: + continue + return data + + if origin in (list, typing.List): + (item_t,) = get_args(typ) or (Any,) + return [decode(v, item_t) for v in data] + + if origin in (tuple, typing.Tuple): + args = get_args(typ) + if not args: + return tuple(data) + if len(args) == 2 and args[1] is Ellipsis: + return tuple(decode(v, args[0]) for v in data) + return tuple(decode(v, t) for v, t in zip(data, args)) + + if origin in (dict, typing.Dict): + args = get_args(typ) or (Any, Any) + kt, vt = args + return {decode(k, kt): decode(v, vt) for k, v in data.items()} + + if isinstance(typ, type) and issubclass(typ, HoloHash): + if typ is HoloHash: + # A generic hash (e.g. AnyLinkableHash): resolve by its prefix. + from .hash import hash_from_raw + + return hash_from_raw(data) + return typ(data) + + if isinstance(typ, type) and issubclass(typ, enum.Enum): + try: + return typ(data) + except ValueError: + return typ[data] + + if isinstance(typ, type) and dataclasses.is_dataclass(typ): + return _decode_dataclass(data, typ) + + # Primitives and anything we don't specialise. + return data + + +def _decode_dataclass(data: Any, typ: type) -> Any: + hints = typing.get_type_hints(typ) + kwargs: dict[str, Any] = {} + for f in dataclasses.fields(typ): + wire_name = f.metadata.get("rename", f.name) + if isinstance(data, dict) and wire_name in data: + kwargs[f.name] = decode(data[wire_name], hints.get(f.name, Any)) + elif f.default is not dataclasses.MISSING: + kwargs[f.name] = f.default + elif f.default_factory is not dataclasses.MISSING: # type: ignore[misc] + kwargs[f.name] = f.default_factory() + else: + kwargs[f.name] = None + return typ(**kwargs) + + +# --------------------------------------------------------------------------- # +# Adjacently-tagged unions (Rust enums) +# --------------------------------------------------------------------------- # +def variant(union: type, tag: str): + """Register a dataclass as a ``tag`` variant of an adjacently-tagged union. + + Usage:: + + class CellInfo(TaggedUnion): ... + + @variant(CellInfo, "provisioned") + @dataclass + class ProvisionedCell: ... + """ + + def register(cls): + union._variants[tag] = cls # type: ignore[attr-defined] + cls._tag = tag + cls._union = union + cls.__to_wire__ = to_tagged + return cls + + return register + + +class TaggedUnion: + """Base for Rust enums that carry a variant tag. + + Subclass it to declare a union, then attach variant dataclasses with + :func:`variant`. Three ``serde`` tagging styles are supported via class + attributes on the subclass: + + * ``"adjacent"`` (default) — ``{tag_key: , content_key: }``, + e.g. ``#[serde(tag = "type", content = "value")]``. Unit variants omit the + content key. + * ``"internal"`` — ``{tag_key: , ...fields}``: the variant's struct + fields are flattened alongside the tag, e.g. ``#[serde(tag = "type")]`` as + used by ``Action``. + * ``"external"`` — a bare string for unit variants, or ``{: content}`` + for data-carrying variants (serde's default), as used by ``RecordEntry``. + + ``tag_key``/``content_key`` default to ``"type"``/``"value"`` and can be + overridden (``Entry`` uses ``"entry_type"``/``"entry"``). + """ + + _variants: dict[str, type] = {} + style: str = "adjacent" + tag_key: str = "type" + content_key: str = "value" + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + cls._variants = {} + + @classmethod + def __from_wire__(cls, data: Any) -> Any: + if cls.style == "external": + if isinstance(data, str): + tag, content = data, None + else: + tag = next(iter(data)) + content = data[tag] + else: + tag = data[cls.tag_key] + content = data if cls.style == "internal" else data.get(cls.content_key) + + target = cls._variants.get(tag) + if target is None: + raise ValueError(f"Unknown {cls.__name__} variant {tag!r}") + + fields = dataclasses.fields(target) + if not fields: + return target() + if len(fields) == 1 and fields[0].metadata.get("newtype"): + hints = typing.get_type_hints(target) + return target(decode(content, hints[fields[0].name])) + return _decode_dataclass(content, target) + + +def to_tagged(obj: Any) -> dict: + """Encode a tagged-union variant instance to its union's wire form.""" + union = getattr(obj, "_union") + tag = getattr(obj, "_tag") + fields = dataclasses.fields(obj) + + is_newtype = len(fields) == 1 and fields[0].metadata.get("newtype") + if is_newtype: + content: Any = encode(getattr(obj, fields[0].name)) + elif fields: + # Encode the variant's fields as a map directly; calling encode(obj) + # here would re-enter __to_wire__ and recurse forever. + content = { + f.metadata.get("rename", f.name): encode(getattr(obj, f.name)) + for f in fields + if not f.metadata.get("skip") + } + else: + content = None + + if union.style == "external": + return tag if not fields else {tag: content} + if union.style == "internal": + out = {union.tag_key: tag} + if isinstance(content, dict): + out.update(content) + return out + out = {union.tag_key: tag} + if fields: + out[union.content_key] = content + return out diff --git a/holochain_client/signing.py b/holochain_client/signing.py new file mode 100644 index 0000000..83d6f82 --- /dev/null +++ b/holochain_client/signing.py @@ -0,0 +1,121 @@ +"""Client-side zome-call signing. + +Holochain authorizes zome calls with an ephemeral ed25519 *signing key* (not the +agent's real key) that has been granted a capability on the conductor. To sign a +call we serialize the :class:`ZomeCallParams` with MessagePack, SHA-512 the +bytes, sign the hash, and transmit ``{bytes, signature}`` — the conductor +verifies by hashing the bytes it received, so our encoding need only round-trip +through its ``serde`` deserializer. +""" + +from __future__ import annotations + +import hashlib +import secrets +import time +from dataclasses import dataclass +from typing import Dict, Optional + +from nacl.signing import SigningKey + +from .hash import AGENT_PREFIX, AgentPubKey +from .serde import encode +from .types.cell import CellId +from .types.common import CapSecret + +# Zome calls expire 5 minutes after signing, matching the JS client. +NONCE_EXPIRY_SECONDS = 5 * 60 + + +class SigningKeyPair: + """An ephemeral ed25519 keypair plus its Holochain ``AgentPubKey`` form.""" + + def __init__(self, signing_key: Optional[SigningKey] = None): + self._signing_key = signing_key or SigningKey.generate() + public_key = bytes(self._signing_key.verify_key) + # A signing key is presented to Holochain as a 39-byte AgentPubKey: + # the agent prefix, the 32-byte public key, and a zeroed DHT location + # (the location is irrelevant for a signing-only identity). + self.agent_pub_key = AgentPubKey(AGENT_PREFIX + public_key + bytes(4)) + + def sign(self, data: bytes) -> bytes: + """Return the 64-byte detached ed25519 signature of ``data``.""" + return self._signing_key.sign(data).signature + + +@dataclass +class SigningCredentials: + """Credentials authorizing zome calls for a single cell.""" + + cap_secret: CapSecret + signing_key: SigningKeyPair + + +def random_cap_secret() -> CapSecret: + return secrets.token_bytes(64) + + +def random_nonce() -> bytes: + return secrets.token_bytes(32) + + +def nonce_expires_at() -> int: + """Expiry timestamp in microseconds since the Unix epoch.""" + return (int(time.time() * 1000) + NONCE_EXPIRY_SECONDS * 1000) * 1000 + + +@dataclass +class SignedZomeCall: + """The ``ZomeCallParamsSigned`` payload sent to the conductor.""" + + bytes: bytes + signature: bytes + + +def sign_zome_call( + *, + cell_id: CellId, + zome_name: str, + fn_name: str, + payload: object, + provenance: AgentPubKey, + signing_key: SigningKeyPair, + cap_secret: Optional[CapSecret], + nonce: Optional[bytes] = None, + expires_at: Optional[int] = None, +) -> SignedZomeCall: + """Serialize and sign a set of zome-call parameters. + + ``payload`` is the (already application-shaped) zome input; it is itself + MessagePack-encoded before being embedded in the params, matching Holochain's + ``ExternIO`` field. + """ + import msgpack + + params = { + "provenance": bytes(provenance), + "cell_id": [bytes(cell_id.dna_hash), bytes(cell_id.agent_pub_key)], + "zome_name": zome_name, + "fn_name": fn_name, + "cap_secret": cap_secret, + "payload": msgpack.packb(encode(payload), use_bin_type=True), + "nonce": nonce if nonce is not None else random_nonce(), + "expires_at": expires_at if expires_at is not None else nonce_expires_at(), + } + data = msgpack.packb(params, use_bin_type=True) + bytes_hash = hashlib.sha512(data).digest() + signature = signing_key.sign(bytes_hash) + return SignedZomeCall(bytes=data, signature=signature) + + +class CredentialStore: + """In-memory store of :class:`SigningCredentials`, keyed by cell.""" + + def __init__(self): + self._store: Dict[CellId, SigningCredentials] = {} + + def set(self, cell_id: CellId, credentials: SigningCredentials) -> None: + self._store[cell_id] = credentials + + def get(self, cell_id: CellId) -> Optional[SigningCredentials]: + return self._store.get(cell_id) diff --git a/holochain_client/api/__init__.py b/holochain_client/types/__init__.py similarity index 100% rename from holochain_client/api/__init__.py rename to holochain_client/types/__init__.py diff --git a/holochain_client/types/app.py b/holochain_client/types/app.py new file mode 100644 index 0000000..5590c61 --- /dev/null +++ b/holochain_client/types/app.py @@ -0,0 +1,88 @@ +"""Application-level types: ``AppInfo``, ``AppStatus``, app interface info.""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Any, Dict, List, Optional + +from ..hash import AgentPubKey +from ..serde import TaggedUnion, variant +from .cell import CellInfo +from .common import InstalledAppId, RoleName, Timestamp + + +def _newtype(typ): + """Field for a single-payload (newtype) enum variant.""" + return field(metadata={"newtype": True}) + + +class DisabledAppReason(TaggedUnion): + """Why an app is disabled.""" + + +@variant(DisabledAppReason, "never_started") +@dataclass +class NeverStarted: + pass + + +@variant(DisabledAppReason, "not_started_after_providing_memproofs") +@dataclass +class NotStartedAfterProvidingMemproofs: + pass + + +@variant(DisabledAppReason, "user") +@dataclass +class DisabledByUser: + pass + + +@variant(DisabledAppReason, "error") +@dataclass +class DisabledByError: + message: str = _newtype(str) + + +class AppStatus(TaggedUnion): + """An app's lifecycle status.""" + + +@variant(AppStatus, "enabled") +@dataclass +class AppEnabled: + pass + + +@variant(AppStatus, "disabled") +@dataclass +class AppDisabled: + reason: DisabledAppReason = _newtype(DisabledAppReason) + + +@variant(AppStatus, "awaiting_memproofs") +@dataclass +class AppAwaitingMemproofs: + pass + + +@dataclass +class AppInfo: + """Information about an installed app.""" + + installed_app_id: InstalledAppId + cell_info: Dict[RoleName, List[CellInfo]] + status: AppStatus + agent_pub_key: AgentPubKey + # The original ``AppManifest`` is left as a decoded structure (pass-through). + manifest: Any + installed_at: Timestamp + + +@dataclass +class AppInterfaceInfo: + """An attached app interface, as listed by ``list_app_interfaces``.""" + + port: int + allowed_origins: Any + installed_app_id: Optional[InstalledAppId] = None diff --git a/holochain_client/types/capability.py b/holochain_client/types/capability.py new file mode 100644 index 0000000..d778b2f --- /dev/null +++ b/holochain_client/types/capability.py @@ -0,0 +1,67 @@ +"""Capability grant types for authorizing zome calls.""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import List, Tuple + +from ..hash import AgentPubKey +from ..serde import TaggedUnion, variant +from .cell import CellId +from .common import CapSecret, FunctionName, ZomeName + +GrantedFunction = Tuple[ZomeName, FunctionName] + + +class GrantedFunctions(TaggedUnion): + """Which zome functions a capability grants access to.""" + + +@variant(GrantedFunctions, "all") +@dataclass +class GrantedFunctionsAll: + pass + + +@variant(GrantedFunctions, "listed") +@dataclass +class GrantedFunctionsListed: + functions: List[GrantedFunction] = field(metadata={"newtype": True}) + + +class CapAccess(TaggedUnion): + """Access requirements for a capability grant.""" + + +@variant(CapAccess, "unrestricted") +@dataclass +class CapAccessUnrestricted: + pass + + +@variant(CapAccess, "transferable") +@dataclass +class CapAccessTransferable: + secret: CapSecret + + +@variant(CapAccess, "assigned") +@dataclass +class CapAccessAssigned: + secret: CapSecret + assignees: List[AgentPubKey] + + +@dataclass +class ZomeCallCapGrant: + """A capability grant for making zome calls.""" + + tag: str + access: CapAccess + functions: GrantedFunctions + + +@dataclass +class GrantZomeCallCapabilityPayload: + cell_id: CellId + cap_grant: ZomeCallCapGrant diff --git a/holochain_client/types/cell.py b/holochain_client/types/cell.py new file mode 100644 index 0000000..f7646f9 --- /dev/null +++ b/holochain_client/types/cell.py @@ -0,0 +1,75 @@ +"""Cell-related types: ``CellId``, DNA modifiers, and ``CellInfo``.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Optional + +from ..hash import AgentPubKey, DnaHash +from ..serde import TaggedUnion, variant +from .common import NetworkSeed + +# A clone cell identifier, formatted ``.``. +CloneId = str + + +@dataclass(frozen=True) +class CellId: + """Identifies a cell: a ``(DnaHash, AgentPubKey)`` pair. + + Serializes as a 2-element array, matching Rust's tuple-struct ``CellId``. + """ + + dna_hash: DnaHash + agent_pub_key: AgentPubKey + + def __to_wire__(self) -> list: + return [bytes(self.dna_hash), bytes(self.agent_pub_key)] + + @classmethod + def __from_wire__(cls, data: Any) -> "CellId": + return cls(DnaHash(data[0]), AgentPubKey(data[1])) + + +@dataclass +class DnaModifiers: + """Modifiers that, together with the integrity zomes, define a DNA hash.""" + + network_seed: NetworkSeed + # ``SerializedBytes`` of the DNA properties (msgpack-encoded), or ``None``. + properties: Optional[bytes] = None + + +class CellInfo(TaggedUnion): + """Information about a cell, as returned in ``AppInfo``.""" + + +@variant(CellInfo, "provisioned") +@dataclass +class ProvisionedCell: + cell_id: CellId + dna_modifiers: DnaModifiers + name: str + + +@variant(CellInfo, "cloned") +@dataclass +class ClonedCell: + cell_id: CellId + clone_id: CloneId + original_dna_hash: DnaHash + dna_modifiers: DnaModifiers + name: str + enabled: bool + + +@variant(CellInfo, "stem") +@dataclass +class StemCell: + original_dna_hash: DnaHash + dna_modifiers: DnaModifiers + name: Optional[str] = None + + +def decode_cell_info(data: Any) -> Any: + return CellInfo.__from_wire__(data) diff --git a/holochain_client/types/common.py b/holochain_client/types/common.py new file mode 100644 index 0000000..fe1e7dd --- /dev/null +++ b/holochain_client/types/common.py @@ -0,0 +1,22 @@ +"""Common scalar type aliases shared across the conductor API. + +These mirror the Rust newtypes/aliases. They are documentation-only aliases: +the wire representation is the underlying primitive. +""" + +from __future__ import annotations + +# String identifiers. +ZomeName = str +FunctionName = str +RoleName = str +InstalledAppId = str +NetworkSeed = str + +# Microseconds since the Unix epoch (Holochain ``Timestamp``). +Timestamp = int + +# Raw byte secrets / tokens. +CapSecret = bytes # 64 bytes +Nonce = bytes # 32 bytes +AppAuthenticationToken = bytes diff --git a/holochain_client/types/record.py b/holochain_client/types/record.py new file mode 100644 index 0000000..e5d3a78 --- /dev/null +++ b/holochain_client/types/record.py @@ -0,0 +1,327 @@ +"""Source-chain ``Record`` types: ``Action`` (and its variants), ``Entry``, and +the wrappers that tie them together. + +Zome calls return application data as opaque MessagePack, so the client cannot +type those results automatically. When a zome returns a ``Record`` (or a list of +them), decode it explicitly:: + + from holochain_client.types.record import Record, decode_record + + raw = await app.call_zome(cell_id, "my_zome", "get_thing", action_hash) + record = decode_record(raw) + record.signed_action.hashed.hash # ActionHash + record.action() # the Action variant + record.entry # RecordEntry +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Any, List, Optional + +from ..hash import ( + ActionHash, + AgentPubKey, + AnyLinkableHash, + DnaHash, + EntryHash, +) +from ..serde import TaggedUnion, decode, variant +from .common import Timestamp + +# Entry visibility serializes as the bare string "Public" / "Private". +EntryVisibility = str + +# A signature is 64 raw bytes; link tags are arbitrary bytes. +Signature = bytes +LinkTag = bytes + + +def _newtype(): + return field(metadata={"newtype": True}) + + +# --------------------------------------------------------------------------- # +# Entry +# --------------------------------------------------------------------------- # +class MigrationTarget(TaggedUnion): + style = "external" + + +@variant(MigrationTarget, "Dna") +@dataclass +class MigrationToDna: + dna_hash: DnaHash = _newtype() + + +@variant(MigrationTarget, "Agent") +@dataclass +class MigrationToAgent: + agent: AgentPubKey = _newtype() + + +@dataclass +class AppEntryDef: + entry_index: int + zome_index: int + visibility: EntryVisibility + + +class EntryType(TaggedUnion): + style = "external" + + +@variant(EntryType, "AgentPubKey") +@dataclass +class EntryTypeAgentPubKey: + pass + + +@variant(EntryType, "App") +@dataclass +class EntryTypeApp: + app_entry_def: AppEntryDef = _newtype() + + +@variant(EntryType, "CapClaim") +@dataclass +class EntryTypeCapClaim: + pass + + +@variant(EntryType, "CapGrant") +@dataclass +class EntryTypeCapGrant: + pass + + +class Entry(TaggedUnion): + """A source-chain entry. Serialized with ``tag = "entry_type"``.""" + + tag_key = "entry_type" + content_key = "entry" + + +@variant(Entry, "Agent") +@dataclass +class AgentEntry: + agent: AgentPubKey = _newtype() + + +@variant(Entry, "App") +@dataclass +class AppEntry: + """App entry bytes (MessagePack of the application entry).""" + + bytes: bytes = _newtype() + + +@variant(Entry, "CapClaim") +@dataclass +class CapClaimEntry: + value: Any = _newtype() + + +@variant(Entry, "CapGrant") +@dataclass +class CapGrantEntry: + value: Any = _newtype() + + +# --------------------------------------------------------------------------- # +# Action variants (internally tagged: {"type": , ...fields}) +# --------------------------------------------------------------------------- # +class Action(TaggedUnion): + """A source-chain action.""" + + style = "internal" + + +@variant(Action, "Dna") +@dataclass +class Dna: + author: AgentPubKey + timestamp: Timestamp + hash: DnaHash + + +@variant(Action, "AgentValidationPkg") +@dataclass +class AgentValidationPkg: + author: AgentPubKey + timestamp: Timestamp + action_seq: int + prev_action: ActionHash + membrane_proof: Optional[bytes] = None + + +@variant(Action, "InitZomesComplete") +@dataclass +class InitZomesComplete: + author: AgentPubKey + timestamp: Timestamp + action_seq: int + prev_action: ActionHash + + +@variant(Action, "CreateLink") +@dataclass +class CreateLink: + author: AgentPubKey + timestamp: Timestamp + action_seq: int + prev_action: ActionHash + base_address: AnyLinkableHash + target_address: AnyLinkableHash + zome_index: int + link_type: int + tag: LinkTag + weight: Any = None + + +@variant(Action, "DeleteLink") +@dataclass +class DeleteLink: + author: AgentPubKey + timestamp: Timestamp + action_seq: int + prev_action: ActionHash + base_address: AnyLinkableHash + link_add_address: ActionHash + + +@variant(Action, "CloseChain") +@dataclass +class CloseChain: + author: AgentPubKey + timestamp: Timestamp + action_seq: int + prev_action: ActionHash + new_target: Optional[MigrationTarget] = None + + +@variant(Action, "OpenChain") +@dataclass +class OpenChain: + author: AgentPubKey + timestamp: Timestamp + action_seq: int + prev_action: ActionHash + prev_target: MigrationTarget + close_hash: ActionHash + + +@variant(Action, "Create") +@dataclass +class Create: + author: AgentPubKey + timestamp: Timestamp + action_seq: int + prev_action: ActionHash + entry_type: EntryType + entry_hash: EntryHash + weight: Any = None + + +@variant(Action, "Update") +@dataclass +class Update: + author: AgentPubKey + timestamp: Timestamp + action_seq: int + prev_action: ActionHash + original_action_address: ActionHash + original_entry_address: EntryHash + entry_type: EntryType + entry_hash: EntryHash + weight: Any = None + + +@variant(Action, "Delete") +@dataclass +class Delete: + author: AgentPubKey + timestamp: Timestamp + action_seq: int + prev_action: ActionHash + deletes_address: ActionHash + deletes_entry_address: EntryHash + weight: Any = None + + +# --------------------------------------------------------------------------- # +# Record +# --------------------------------------------------------------------------- # +@dataclass +class ActionHashed: + """An ``Action`` paired with its ``ActionHash`` (``HoloHashed``).""" + + content: Action + hash: ActionHash + + +@dataclass +class SignedActionHashed: + hashed: ActionHashed + signature: Signature + + +class RecordEntry(TaggedUnion): + """The entry component of a record: present, hidden, absent, or not stored.""" + + style = "external" + + +@variant(RecordEntry, "Present") +@dataclass +class EntryPresent: + entry: Entry = _newtype() + + +@variant(RecordEntry, "Hidden") +@dataclass +class EntryHidden: + pass + + +@variant(RecordEntry, "NA") +@dataclass +class EntryNotApplicable: + pass + + +@variant(RecordEntry, "NotStored") +@dataclass +class EntryNotStored: + pass + + +@dataclass +class Record: + """A chain record: a signed action and, if applicable, its entry.""" + + signed_action: SignedActionHashed + entry: RecordEntry + + def action(self) -> Any: + """The decoded ``Action`` variant for this record.""" + return self.signed_action.hashed.content + + def action_hash(self) -> ActionHash: + return self.signed_action.hashed.hash + + def entry_value(self) -> Optional[Entry]: + """The ``Entry`` if one is present, else ``None``.""" + if isinstance(self.entry, EntryPresent): + return self.entry.entry + return None + + +def decode_record(data: Any) -> Record: + """Decode raw MessagePack-decoded data into a :class:`Record`.""" + return decode(data, Record) + + +def decode_records(data: Any) -> List[Record]: + """Decode a list of records.""" + return [decode_record(item) for item in data] diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 451f8f2..0000000 --- a/poetry.lock +++ /dev/null @@ -1,470 +0,0 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. - -[[package]] -name = "cffi" -version = "1.16.0" -description = "Foreign Function Interface for Python calling C code." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, - {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, - {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, - {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, - {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, - {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, - {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, - {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, - {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, - {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, - {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, - {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, - {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, - {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, - {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, -] - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "cryptography" -version = "42.0.2" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -optional = false -python-versions = ">=3.7" -files = [ - {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be"}, - {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2"}, - {file = "cryptography-42.0.2-cp37-abi3-win32.whl", hash = "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee"}, - {file = "cryptography-42.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee"}, - {file = "cryptography-42.0.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33"}, - {file = "cryptography-42.0.2-cp39-abi3-win32.whl", hash = "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635"}, - {file = "cryptography-42.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65"}, - {file = "cryptography-42.0.2.tar.gz", hash = "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888"}, -] - -[package.dependencies] -cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} - -[package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] -docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] -nox = ["nox"] -pep8test = ["check-sdist", "click", "mypy", "ruff"] -sdist = ["build"] -ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] -test-randomorder = ["pytest-randomly"] - -[[package]] -name = "exceptiongroup" -version = "1.2.0" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "holochain-serialization" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.8" -files = [ - {file = "holochain_serialization-0.1.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ff1c4657262c4ba2824bf25719a3b542625cc6d0e9347fe54ce8edae845a94dc"}, - {file = "holochain_serialization-0.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab655d683cb1faf912856e55cb8ecf8213e0d4bcac7731f7224a4677bf014e42"}, - {file = "holochain_serialization-0.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a41bda8088dbea443e493bf36d6ebd44d74d4652a1c2776157eef4f2501d0aae"}, - {file = "holochain_serialization-0.1.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f29123eb4c496e587868241086da75ff952f3833ed9b37743f12af9303acb909"}, - {file = "holochain_serialization-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09280dad1425d4b188b07c2069b05a290b16d833bfd1f93449f26726e2f915b7"}, - {file = "holochain_serialization-0.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7051c45d47d034de1d2778b008ab26fb6bd06d341069574d258c8436ee71a853"}, - {file = "holochain_serialization-0.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c95237ed7f81f9c5bc168175844f31c21410601241dc0d69149e36c33fef2169"}, - {file = "holochain_serialization-0.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55ebb25c0a3486f8f1a74a918c1e0e08e60dd10b029a2fa41377720649194b3c"}, - {file = "holochain_serialization-0.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01d02527cd15fbc635f23bb624b3d08bfeaf98c94ab28dceaa8839640f94f94f"}, - {file = "holochain_serialization-0.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1eb3399357dd7013d14ae4af99ff9ae0c373c431116a9aa99383794dcc9aaad9"}, - {file = "holochain_serialization-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a6ef12e94c2088f16c79bf7687ad6a83cd00f232a01a2352a48e48d63da0685"}, - {file = "holochain_serialization-0.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03cc4a56da68c8284d995d5f9b14b04b9e0d2ded85c6190d98e59630cc3a8ab8"}, - {file = "holochain_serialization-0.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:147ceb8c7ba0049bb0e8a484216b00ae2790c13b6db9e3184d1a3d2cdd8db3c2"}, - {file = "holochain_serialization-0.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ade7c89f870caeb8a72fd023aa5a8e752b2d5ec3aab682da72b57cafee67d479"}, - {file = "holochain_serialization-0.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92e269aba49cdb59e5caf8e6fbb21daa1afa2db424c60d7d9c16c510dfaf52f"}, - {file = "holochain_serialization-0.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0eea229b56352ce561eabb48173d617ed4d352bf505ad836707cc8fc39efeb94"}, - {file = "holochain_serialization-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cd558c411e98816d44332d48690907ce71d6720c5a347d17cb9d2a8a571cacb"}, - {file = "holochain_serialization-0.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e67658baaf6f23904bfb2b304d996e49dc2c5ebf17b60a3ddd05851b8c68940e"}, - {file = "holochain_serialization-0.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1109433de6bd7dd512c8ac0b08041e652bbf030b95b219a2e5c35864bf18396c"}, - {file = "holochain_serialization-0.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8851a1a14a94b71615520f9a147257fee75221d7ad001168803b13a02c530db"}, - {file = "holochain_serialization-0.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7d4cb37a5a7e8f23d3fe9459a6a182b90310387d4265a45da14648e208c8d07"}, - {file = "holochain_serialization-0.1.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23496d8ebfdc73a0af083121e5f55ba7e1808b02f9013d1d4d3ad8f3b002acbd"}, - {file = "holochain_serialization-0.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce953b9f244f1d7a70bebd28f5a6f03857d4e4a35db601162b9ac159a14fa2be"}, - {file = "holochain_serialization-0.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3d986ceb21dc0d961b3bd57067fb07c65ec0de1cbb11b64d486e6a16398d9395"}, - {file = "holochain_serialization-0.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ea03ecf3952d3398313fa9977f0d29f4ecd82bef5da1e133713b4700013947d"}, - {file = "holochain_serialization-0.1.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f2e661521618b542e7f6b824f306799490475555f1de2db61618933d0f366eba"}, - {file = "holochain_serialization-0.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:811a49ca47d1a747a66e91c58505276312d759502442a80d5d8fd5ad00336f99"}, - {file = "holochain_serialization-0.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:93185120b0f11947cb304b8361876cc0f4d5d92973f042014bc2b93c556feea9"}, - {file = "holochain_serialization-0.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f277d1b5e3b3756632d1e6633d2843b3c7f901f3d01c34bc2956d450d4a4c1e1"}, - {file = "holochain_serialization-0.1.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:57c9b8ef1f326d168ff525a4a3de6dfe9494f14940a057b451454f10a8b74f04"}, - {file = "holochain_serialization-0.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2da21ba8cd495966670d33080b786d492b0883f6cb8c4ae6ee80f92ae870d716"}, - {file = "holochain_serialization-0.1.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d91684fdd9c898a51ab4c0dc086ad2d88f9bfc1e6d496ae7aa02099e43599116"}, - {file = "holochain_serialization-0.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fbcd9631ffe6a1c14df05cd5cd9d5cd5b9c9ddcfb9a160579638ba9aed2f185c"}, - {file = "holochain_serialization-0.1.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:601065a9c22595f8be9fe97b185c727bcecd895b4bece7b8c941e488b6b50245"}, - {file = "holochain_serialization-0.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e43cc5a91bc54a1fefe8a952b2e67cf9af6ab955efda8aacd0013f94f05f6b98"}, - {file = "holochain_serialization-0.1.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f9ccf004d5f5f18cfea77ceb75d602c9f6e4f9566e256bc610f58bac01e5567e"}, - {file = "holochain_serialization-0.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c32e0318bbefe5efd578b9bff6f593f6f00aea933d10e5a49cdfde97efbfa825"}, - {file = "holochain_serialization-0.1.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38e67a11fa7defaf2839466449718455f880417e70ea5eb2730b2c78afac24ec"}, - {file = "holochain_serialization-0.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25957708756705b4572d63935d06924db74ddc197a8b6dc85f8fa8de912ef429"}, - {file = "holochain_serialization-0.1.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ccdd8d96e828a9e838557090ea2ded3f950963314742887d8b90f835e82a5d4a"}, - {file = "holochain_serialization-0.1.0.tar.gz", hash = "sha256:ea3edf4ee306c1821ae60f86811e79f85278789b6fe7a3307ee257b993f70169"}, -] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "msgpack" -version = "1.0.7" -description = "MessagePack serializer" -optional = false -python-versions = ">=3.8" -files = [ - {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862"}, - {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329"}, - {file = "msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b"}, - {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6"}, - {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee"}, - {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d"}, - {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d"}, - {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1"}, - {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681"}, - {file = "msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9"}, - {file = "msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415"}, - {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84"}, - {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93"}, - {file = "msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8"}, - {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46"}, - {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b"}, - {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e"}, - {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002"}, - {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c"}, - {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e"}, - {file = "msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1"}, - {file = "msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82"}, - {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b"}, - {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4"}, - {file = "msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee"}, - {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5"}, - {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672"}, - {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075"}, - {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba"}, - {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c"}, - {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5"}, - {file = "msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9"}, - {file = "msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf"}, - {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95"}, - {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0"}, - {file = "msgpack-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7"}, - {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d"}, - {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524"}, - {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc"}, - {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc"}, - {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf"}, - {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c"}, - {file = "msgpack-1.0.7-cp38-cp38-win32.whl", hash = "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2"}, - {file = "msgpack-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c"}, - {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f"}, - {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81"}, - {file = "msgpack-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc"}, - {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d"}, - {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7"}, - {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61"}, - {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819"}, - {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd"}, - {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f"}, - {file = "msgpack-1.0.7-cp39-cp39-win32.whl", hash = "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad"}, - {file = "msgpack-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3"}, - {file = "msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87"}, -] - -[[package]] -name = "packaging" -version = "23.2" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, -] - -[[package]] -name = "pluggy" -version = "1.4.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pycparser" -version = "2.21" -description = "C parser in Python" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] - -[[package]] -name = "pytest" -version = "7.4.4" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, - {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-asyncio" -version = "0.23.4" -description = "Pytest support for asyncio" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-asyncio-0.23.4.tar.gz", hash = "sha256:2143d9d9375bf372a73260e4114541485e84fca350b0b6b92674ca56ff5f7ea2"}, - {file = "pytest_asyncio-0.23.4-py3-none-any.whl", hash = "sha256:b0079dfac14b60cd1ce4691fbfb1748fe939db7d0234b5aba97197d10fbe0fef"}, -] - -[package.dependencies] -pytest = ">=7.0.0,<8" - -[package.extras] -docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] -testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] - -[[package]] -name = "ruff" -version = "0.2.1" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = false -python-versions = ">=3.7" -files = [ - {file = "ruff-0.2.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:dd81b911d28925e7e8b323e8d06951554655021df8dd4ac3045d7212ac4ba080"}, - {file = "ruff-0.2.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dc586724a95b7d980aa17f671e173df00f0a2eef23f8babbeee663229a938fec"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c92db7101ef5bfc18e96777ed7bc7c822d545fa5977e90a585accac43d22f18a"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13471684694d41ae0f1e8e3a7497e14cd57ccb7dd72ae08d56a159d6c9c3e30e"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a11567e20ea39d1f51aebd778685582d4c56ccb082c1161ffc10f79bebe6df35"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:00a818e2db63659570403e44383ab03c529c2b9678ba4ba6c105af7854008105"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be60592f9d218b52f03384d1325efa9d3b41e4c4d55ea022cd548547cc42cd2b"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbd2288890b88e8aab4499e55148805b58ec711053588cc2f0196a44f6e3d855"}, - {file = "ruff-0.2.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ef052283da7dec1987bba8d8733051c2325654641dfe5877a4022108098683"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7022d66366d6fded4ba3889f73cd791c2d5621b2ccf34befc752cb0df70f5fad"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0a725823cb2a3f08ee743a534cb6935727d9e47409e4ad72c10a3faf042ad5ba"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0034d5b6323e6e8fe91b2a1e55b02d92d0b582d2953a2b37a67a2d7dedbb7acc"}, - {file = "ruff-0.2.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e5cb5526d69bb9143c2e4d2a115d08ffca3d8e0fddc84925a7b54931c96f5c02"}, - {file = "ruff-0.2.1-py3-none-win32.whl", hash = "sha256:6b95ac9ce49b4fb390634d46d6ece32ace3acdd52814671ccaf20b7f60adb232"}, - {file = "ruff-0.2.1-py3-none-win_amd64.whl", hash = "sha256:e3affdcbc2afb6f5bd0eb3130139ceedc5e3f28d206fe49f63073cb9e65988e0"}, - {file = "ruff-0.2.1-py3-none-win_arm64.whl", hash = "sha256:efababa8e12330aa94a53e90a81eb6e2d55f348bc2e71adbf17d9cad23c03ee6"}, - {file = "ruff-0.2.1.tar.gz", hash = "sha256:3b42b5d8677cd0c72b99fcaf068ffc62abb5a19e71b4a3b9cfa50658a0af02f1"}, -] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "websockets" -version = "12.0" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, - {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, - {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, - {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, - {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, - {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, - {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, - {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, - {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, - {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, - {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, - {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, - {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, - {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, - {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, - {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, - {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, - {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, - {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, - {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, - {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, - {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, - {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, - {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, - {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, -] - -[metadata] -lock-version = "2.0" -python-versions = "^3.8" -content-hash = "d515c8c47af8495ad8e9056cbcf9bb0737e1ad3d03f6f5c97cba0d7b6badca8e" diff --git a/pyproject.toml b/pyproject.toml index d0785be..90753e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,20 +15,24 @@ packages = [ ] [tool.poetry.dependencies] -python = "^3.8" +python = "^3.10" msgpack = "^1.0.7" -websockets = "^12.0" -holochain-serialization = "^0.1.0" -cryptography = "^42.0.2" -ruff = "^0.2.1" - -[tool.poetry.group.test.dependencies] -pytest = "^7.0.0" - +websockets = "^13.0" +pynacl = "^1.5.0" [tool.poetry.group.dev.dependencies] -pytest-asyncio = "^0.23.4" +ruff = "^0.6.0" +pytest = "^8.0.0" +pytest-asyncio = "^0.24.0" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "session" +testpaths = ["tests"] + +[tool.ruff] +target-version = "py310" diff --git a/holochain_client/api/admin/__init__.py b/tests/admin/__init__.py similarity index 100% rename from holochain_client/api/admin/__init__.py rename to tests/admin/__init__.py diff --git a/tests/admin/test_admin.py b/tests/admin/test_admin.py new file mode 100644 index 0000000..51ed662 --- /dev/null +++ b/tests/admin/test_admin.py @@ -0,0 +1,44 @@ +"""Admin interface integration tests (require a running conductor).""" + +import pytest + +from holochain_client.hash import AgentPubKey, DnaHash +from holochain_client.types.app import AppInfo +from holochain_client.types.cell import CellId + +pytestmark = pytest.mark.asyncio(loop_scope="session") + + +async def test_list_dnas(harness): + dnas = await harness.admin.list_dnas() + assert len(dnas) == 1 + assert isinstance(dnas[0], DnaHash) + + +async def test_list_cell_ids(harness): + cell_ids = await harness.admin.list_cell_ids() + assert harness.cell_id in cell_ids + assert all(isinstance(c, CellId) for c in cell_ids) + + +async def test_list_apps(harness): + apps = await harness.admin.list_apps() + assert any(app.installed_app_id == harness.installed_app_id for app in apps) + assert all(isinstance(app, AppInfo) for app in apps) + + +async def test_generate_agent_pub_key(harness): + key = await harness.admin.generate_agent_pub_key() + assert isinstance(key, AgentPubKey) + assert len(key) == 39 + + +async def test_get_dna_definition(harness): + definition = await harness.admin.get_dna_definition(harness.cell_id) + assert definition["name"] == "fixture" + + +async def test_list_app_interfaces(harness): + interfaces = await harness.admin.list_app_interfaces() + assert len(interfaces) >= 1 + assert all(i.port > 0 for i in interfaces) diff --git a/tests/api/__init__.py b/tests/api/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/api/admin/__init__.py b/tests/api/admin/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/api/admin/client_test.py b/tests/api/admin/client_test.py deleted file mode 100644 index d1efc80..0000000 --- a/tests/api/admin/client_test.py +++ /dev/null @@ -1,203 +0,0 @@ -import pytest -from holochain_client.api.admin.types import ( - AddAdminInterface, - DisableApp, - DnaModifiers, - EnableApp, - InterfaceDriverWebsocket, - InstallApp, - RegisterDnaPayloadHash, - RegisterDnaPayloadPath, - UninstallApp, -) -from holochain_client.api.admin.client import AdminClient -from tests.harness import TestHarness - - -@pytest.mark.asyncio -async def test_add_admin_interface(): - async with TestHarness() as harness: - # Get a free port - import socket - - sock = socket.socket() - sock.bind(("", 0)) - port = sock.getsockname()[1] - sock.close() - - await harness.admin_client.add_admin_interfaces( - [AddAdminInterface(InterfaceDriverWebsocket(port))] - ) - - new_admin_client = await AdminClient.create(f"ws://localhost:{port}") - - agent_pub_key = await new_admin_client.generate_agent_pub_key() - await new_admin_client.close() - assert len(agent_pub_key) == 39 - - -@pytest.mark.asyncio -async def test_register_dna(): - async with TestHarness() as harness: - dna_hash = await harness.admin_client.register_dna( - RegisterDnaPayloadPath(harness.fixture_dna_path) - ) - assert len(dna_hash) == 39 - - dna_hash_alt = await harness.admin_client.register_dna( - RegisterDnaPayloadHash(dna_hash, DnaModifiers(network_seed="testing")) - ) - assert len(dna_hash_alt) == 39 - assert dna_hash != dna_hash_alt - - -@pytest.mark.asyncio -async def test_get_dna_definition(): - async with TestHarness() as harness: - dna_hash = await harness.admin_client.register_dna( - RegisterDnaPayloadPath(harness.fixture_dna_path) - ) - assert len(dna_hash) == 39 - - dna_def = await harness.admin_client.get_dna_definition(dna_hash) - assert "name" in dna_def - assert dna_def["name"] == "fixture" - - -@pytest.mark.asyncio -async def test_install_app(): - async with TestHarness() as harness: - agent_pub_key = await harness.admin_client.generate_agent_pub_key() - - response = await harness.admin_client.install_app( - InstallApp( - agent_key=agent_pub_key, - installed_app_id="test_app", - path=harness.fixture_path, - ) - ) - - assert response.installed_app_id == "test_app" - - -@pytest.mark.asyncio -async def test_uninstall_app(): - async with TestHarness() as harness: - agent_pub_key = await harness.admin_client.generate_agent_pub_key() - - await harness.admin_client.install_app( - InstallApp( - agent_key=agent_pub_key, - installed_app_id="test_app", - path=harness.fixture_path, - ) - ) - - assert len(await harness.admin_client.list_apps()) == 1 - - await harness.admin_client.uninstall_app(UninstallApp("test_app")) - - assert len(await harness.admin_client.list_apps()) == 0 - - -@pytest.mark.asyncio -async def test_list_dnas(): - async with TestHarness() as harness: - agent_pub_key = await harness.admin_client.generate_agent_pub_key() - - await harness.admin_client.install_app( - InstallApp( - agent_key=agent_pub_key, - installed_app_id="test_app", - path=harness.fixture_path, - ) - ) - - assert len(await harness.admin_client.list_dnas()) == 1 - - -@pytest.mark.asyncio -async def test_list_cell_ids(): - async with TestHarness() as harness: - agent_pub_key = await harness.admin_client.generate_agent_pub_key() - - await harness.admin_client.install_app( - InstallApp( - agent_key=agent_pub_key, - installed_app_id="test_app", - path=harness.fixture_path, - ) - ) - - # Lists running only, so should be empty before enabling - cell_ids = await harness.admin_client.list_cell_ids() - assert len(cell_ids) == 0 - - await harness.admin_client.enable_app(EnableApp("test_app")) - - cell_ids = await harness.admin_client.list_cell_ids() - assert len(cell_ids) == 1 - assert cell_ids[0][0] == (await harness.admin_client.list_dnas())[0] - assert cell_ids[0][1] == agent_pub_key - - -@pytest.mark.asyncio -async def test_list_apps(): - async with TestHarness() as harness: - agent_pub_key = await harness.admin_client.generate_agent_pub_key() - - await harness.admin_client.install_app( - InstallApp( - agent_key=agent_pub_key, - installed_app_id="test_app", - path=harness.fixture_path, - ) - ) - - apps = await harness.admin_client.list_apps() - assert apps[0].agent_pub_key == agent_pub_key - assert apps[0].installed_app_id == "test_app" - - -@pytest.mark.asyncio -async def test_enable_and_disable_app(): - async with TestHarness() as harness: - agent_pub_key = await harness.admin_client.generate_agent_pub_key() - - await harness.admin_client.install_app( - InstallApp( - agent_key=agent_pub_key, - installed_app_id="test_app", - path=harness.fixture_path, - ) - ) - - app_info = (await harness.admin_client.list_apps())[0] - assert "disabled" in app_info.status - - await harness.admin_client.enable_app(EnableApp("test_app")) - - app_info = (await harness.admin_client.list_apps())[0] - assert "running" in app_info.status - - await harness.admin_client.disable_app(DisableApp("test_app")) - - app_info = (await harness.admin_client.list_apps())[0] - assert "disabled" in app_info.status - - -@pytest.mark.asyncio -async def test_attach_app_interface(): - async with TestHarness() as harness: - app_interface = await harness.admin_client.attach_app_interface() - assert app_interface.port > 0 - - listed = await harness.admin_client.list_app_interfaces() - assert app_interface.port in listed - - -@pytest.mark.asyncio -async def test_dump_network_stats(): - async with TestHarness() as harness: - network_stats = await harness.admin_client.dump_network_stats() - print("network stats: ", network_stats) diff --git a/tests/api/admin/signing_test.py b/tests/api/admin/signing_test.py deleted file mode 100644 index c47d969..0000000 --- a/tests/api/admin/signing_test.py +++ /dev/null @@ -1,39 +0,0 @@ -from holochain_client.api.common.signing import ( - authorize_signing_credentials, - generate_signing_keypair, -) -import pytest -from holochain_client.api.admin.types import EnableApp, InstallApp - -from tests.harness import TestHarness - - -def test_generate_signing_keypair(): - signing_keypair = generate_signing_keypair() - assert len(signing_keypair.identity) == 39 - - -@pytest.mark.asyncio -async def test_authorize_signing_credentials(): - async with TestHarness() as harness: - agent_pub_key = await harness.admin_client.generate_agent_pub_key() - - response = await harness.admin_client.install_app( - InstallApp( - agent_key=agent_pub_key, - installed_app_id="test_app", - path=harness.fixture_path, - ) - ) - - await harness.admin_client.enable_app( - EnableApp(installed_app_id=response.installed_app_id) - ) - - await authorize_signing_credentials( - harness.admin_client, - response.cell_info["fixture"][0]["provisioned"]["cell_id"], - None, - ) - - assert response.installed_app_id == "test_app" diff --git a/tests/api/app/client_test.py b/tests/api/app/client_test.py deleted file mode 100644 index f18d555..0000000 --- a/tests/api/app/client_test.py +++ /dev/null @@ -1,43 +0,0 @@ -import pytest -from holochain_client.api.admin.types import ( - EnableApp, - InstallApp, -) -from holochain_client.api.app.types import ZomeCallUnsigned -from holochain_client.api.common.signing import authorize_signing_credentials -from tests.harness import TestHarness -import msgpack - - -@pytest.mark.asyncio -async def test_call_zome(): - async with TestHarness() as harness: - agent_pub_key = await harness.admin_client.generate_agent_pub_key() - - app_info = await harness.admin_client.install_app( - InstallApp( - agent_key=agent_pub_key, - installed_app_id="test_app", - path=harness.fixture_path, - ) - ) - - await harness.admin_client.enable_app(EnableApp(app_info.installed_app_id)) - - cell_id = app_info.cell_info["fixture"][0]["provisioned"]["cell_id"] - await authorize_signing_credentials(harness.admin_client, cell_id) - - response = await harness.app_client.call_zome( - ZomeCallUnsigned( - cell_id=cell_id, - zome_name="fixture", - fn_name="create_fixture", - payload=msgpack.packb({"name": "hello fixture"}), - ) - ) - - response_struct = msgpack.unpackb(response) - assert ( - response_struct["signed_action"]["hashed"]["content"]["author"] - == agent_pub_key - ) diff --git a/holochain_client/api/app/__init__.py b/tests/app/__init__.py similarity index 100% rename from holochain_client/api/app/__init__.py rename to tests/app/__init__.py diff --git a/tests/app/test_zome_call.py b/tests/app/test_zome_call.py new file mode 100644 index 0000000..d2b365d --- /dev/null +++ b/tests/app/test_zome_call.py @@ -0,0 +1,64 @@ +"""App interface and zome-call integration tests (require a running conductor).""" + +import asyncio + +import msgpack +import pytest + +from holochain_client.hash import ActionHash +from holochain_client.types.record import AppEntry, Create, decode_record + +pytestmark = pytest.mark.asyncio(loop_scope="session") + + +async def test_echo(harness): + result = await harness.app.call_zome(harness.cell_id, "fixture", "echo", "hello") + assert result == "hello" + + +async def test_app_info(harness): + info = await harness.app.app_info() + assert info is not None + assert info.installed_app_id == harness.installed_app_id + assert "fixture" in info.cell_info + + +async def test_create_and_get_fixture(harness): + raw = await harness.app.call_zome( + harness.cell_id, "fixture", "create_fixture", {"value": "first"} + ) + # The created Record decodes into typed Action/Entry models. + record = decode_record(raw) + assert isinstance(record.action(), Create) + assert isinstance(record.action_hash(), ActionHash) + entry = record.entry_value() + assert isinstance(entry, AppEntry) + assert msgpack.unpackb(entry.bytes, raw=False) == {"value": "first"} + + fetched = decode_record( + await harness.app.call_zome( + harness.cell_id, "fixture", "get_fixture", record.action_hash() + ) + ) + assert fetched.action_hash() == record.action_hash() + + +async def test_get_all_fixtures(harness): + await harness.app.call_zome( + harness.cell_id, "fixture", "create_fixture", {"value": "linked"} + ) + links = await harness.app.call_zome( + harness.cell_id, "fixture", "get_all_fixtures", None + ) + assert isinstance(links, list) + assert len(links) >= 1 + + +async def test_signal_delivery(harness): + received: asyncio.Queue = asyncio.Queue() + harness.app.on_signal(lambda signal: received.put_nowait(signal)) + + await harness.app.call_zome(harness.cell_id, "fixture", "emit", "ping") + + signal = await asyncio.wait_for(received.get(), timeout=10) + assert signal.payload["value"] == "ping" diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..356e0d6 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,9 @@ +import pytest_asyncio + +from tests.harness import Harness + + +@pytest_asyncio.fixture(loop_scope="session", scope="session") +async def harness(): + async with Harness() as h: + yield h diff --git a/tests/harness.py b/tests/harness.py index 89d0451..e0f7ef6 100644 --- a/tests/harness.py +++ b/tests/harness.py @@ -1,79 +1,121 @@ -from subprocess import Popen, run, PIPE -import signal -import re +"""Test harness that boots a real Holochain conductor sandbox. + +Requires the ``hc`` and ``holochain`` binaries on ``PATH`` (provided by Holonix +in CI). The fixture hApp must be built first (``cd fixture && npm run +build:happ``). +""" + +from __future__ import annotations + import json -from typing import Tuple -from holochain_client.api.admin.client import AdminClient +import os +import re +import signal +import subprocess from pathlib import Path -from os import path +from typing import Optional, Tuple -from holochain_client.api.app.client import AppClient +from holochain_client.admin import AdminClient +from holochain_client.app import AppClient +from holochain_client.types.cell import CellId, ProvisionedCell +FIXTURE_HAPP = (Path(__file__).parent / "../fixture/workdir/fixture.happ").resolve() +PASSPHRASE = "test-phrase\n" -class TestHarness: - admin_client: AdminClient - app_client: AppClient - fixture_path: str - async def __aenter__(self): - (self._sandbox_process, admin_port) = _start_holochain() - self.admin_client = await AdminClient.create(f"ws://localhost:{admin_port}") +class Harness: + """A running conductor with the fixture app installed and connected.""" - app_interface = await self.admin_client.attach_app_interface() - self.app_client = await AppClient.create(f"ws://localhost:{app_interface.port}") + admin: AdminClient + app: AppClient + cell_id: CellId + installed_app_id: str - fixture_path = ( - Path(__file__).parent / "../fixture/workdir/fixture.happ" - ).resolve() - if not path.exists(fixture_path): - raise Exception( - "Fixture does not exist, please build it before running this test" - ) - self.fixture_path = str(fixture_path) - - fixture_dna_path = ( - Path(__file__).parent / "../fixture/dnas/fixture/workdir/fixture.dna" - ).resolve() - if not path.exists(fixture_dna_path): - raise Exception( - "Fixture DNA does not exist, please build it before running this test" + def __init__(self): + self._process: Optional[subprocess.Popen] = None + + async def __aenter__(self) -> "Harness": + if not FIXTURE_HAPP.exists(): + raise RuntimeError( + f"fixture hApp not found at {FIXTURE_HAPP}; build it with " + "`cd fixture && npm run build:happ`" ) - self.fixture_dna_path = str(fixture_dna_path) + # ``HC_ADMIN_PORT`` lets the suite run against an already-running + # conductor instead of spawning one (useful where spawning is awkward). + existing_port = os.environ.get("HC_ADMIN_PORT") + if existing_port: + admin_port = int(existing_port) + else: + self._process, admin_port = _start_conductor() + self.admin = await AdminClient.connect(f"ws://localhost:{admin_port}") + self.installed_app_id = "fixture" + try: + await self.admin.uninstall_app(self.installed_app_id, force=True) + except Exception: + pass + app_info = await self.admin.install_app( + _bundle_source(), + installed_app_id=self.installed_app_id, + ) + await self.admin.enable_app(self.installed_app_id) + + provisioned = app_info.cell_info["fixture"][0] + assert isinstance(provisioned, ProvisionedCell) + self.cell_id = provisioned.cell_id + + app_port = await self.admin.attach_app_interface(allowed_origins="*") + token = await self.admin.issue_app_authentication_token(self.installed_app_id) + self.app = await AppClient.connect(f"ws://localhost:{app_port}", token.token) + + credentials = await self.admin.authorize_signing_credentials(self.cell_id) + self.app.credentials.set(self.cell_id, credentials) return self - async def __aexit__(self, exc_type, exc, tb): - del exc_type, exc, tb - await self.app_client.close() - await self.admin_client.close() - self._sandbox_process.send_signal(signal.SIGINT) + async def __aexit__(self, exc_type, exc, tb) -> None: + try: + await self.app.close() + await self.admin.close() + finally: + if self._process is not None: + self._process.send_signal(signal.SIGINT) + try: + self._process.wait(timeout=15) + except subprocess.TimeoutExpired: + self._process.kill() + +def _bundle_source(): + from holochain_client.admin.types import AppBundleBytes -def _start_holochain() -> Tuple[Popen, int]: - ps = run(["hc", "sandbox", "clean"]) - if ps.returncode != 0: - raise Exception("Failed to clean sandbox") + return AppBundleBytes(bundle=FIXTURE_HAPP.read_bytes()) - ps = run( + +def _start_conductor() -> Tuple[subprocess.Popen, int]: + subprocess.run(["hc", "sandbox", "clean"], check=False) + created = subprocess.run( ["hc", "sandbox", "--piped", "create", "--in-process-lair"], + input=PASSPHRASE, text=True, - input="passphrase\n", ) - if ps.returncode != 0: - raise Exception("Failed to create sandbox") + if created.returncode != 0: + raise RuntimeError("failed to create sandbox") - ps = Popen(["hc", "sandbox", "--piped", "run"], stdin=PIPE, stdout=PIPE, text=True) - ps.stdin.write("passphrase\n") - ps.stdin.flush() + process = subprocess.Popen( + ["hc", "sandbox", "--piped", "run"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + ) + assert process.stdin is not None and process.stdout is not None + process.stdin.write(PASSPHRASE) + process.stdin.flush() - # TODO if the sandbox fails to start or print the expected magic line, this loop won't exit - admin_port = 0 while True: - line = ps.stdout.readline() + line = process.stdout.readline() + if line == "": + raise RuntimeError("conductor exited before reporting its admin port") match = re.search(r"#!0 (.*)", line) if match: - info = json.loads(match.group(1)) - admin_port = info["admin_port"] - break - - return (ps, admin_port) + return process, json.loads(match.group(1))["admin_port"] diff --git a/tests/test_record.py b/tests/test_record.py new file mode 100644 index 0000000..1a219f2 --- /dev/null +++ b/tests/test_record.py @@ -0,0 +1,80 @@ +"""Unit tests for Record/Action/Entry decoding (no conductor required).""" + +import msgpack + +from holochain_client.hash import ( + ACTION_PREFIX, + AGENT_PREFIX, + ENTRY_PREFIX, + ActionHash, + AgentPubKey, +) +from holochain_client.types.record import ( + AppEntry, + Create, + EntryPresent, + EntryTypeApp, + decode_record, +) + + +def _hash(prefix): + return prefix + bytes(32) + bytes(4) + + +def _create_record(): + action_hash = _hash(ACTION_PREFIX) + return { + "signed_action": { + "hashed": { + "hash": action_hash, + "content": { + "type": "Create", + "author": _hash(AGENT_PREFIX), + "timestamp": 1700000000000000, + "action_seq": 5, + "prev_action": action_hash, + "entry_type": { + "App": { + "entry_index": 0, + "zome_index": 1, + "visibility": "Public", + } + }, + "entry_hash": _hash(ENTRY_PREFIX), + "weight": {"bucket_id": 0, "units": 0}, + }, + }, + "signature": bytes(64), + }, + "entry": { + "Present": { + "entry_type": "App", + "entry": msgpack.packb({"value": "first"}, use_bin_type=True), + } + }, + } + + +def test_decode_record_action_is_typed(): + record = decode_record(_create_record()) + action = record.action() + assert isinstance(action, Create) + assert isinstance(action.author, AgentPubKey) + assert action.action_seq == 5 + assert isinstance(record.action_hash(), ActionHash) + + +def test_decode_record_internal_external_adjacent_tagging(): + record = decode_record(_create_record()) + # internal-tagged Action -> external-tagged EntryType -> nested struct. + assert isinstance(record.action().entry_type, EntryTypeApp) + assert record.action().entry_type.app_entry_def.zome_index == 1 + + +def test_decode_record_entry_roundtrips(): + record = decode_record(_create_record()) + assert isinstance(record.entry, EntryPresent) + entry = record.entry_value() + assert isinstance(entry, AppEntry) + assert msgpack.unpackb(entry.bytes, raw=False) == {"value": "first"} diff --git a/tests/test_serde.py b/tests/test_serde.py new file mode 100644 index 0000000..c10245c --- /dev/null +++ b/tests/test_serde.py @@ -0,0 +1,79 @@ +"""Unit tests for the serde layer and hashes (no conductor required).""" + +from holochain_client.hash import ( + AGENT_PREFIX, + DNA_PREFIX, + AgentPubKey, + DnaHash, + hash_from_raw, +) +from holochain_client.serde import decode, encode +from holochain_client.types.app import AppDisabled, AppStatus, DisabledByError +from holochain_client.types.capability import ( + CapAccessAssigned, + GrantedFunctionsAll, + GrantedFunctionsListed, +) +from holochain_client.types.cell import CellId, CellInfo, ProvisionedCell + + +def _dna() -> DnaHash: + return DnaHash(DNA_PREFIX + bytes(32) + bytes(4)) + + +def _agent() -> AgentPubKey: + return AgentPubKey(AGENT_PREFIX + bytes(32) + bytes(4)) + + +def test_holohash_roundtrip_and_b64(): + dna = _dna() + assert len(dna) == 39 + assert dna.to_b64().startswith("uhC0k") + assert DnaHash.from_b64(dna.to_b64()) == dna + assert isinstance(hash_from_raw(bytes(dna)), DnaHash) + + +def test_cell_id_serializes_as_pair(): + cell_id = CellId(_dna(), _agent()) + wire = encode(cell_id) + assert wire == [bytes(_dna()), bytes(_agent())] + assert decode(wire, CellId) == cell_id + + +def test_cell_info_provisioned_decode(): + wire = { + "type": "provisioned", + "value": { + "cell_id": [bytes(_dna()), bytes(_agent())], + "dna_modifiers": {"network_seed": "seed", "properties": None}, + "name": "fixture", + }, + } + info = CellInfo.__from_wire__(wire) + assert isinstance(info, ProvisionedCell) + assert info.name == "fixture" + assert info.cell_id == CellId(_dna(), _agent()) + + +def test_app_status_nested_union(): + status = decode( + {"type": "disabled", "value": {"type": "error", "value": "boom"}}, AppStatus + ) + assert isinstance(status, AppDisabled) + assert isinstance(status.reason, DisabledByError) + assert status.reason.message == "boom" + + +def test_granted_functions_encode(): + assert encode(GrantedFunctionsAll()) == {"type": "all"} + assert encode(GrantedFunctionsListed(functions=[["zome", "fn"]])) == { + "type": "listed", + "value": [["zome", "fn"]], + } + + +def test_cap_access_assigned_encode(): + wire = encode(CapAccessAssigned(secret=bytes(64), assignees=[_agent()])) + assert wire["type"] == "assigned" + assert wire["value"]["secret"] == bytes(64) + assert wire["value"]["assignees"] == [bytes(_agent())] diff --git a/tests/test_signing.py b/tests/test_signing.py new file mode 100644 index 0000000..dc9d955 --- /dev/null +++ b/tests/test_signing.py @@ -0,0 +1,48 @@ +"""Unit tests for zome-call signing (no conductor required).""" + +import hashlib + +import msgpack +from nacl.signing import VerifyKey + +from holochain_client.hash import AGENT_PREFIX, DNA_PREFIX, AgentPubKey, DnaHash +from holochain_client.signing import SigningKeyPair, sign_zome_call +from holochain_client.types.cell import CellId + + +def test_signing_key_agent_pub_key_layout(): + keypair = SigningKeyPair() + agent = keypair.agent_pub_key + assert isinstance(agent, AgentPubKey) + assert len(agent) == 39 + assert bytes(agent[:3]) == AGENT_PREFIX + assert bytes(agent[-4:]) == bytes(4) # zeroed location + + +def test_sign_zome_call_is_verifiable(): + keypair = SigningKeyPair() + cell_id = CellId( + DnaHash(DNA_PREFIX + bytes(32) + bytes(4)), + keypair.agent_pub_key, + ) + signed = sign_zome_call( + cell_id=cell_id, + zome_name="fixture", + fn_name="echo", + payload="hello", + provenance=keypair.agent_pub_key, + signing_key=keypair, + cap_secret=bytes(64), + ) + # The signature is over the SHA-512 of the transmitted bytes. + expected_hash = hashlib.sha512(signed.bytes).digest() + verify_key = VerifyKey(bytes(keypair.agent_pub_key[3:35])) + verify_key.verify(expected_hash, signed.signature) + + # The transmitted bytes deserialize back to the params, with a + # double-encoded payload. + params = msgpack.unpackb(signed.bytes, raw=False) + assert params["zome_name"] == "fixture" + assert params["fn_name"] == "echo" + assert msgpack.unpackb(params["payload"], raw=False) == "hello" + assert len(params["nonce"]) == 32