cowboy-protocol-py 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. cowboy_protocol_py-0.6.0/.gitignore +11 -0
  2. cowboy_protocol_py-0.6.0/LICENSE-APACHE +178 -0
  3. cowboy_protocol_py-0.6.0/LICENSE-MIT +21 -0
  4. cowboy_protocol_py-0.6.0/PKG-INFO +283 -0
  5. cowboy_protocol_py-0.6.0/README.md +264 -0
  6. cowboy_protocol_py-0.6.0/pyproject.toml +54 -0
  7. cowboy_protocol_py-0.6.0/src/cowboy_protocol/__init__.py +458 -0
  8. cowboy_protocol_py-0.6.0/src/cowboy_protocol/_host.py +204 -0
  9. cowboy_protocol_py-0.6.0/src/cowboy_protocol/_provenance.json +123 -0
  10. cowboy_protocol_py-0.6.0/src/cowboy_protocol/_wasm/__init__.py +0 -0
  11. cowboy_protocol_py-0.6.0/src/cowboy_protocol/_wasm/cowboy_protocol_wasm_ffi.wasm +0 -0
  12. cowboy_protocol_py-0.6.0/src/cowboy_protocol/error_codes.py +633 -0
  13. cowboy_protocol_py-0.6.0/src/cowboy_protocol/py.typed +0 -0
  14. cowboy_protocol_py-0.6.0/tests/fixtures/cbqs-v2/register-provider/input.json +200 -0
  15. cowboy_protocol_py-0.6.0/tests/fixtures/cbqs-v2/register-provider/transaction.hex +1 -0
  16. cowboy_protocol_py-0.6.0/tests/fixtures/cbss/blind_release_v4.json +267 -0
  17. cowboy_protocol_py-0.6.0/tests/fixtures/cip45/requests.json +253 -0
  18. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deferred_origin/input.json +133 -0
  19. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deferred_origin/preimage.bin +0 -0
  20. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deferred_origin/signing_hash.hex +1 -0
  21. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deferred_origin/submission.bin +0 -0
  22. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deploy/input.json +120 -0
  23. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deploy/preimage.bin +0 -0
  24. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deploy/signing_hash.hex +1 -0
  25. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deploy/submission.bin +0 -0
  26. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deploy_routes/input.json +130 -0
  27. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deploy_routes/preimage.bin +0 -0
  28. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deploy_routes/signing_hash.hex +1 -0
  29. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deploy_routes/submission.bin +0 -0
  30. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deploy_shard/input.json +124 -0
  31. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deploy_shard/preimage.bin +0 -0
  32. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deploy_shard/signing_hash.hex +1 -0
  33. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/deploy_shard/submission.bin +0 -0
  34. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/execute/input.json +106 -0
  35. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/execute/preimage.bin +0 -0
  36. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/execute/signing_hash.hex +1 -0
  37. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/execute/submission.bin +0 -0
  38. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/max_u128_access/input.json +100 -0
  39. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/max_u128_access/preimage.bin +0 -0
  40. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/max_u128_access/signing_hash.hex +1 -0
  41. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/max_u128_access/submission.bin +0 -0
  42. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/max_u64/input.json +100 -0
  43. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/max_u64/preimage.bin +0 -0
  44. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/max_u64/signing_hash.hex +1 -0
  45. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/max_u64/submission.bin +0 -0
  46. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/minimal/input.json +99 -0
  47. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/minimal/preimage.bin +0 -0
  48. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/minimal/signing_hash.hex +1 -0
  49. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/minimal/submission.bin +0 -0
  50. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/multi_instruction/input.json +135 -0
  51. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/multi_instruction/preimage.bin +0 -0
  52. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/multi_instruction/signing_hash.hex +1 -0
  53. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/multi_instruction/submission.bin +0 -0
  54. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/multisig/input.json +249 -0
  55. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/multisig/preimage.bin +0 -0
  56. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/multisig/signing_hash.hex +1 -0
  57. cowboy_protocol_py-0.6.0/tests/fixtures/commonware-codec/multisig/submission.bin +0 -0
  58. cowboy_protocol_py-0.6.0/tests/fixtures/ras/vectors.json +11439 -0
  59. cowboy_protocol_py-0.6.0/tests/fixtures/retired-system-instructions.json +141 -0
  60. cowboy_protocol_py-0.6.0/tests/fixtures/workload/transactions.json +1087 -0
  61. cowboy_protocol_py-0.6.0/tests/test_fixture_sync.py +140 -0
  62. cowboy_protocol_py-0.6.0/tests/test_golden.py +1095 -0
@@ -0,0 +1,11 @@
1
+ # Python build / install artifacts (the .wasm under src/cowboy_protocol/_wasm/
2
+ # is committed package data and must NOT be ignored).
3
+ __pycache__/
4
+ *.py[cod]
5
+ *.egg-info/
6
+ .eggs/
7
+ build/
8
+ dist/
9
+ .pytest_cache/
10
+ .venv/
11
+ venv/
@@ -0,0 +1,178 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright (c) 2025 Commonware, Inc.
6
+
7
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
+
9
+ 1. Definitions.
10
+
11
+ "License" shall mean the terms and conditions for use, reproduction,
12
+ and distribution as defined by Sections 1 through 9 of this document.
13
+
14
+ "Licensor" shall mean the copyright owner or entity authorized by
15
+ the copyright owner that is granting the License.
16
+
17
+ "Legal Entity" shall mean the union of the acting entity and all
18
+ other entities that control, are controlled by, or are under common
19
+ control with that entity. For the purposes of this definition,
20
+ "control" means (i) the power, direct or indirect, to cause the
21
+ direction or management of such entity, whether by contract or
22
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
23
+ outstanding shares, or (iii) beneficial ownership of such entity.
24
+
25
+ "You" (or "Your") shall mean an individual or Legal Entity
26
+ exercising permissions granted by this License.
27
+
28
+ "Source" form shall mean the preferred form for making modifications,
29
+ including but not limited to software source code, documentation
30
+ source, and configuration files.
31
+
32
+ "Object" form shall mean any form resulting from mechanical
33
+ transformation or translation of a Source form, including but
34
+ not limited to compiled object code, generated documentation,
35
+ and conversions to other media types.
36
+
37
+ "Work" shall mean the work of authorship, whether in Source or
38
+ Object form, made available under the License, as indicated by a
39
+ copyright notice that is included in or attached to the work
40
+ (an example is provided in the Appendix below).
41
+
42
+ "Derivative Works" shall mean any work, whether in Source or Object
43
+ form, that is based on (or derived from) the Work and for which the
44
+ editorial revisions, annotations, elaborations, or other modifications
45
+ represent, as a whole, an original work of authorship. For the purposes
46
+ of this License, Derivative Works shall not include works that remain
47
+ separable from, or merely link (or bind by name) to the interfaces of,
48
+ the Work and Derivative Works thereof.
49
+
50
+ "Contribution" shall mean any work of authorship, including
51
+ the original version of the Work and any modifications or additions
52
+ to that Work or Derivative Works thereof, that is intentionally
53
+ submitted to Licensor for inclusion in the Work by the copyright owner
54
+ or by an individual or Legal Entity authorized to submit on behalf of
55
+ the copyright owner. For the purposes of this definition, "submitted"
56
+ means any form of electronic, verbal, or written communication sent
57
+ to the Licensor or its representatives, including but not limited to
58
+ communication on electronic mailing lists, source code control systems,
59
+ and issue tracking systems that are managed by, or on behalf of, the
60
+ Licensor for the purpose of discussing and improving the Work, but
61
+ excluding communication that is conspicuously marked or otherwise
62
+ designated in writing by the copyright owner as "Not a Contribution."
63
+
64
+ "Contributor" shall mean Licensor and any individual or Legal Entity
65
+ on behalf of whom a Contribution has been received by Licensor and
66
+ subsequently incorporated within the Work.
67
+
68
+ 2. Grant of Copyright License. Subject to the terms and conditions of
69
+ this License, each Contributor hereby grants to You a perpetual,
70
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71
+ copyright license to reproduce, prepare Derivative Works of,
72
+ publicly display, publicly perform, sublicense, and distribute the
73
+ Work and such Derivative Works in Source or Object form.
74
+
75
+ 3. Grant of Patent License. Subject to the terms and conditions of
76
+ this License, each Contributor hereby grants to You a perpetual,
77
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78
+ (except as stated in this section) patent license to make, have made,
79
+ use, offer to sell, sell, import, and otherwise transfer the Work,
80
+ where such license applies only to those patent claims licensable
81
+ by such Contributor that are necessarily infringed by their
82
+ Contribution(s) alone or by combination of their Contribution(s)
83
+ with the Work to which such Contribution(s) was submitted. If You
84
+ institute patent litigation against any entity (including a
85
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
86
+ or a Contribution incorporated within the Work constitutes direct
87
+ or contributory patent infringement, then any patent licenses
88
+ granted to You under this License for that Work shall terminate
89
+ as of the date such litigation is filed.
90
+
91
+ 4. Redistribution. You may reproduce and distribute copies of the
92
+ Work or Derivative Works thereof in any medium, with or without
93
+ modifications, and in Source or Object form, provided that You
94
+ meet the following conditions:
95
+
96
+ (a) You must give any other recipients of the Work or
97
+ Derivative Works a copy of this License; and
98
+
99
+ (b) You must cause any modified files to carry prominent notices
100
+ stating that You changed the files; and
101
+
102
+ (c) You must retain, in the Source form of any Derivative Works
103
+ that You distribute, all copyright, patent, trademark, and
104
+ attribution notices from the Source form of the Work,
105
+ excluding those notices that do not pertain to any part of
106
+ the Derivative Works; and
107
+
108
+ (d) If the Work includes a "NOTICE" text file as part of its
109
+ distribution, then any Derivative Works that You distribute must
110
+ include a readable copy of the attribution notices contained
111
+ within such NOTICE file, excluding those notices that do not
112
+ pertain to any part of the Derivative Works, in at least one
113
+ of the following places: within a NOTICE text file distributed
114
+ as part of the Derivative Works; within the Source form or
115
+ documentation, if provided along with the Derivative Works; or,
116
+ within a display generated by the Derivative Works, if and
117
+ wherever such third-party notices normally appear. The contents
118
+ of the NOTICE file are for informational purposes only and
119
+ do not modify the License. You may add Your own attribution
120
+ notices within Derivative Works that You distribute, alongside
121
+ or as an addendum to the NOTICE text from the Work, provided
122
+ that such additional attribution notices cannot be construed
123
+ as modifying the License.
124
+
125
+ You may add Your own copyright statement to Your modifications and
126
+ may provide additional or different license terms and conditions
127
+ for use, reproduction, or distribution of Your modifications, or
128
+ for any such Derivative Works as a whole, provided Your use,
129
+ reproduction, and distribution of the Work otherwise complies with
130
+ the conditions stated in this License.
131
+
132
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
133
+ any Contribution intentionally submitted for inclusion in the Work
134
+ by You to the Licensor shall be under the terms and conditions of
135
+ this License, without any additional terms or conditions.
136
+ Notwithstanding the above, nothing herein shall supersede or modify
137
+ the terms of any separate license agreement you may have executed
138
+ with Licensor regarding such Contributions.
139
+
140
+ 6. Trademarks. This License does not grant permission to use the trade
141
+ names, trademarks, service marks, or product names of the Licensor,
142
+ except as required for reasonable and customary use in describing the
143
+ origin of the Work and reproducing the content of the NOTICE file.
144
+
145
+ 7. Disclaimer of Warranty. Unless required by applicable law or
146
+ agreed to in writing, Licensor provides the Work (and each
147
+ Contributor provides its Contributions) on an "AS IS" BASIS,
148
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149
+ implied, including, without limitation, any warranties or conditions
150
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151
+ PARTICULAR PURPOSE. You are solely responsible for determining the
152
+ appropriateness of using or redistributing the Work and assume any
153
+ risks associated with Your exercise of permissions under this License.
154
+
155
+ 8. Limitation of Liability. In no event and under no legal theory,
156
+ whether in tort (including negligence), contract, or otherwise,
157
+ unless required by applicable law (such as deliberate and grossly
158
+ negligent acts) or agreed to in writing, shall any Contributor be
159
+ liable to You for damages, including any direct, indirect, special,
160
+ incidental, or consequential damages of any character arising as a
161
+ result of this License or out of the use or inability to use the
162
+ Work (including but not limited to damages for loss of goodwill,
163
+ work stoppage, computer failure or malfunction, or any and all
164
+ other commercial damages or losses), even if such Contributor
165
+ has been advised of the possibility of such damages.
166
+
167
+ 9. Accepting Warranty or Additional Liability. While redistributing
168
+ the Work or Derivative Works thereof, You may choose to offer,
169
+ and charge a fee for, acceptance of support, warranty, indemnity,
170
+ or other liability obligations and/or rights consistent with this
171
+ License. However, in accepting such obligations, You may act only
172
+ on Your own behalf and on Your sole responsibility, not on behalf
173
+ of any other Contributor, and only if You agree to indemnify,
174
+ defend, and hold each Contributor harmless for any liability
175
+ incurred by, or claims asserted against, such Contributor by reason
176
+ of your accepting any such warranty or additional liability.
177
+
178
+ END OF TERMS AND CONDITIONS
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Commonware, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,283 @@
1
+ Metadata-Version: 2.5
2
+ Name: cowboy-protocol-py
3
+ Version: 0.6.0
4
+ Summary: Pure-Python wrapper over the cowboy-protocol-wasm-ffi codec wasm (the canonical Cowboy transaction codec, via wasmtime). The Python analog of @cowboyinc/protocol-wasm.
5
+ Project-URL: Repository, https://github.com/cowboyinc/core
6
+ Project-URL: Homepage, https://github.com/cowboyinc/core/tree/devnet/protocol/bindings/python
7
+ Author: Cowboy Foundation
8
+ License-Expression: MIT OR Apache-2.0
9
+ License-File: LICENSE-APACHE
10
+ License-File: LICENSE-MIT
11
+ Keywords: blockchain,codec,cowboy,transaction,wasm
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Rust
14
+ Requires-Python: >=3.10
15
+ Requires-Dist: wasmtime>=20
16
+ Provides-Extra: test
17
+ Requires-Dist: pytest>=7; extra == 'test'
18
+ Description-Content-Type: text/markdown
19
+
20
+ # cowboy-protocol-py
21
+
22
+ The canonical Cowboy transaction codec for Python — a thin, **pure-Python**
23
+ wrapper (import name `cowboy_protocol`) that loads the
24
+ [`cowboy-protocol-wasm-ffi`](../../crates/cowboy-protocol-wasm-ffi) `.wasm` via
25
+ the [`wasmtime`](https://pypi.org/project/wasmtime/) pip package and exposes a
26
+ Pythonic API.
27
+
28
+ It is the Python analog of `@cowboyinc/protocol-wasm`: every byte and every
29
+ signature comes from the **same** `cowboy-protocol-codec` compiled to wasm, so
30
+ `python == native == wasm == ts` — byte-identical, proven by the golden gate
31
+ (every vector in `crates/cowboy-protocol-codec/fixtures/commonware-codec/`
32
+ plus the quoted CBQS `CreateStream` wire vector).
33
+
34
+ This package lives **in** [`cowboyinc/core`](https://github.com/cowboyinc/core)
35
+ under `protocol/bindings/python`, co-located and versioned with the wasm and its
36
+ flat ABI, so the ABI and its only Python consumer can never drift across repos.
37
+ (Paths below are relative to `protocol/`.) The bundled `.wasm` is built from the
38
+ wasm-ffi crate via `crates/cowboy-protocol-wasm-ffi/build-ffi.sh` and committed.
39
+ A codec change makes the committed artifact stale; CI reports that as a warning
40
+ and the release owner refreshes it before the next release, which refuses to
41
+ publish a stale one. The package tests load that committed artifact and check
42
+ golden bytes and retired-instruction rejection.
43
+
44
+ Version 0.6.0 ships codec/FFI 0.4.0 and rejects the retired
45
+ `EntitlementCreateRole`, `EntitlementAssignRole` and `EntitlementRevokeRole`
46
+ variants. This is a breaking, coordinated fresh-devnet release (COW-4964).
47
+
48
+ ## Why pure-Python
49
+
50
+ `wasmtime` ships its own platform wheels, so installing this package needs **no
51
+ Rust/C compiler** — `pip install` just pulls the `wasmtime` wheel and the
52
+ committed `.wasm`. The wasm has **no JS / wasm-bindgen imports** (it instantiates
53
+ with an empty import set), so a plain wasmtime host runs it directly.
54
+
55
+ ## Install
56
+
57
+ ```bash
58
+ pip install cowboy-protocol-py
59
+ ```
60
+
61
+ To install an unreleased revision, pin it by commit:
62
+
63
+ ```bash
64
+ pip install "cowboy-protocol-py @ git+https://github.com/cowboyinc/core.git@<rev>#subdirectory=protocol/bindings/python"
65
+ ```
66
+
67
+ Replace `<rev>` with a full commit SHA (or tag) to pin. For local development
68
+ against a checkout:
69
+
70
+ ```bash
71
+ pip install -e bindings/python
72
+ ```
73
+
74
+ ## API
75
+
76
+ ```python
77
+ import cowboy_protocol as cp
78
+
79
+ cp.signing_preimage(tx_json: str) -> bytes
80
+ cp.signing_hash(tx_json: str) -> bytes # 32 bytes
81
+ cp.signing_hash_hex(tx_json: str) -> str # lowercase hex, no 0x
82
+ cp.encode_transaction(tx_json: str) -> bytes # full canonical tx encoding
83
+ cp.encode_submission(tx_json: str) -> bytes # the POST /submit body
84
+ cp.sign(hash32: bytes, priv_key32: bytes) -> bytes # 65-byte r||s||v
85
+ cp.sign_transaction(tx_json: str, priv_key32: bytes) -> str # signed tx as canyon-JSON
86
+ cp.address_from_private_key(priv_key32: bytes) -> str # EIP-55 checksum
87
+ ```
88
+
89
+ `tx_json` is the **canyon-JSON transaction shape** — byte-for-byte the same
90
+ serde form node serializes, identical to the golden vectors' `input.json` (see
91
+ `crates/cowboy-protocol-codec/fixtures/commonware-codec/<name>/input.json`).
92
+
93
+ On any wasm error (malformed JSON, bad private key, etc.) the wrapper reads the
94
+ wasm's `cpw_last_error` and raises `cowboy_protocol.CowboyProtocolError` — it
95
+ never returns garbage.
96
+
97
+ ### RAS + CBFS control-plane signing bytes
98
+
99
+ The canonical **signing-byte** contract (`-codec`'s `ras` + `cbfs_signing`
100
+ modules) is surfaced so a non-Rust signer — the python-sdk — derives the bytes
101
+ it signs from here instead of re-implementing them (a byte-level fork is exactly
102
+ the split that caused the 2026-07-11 outage, COW-2608):
103
+
104
+ ```python
105
+ # RAS consensus-auth (COW-2636)
106
+ cp.owner_authorization_signing_bytes(authorization_json: str) -> bytes # RasOwnerAuthorization
107
+ cp.owner_delegation_signing_bytes(delegation_json: str) -> bytes # RasOwnerDelegation
108
+ cp.relay_authorization_signing_bytes(authorization_json: str) -> bytes # RasRelayAuthorization
109
+
110
+ # CBFS control-plane (COW-2649)
111
+ cp.delegation_cert_signing_bytes(cert_json: str) -> bytes # DelegationCert
112
+ cp.delegation_cert_hash(cert_json: str) -> bytes # 32-byte keccak256(signing bytes)
113
+ cp.owner_cap_token_signing_bytes(token_json: str) -> bytes # OwnerCapTokenV1
114
+ cp.delegated_request_signing_bytes(request_input_json: str) -> bytes # DelegatedRequestInput
115
+ cp.relay_usage_signing_bytes(usage_input_json: str) -> bytes # RelayUsageSigningInput
116
+ ```
117
+
118
+ Each takes **one** canyon-JSON document — the serde shape of the named `-codec`
119
+ struct, byte-for-byte the fixtures' `input` (fixed-width byte fields are JSON
120
+ arrays of `u8`; `Address` is the `0x…` string; `Option<T>` is the value or
121
+ `null`). `RasOwnerDelegation` and `DelegationCert` require a typed
122
+ `service_class`: the string `MOUNT_CAPABLE_V1` or `NON_MOUNT_SERVICE_V1`, or
123
+ one of the bound classes `{"VOLUME_BOUND_SERVICE_V1": {"volume_id": [32 u8],
124
+ "workload_id": [32 u8]}}`, `{"WORKLOAD_MOUNT_V1": {"volume_id": [32 u8],
125
+ "workload_id": [32 u8]}}` and `{"SERVICE_RELEASE_V1": {"volume_id": [32 u8]}}`;
126
+ omission and unknown classes fail closed. Every byte comes from the **same** wasm the
127
+ native golden gate pins:
128
+ `tests/test_golden.py` drives these against
129
+ `crates/cowboy-protocol-codec/fixtures/ras/vectors.json`, the same manifest the
130
+ Rust `wasmtime_golden` gate pins, so `python == native == wasm`. See the
131
+ per-function docstrings and the crate-level ABI doc in
132
+ `crates/cowboy-protocol-wasm-ffi/src/lib.rs` for the exact per-struct JSON shape.
133
+
134
+ ### Example
135
+
136
+ ```python
137
+ import cowboy_protocol as cp
138
+
139
+ tx = """{
140
+ "chain_id": 42, "nonce": 0,
141
+ "instruction": {"System": {"CreateAccount": {"account": "0x0000000000000000000000000000000000001111"}}},
142
+ "cycles_limit": 50000, "cells_limit": 50000, "access_limit": 512,
143
+ "max_fee_per_cycle": 1, "max_fee_per_cell": 1, "max_fee_per_access": "1",
144
+ "max_priority_fee_per_cycle": 0, "max_priority_fee_per_cell": 0,
145
+ "max_priority_fee_per_access": "0",
146
+ "from": "0x1a642f0E3c3aF545E7AcBD38b07251B3990914F1",
147
+ "access_list": null, "signature": {"r": [0], "s": [0], "v": 0},
148
+ "additional_signers": [], "origin_tx_hash": null,
149
+ "origin_remaining_cycles": null, "origin_remaining_cells": null,
150
+ "origin_remaining_access": null, "metadata": []
151
+ }"""
152
+
153
+ priv = bytes([1]) * 32
154
+ print(cp.address_from_private_key(priv)) # EIP-55 checksummed
155
+ h = cp.signing_hash(tx) # 32 bytes
156
+ sig = cp.sign(h, priv) # 65 bytes r||s||v
157
+ signed_tx_json = cp.sign_transaction(tx, priv)
158
+ body = cp.encode_submission(signed_tx_json) # bytes to POST /submit
159
+ ```
160
+
161
+ ## Security — in-wasm signing threat model
162
+
163
+ `sign`, `sign_transaction` and `address_from_private_key` pass the raw 32-byte
164
+ private key **into wasm**, where it lives in the host's linear memory for the
165
+ duration of the call. This is the intentionally **weaker** threat model accepted
166
+ for COW-2363; isolated-signer hardening is deferred. Do not feed high-value keys
167
+ to these in-process signers in an untrusted host.
168
+
169
+ ## The golden gate (anti-drift)
170
+
171
+ `tests/test_golden.py` loads this package for every transaction fixture vector and the
172
+ quoted CBQS `CreateStream` vector, then asserts `cp.*` reproduces
173
+ `preimage.bin` / `signing_hash.hex` / `submission.bin`
174
+ **byte-identically** and reproduces the golden RFC-6979 signatures. It also
175
+ replays the RAS/CBFS signing manifest, the CIP-10 workload transactions and the
176
+ JobSpec vectors.
177
+
178
+ The vectors live in `tests/fixtures`: copies of the source fixtures under
179
+ `crates/`, shipped in the sdist so the gate runs against an installed package
180
+ with no checkout. `tests/test_fixture_sync.py` compares each copy with its
181
+ source; refresh them with `python bindings/python/tests/test_fixture_sync.py`
182
+ (it needs only Python).
183
+
184
+ CI (`cowboyinc/core`'s `.github/workflows/core.yml`, the `protocol-python` job)
185
+ runs this suite on the lowest and a current supported Python, twice:
186
+
187
+ - **Blocking: against the commit's own codec.** In a temporary copy of the
188
+ package, with the `.wasm` the `wasm` job just built and fixtures refreshed
189
+ from source. A codec change that breaks the Python binding fails here.
190
+ - **Advisory: against what is committed.** The committed `.wasm` and the
191
+ committed fixture copies. If either is stale the job shows a warning and
192
+ still passes. A codec change never has to touch this package.
193
+
194
+ The release owner refreshes both the `.wasm` and the fixture copies before
195
+ tagging, and the release workflow refuses to publish until they are current.
196
+
197
+ A bit-exact committed-vs-fresh byte-match is intentionally **not** used: wasm
198
+ builds aren't reproducible across toolchains (macOS dev vs CI ubuntu), so a
199
+ byte-match would false-fail on benign build differences rather than real drift.
200
+ Instead the `wasm` job in `core.yml` rebuilds the `.wasm` from source via
201
+ `build-ffi.sh` and runs the wasmtime golden gate twice. The fresh build must
202
+ equal the native codec: that blocks. The committed package `.wasm` is checked
203
+ the same way, and against its build record, but only reports: a stale one shows
204
+ a warning and does not fail the job. The release workflow runs those same two
205
+ checks and blocks on them.
206
+
207
+ ## Refreshing the bundled wasm
208
+
209
+ **This is the release owner's step before tagging a release. It is optional on
210
+ an ordinary pull request:** a codec change makes the committed wasm, and
211
+ possibly the fixture copies, stale; CI says so in a warning, and nothing is
212
+ blocked. `publish-protocol-py.yml` refuses to release until the wasm, its
213
+ record and the fixture copies are current.
214
+
215
+ The shipped `.wasm` is the one CI builds on Linux with the pinned toolchain
216
+ (`rust-toolchain.toml`, `wasm32-unknown-unknown`, `--release --locked`, no
217
+ `wasm-opt`). It is never built on a developer machine. `_provenance.json`,
218
+ shipped beside it and returned by `cowboy_protocol.provenance()`, records its
219
+ sha256 and size, the JobSpec version, the toolchain, and a digest of what the
220
+ wasm is compiled from, and only that:
221
+
222
+ - `src/`, `Cargo.toml` and `build.rs` of the codec crate,
223
+ `cowboy-protocol-types` and the wasm-ffi crate. Tests and fixtures are not
224
+ inputs.
225
+ - The `Cargo.lock` entries the wasm-ffi crate resolves for
226
+ `wasm32-unknown-unknown`, from `cargo tree`. The rest of the monorepo's
227
+ lockfile is not an input.
228
+ - `rust-toolchain.toml` and the workspace's `[profile.release]` section.
229
+ - `crates/cowboy-protocol-wasm-ffi/build-ffi.sh`, the build recipe. It refuses
230
+ to run with `RUSTFLAGS` (or its `CARGO_*` equivalents) or a
231
+ `CARGO_PROFILE_RELEASE_*` override set, so the script holds every flag.
232
+ - The repository's `.cargo/config.toml`, which can set `rustflags` or
233
+ `[profile.release]` overrides for the same build.
234
+
235
+ Releases are cut by `.github/workflows/publish-protocol-py.yml`; see
236
+ `docs/ci/protocol-py-releases.md` in the repository root.
237
+
238
+ `scripts/protocol-py-provenance.py check` (it needs cargo and Python 3.11+)
239
+ fails when the committed wasm or any of those inputs no longer matches the
240
+ record, and refuses a record written with `record --allow-host`, for a
241
+ non-Linux build host, or for a wasm whose `producers` section names another
242
+ compiler than the record's `rustc` at the pinned toolchain. It is advisory in the `wasm` job of `core.yml` and blocking in the
243
+ release workflow. To refresh, from the repository root:
244
+
245
+ ```bash
246
+ # 1. Start from the commit to release: a branch at the tip of devnet.
247
+ # 2. Download the build core.yml's `wasm` job uploaded for that commit. Every
248
+ # devnet push and pull request run uploads one, kept for 30 days:
249
+ gh run list --workflow core.yml --branch devnet --event push --limit 5
250
+ gh run download <run-id> --name protocol-py-wasm --dir /tmp/protocol-py-wasm
251
+ # 3. Install it and write the record. This refuses a wasm that does not match
252
+ # its sha256 file, was not built by the pinned toolchain on Linux, or was
253
+ # built from inputs other than the ones in this checkout. If it refuses,
254
+ # the run built different inputs: use the run for the commit you are on.
255
+ python3 scripts/protocol-py-provenance.py record \
256
+ --wasm /tmp/protocol-py-wasm/cowboy_protocol_wasm_ffi.wasm \
257
+ --sha256 /tmp/protocol-py-wasm/cowboy_protocol_wasm_ffi.wasm.sha256 \
258
+ --rustc-vv /tmp/protocol-py-wasm/rustc-vv.txt \
259
+ --inputs /tmp/protocol-py-wasm/inputs.json
260
+ python3 scripts/protocol-py-provenance.py check
261
+ # 4. Commit the wasm and _provenance.json together, merge that pull request,
262
+ # and tag the merged commit.
263
+ ```
264
+
265
+ Refresh the fixture copies in the same change: run
266
+ `python protocol/bindings/python/tests/test_fixture_sync.py` and commit whatever it
267
+ changes under `tests/fixtures`.
268
+
269
+ `source_revision` in the record is the release commit. A commit cannot name
270
+ itself, so it is `null` in the repository and written by the publish workflow
271
+ before the archives are built.
272
+
273
+ Run locally:
274
+
275
+ ```bash
276
+ pip install -e bindings/python
277
+ pip install pytest
278
+ python -m pytest bindings/python/tests -v
279
+ ```
280
+
281
+ ## License
282
+
283
+ MIT OR Apache-2.0.