From 0a57d9a3b032992a862698c80dae1cc45bdb7339 Mon Sep 17 00:00:00 2001 From: Lola Aimar Date: Fri, 26 Jun 2026 17:55:40 -0300 Subject: [PATCH 1/4] updates readme --- README.md | 191 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 110 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index d5ec5aa..5082bc6 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,67 @@ -# Warden tool +# Midnight: Warden Tool -CLI-based application to generate compact code that checks commitments and time-lock conditions based on multisignature scripts. Accepts JSON inputs structured around Cardano native script patterns, supporting nested conditions (`any`, `all`, `atLeast`). - -The idea is that the CLI generates a Compact contract module that can be used to grant access to circuits based on a set of commitments. The contract has three exported circuits: `init`, `commit` and `verify`: - -- `init` is used to initalize the contract's state -- `commit` is used by a user when it wants to add its commitment to the set to authorize a certain circuit running. -- `verify` is used within a circuit to ensure that it will be run if and only if the set of commitments present satisfies the predefined assertions. - The predefined assertions are constructed based on Cardano native scripts. +CLI-based code generation tool that produces a **reusable Compact access-control module** from JSON inputs modeled on Cardano native scripts. The generated `Warden.compact` can be imported by any Midnight contract to gate circuits behind multisig-equivalent authorization policies (commitment sets, time locks, and nested combinators). ## Table of Contents -- [Warden tool](#warden-tool) +- [Midnight: Warden Tool](#midnight-warden-tool) - [Table of Contents](#table-of-contents) - - [Prerequisites](#prerequisites) - - [Setup](#setup) - - [Install CLI globally](#install-cli-globally) - - [Documentation](#documentation) + - [Motivation](#motivation) + - [How It Works](#how-it-works) + - [Limitations](#limitations) - [Authorization workflow](#authorization-workflow) - [Participant](#participant) - [Script author](#script-author) - - [Commands](#commands) - - [Commitment generator](#commitment-generator) - - [Script wizard](#script-wizard) + - [Prerequisites](#prerequisites) + - [Setup](#setup) + - [Install CLI globally](#install-cli-globally) - [Usage](#usage) - - [Compact code generator](#compact-code-generator) - [Compile](#compile) - [Test](#test) - - [E2E example](#e2e-example) + - [E2E Example: TokenSupply](#e2e-example-tokensupply) + - [Future work](#future-work) -## Prerequisites +## Motivation -- Compact Devtools 0.4.0 (check with `compact --version`) -- Compact Toolchain 0.31.0 (check with `compact compile --version`) -- PNPM 10.30.1 +It is desirable to have contracts in Midnight that handle multisignature scripts like in Cardano. In these scripts the authorization condition is that the transaction has signatures from multiple cryptographic keys, according to a requirement that can be "allOf", "anyOf" or "atLeastNOfM". -## Setup +The goal is to have Midnight contracts that provide access control to circuits following these well-known multisignature patterns. Whereas a common multisignature script involves multiple signatures being gathered on a single transaction before submission, an implementation in Midnight must be different because of the blockchain's own design and limitations. -Install dependencies +Midnight doesn't use signatures for transactions involving contracts, so instead the prototype is built on commitments. Commitments are a cryptographic primitive that allows one to commit to a value without revealing it. -```bash -pnpm install -``` +In other blockchains, multisignature scripts work by gathering signatures off-chain and submitting a single fully-signed transaction. In Midnight, proofs are what guarantee correctness when a transaction is submitted, but obtaining partial proofs is not feasible and also introduces a problem with the order of authorizations. This is why this implementation uses commitments instead of proofs: a user can commit in one transaction, and the contract later verifies that the required set of commitments has been accumulated across multiple transactions before allowing the protected operation. -### Install CLI globally +There are two other core concerns as well: making the solution simple for the end user, and engaging developers coming from the Cardano ecosystem. Because of these, the usage of this implementation is based on Cardano native scripts, which are scripts composed of clauses like `any`, `all` and `atLeastNOfM`, that refer to the signatures, `after` and `before`, which are time conditions, and regular signature expressions. These scripts can be written using JSON syntax, and the format used here is largely similar to the one accepted by the `cardano-cli` as described in IntersectMBO's Cardano node reference. This simplifies the input to lists of commitments and condition descriptors, and also provides familiarity for Cardano developers. Nevertheless, the usage is different since in Cardano native scripts create script addresses, and in this implementation the authorization functions are Compact circuits that have to be called from within other Compact contracts. -Build the project, then register the `warden-tool` binary on your PATH: +## How It Works -```bash -pnpm build -pnpm link --global -``` +The tool reads a JSON file describing the authorization policy (commitment hashes, time locks, composite conditions) and generates a Compact module called **Warden** with three exported circuits. See [docs/schema.md](docs/schema.md) for the complete schema design documentation, and [docs/design.md](docs/design.md) for a description of the generated Compact code and usage instructions. -Now `warden-tool` is available as a system-wide command: +| Circuit | Purpose | +|---------|---------| +| `init()` | Initializes the on-ledger maps that encode the policy | +| `commit()` | A user registers their commitment (proves knowledge of a secret + randomness) | +| `verify()` | Checks that the set of committed commitments satisfies the policy conditions | -```bash -warden-tool --help -warden-tool generate-code -i -warden-tool make-commitment -s -``` +Any contract that needs access control **imports** `Warden`, calls `init()` in its constructor, exposes `commit()` to participants, and calls `verify()` before any protected operation: -> **Note:** `pnpm link --global` creates a symlink to your local build. After pulling changes or rebuilding, the command reflects updates automatically. +```compact +import "generated/Warden" prefix Warden_; + +constructor() { + Warden_init(); + // ... other initialization logic +} -## Documentation +export circuit protectedAction(): [] { + Warden_verify(); + // ... application logic +} +``` -See [docs/schema.md](docs/schema.md) for the complete schema design documentation, and [docs/design.md](docs/design.md) for a description of the Compact code that the CLI generates and usage instructions. +## Limitations + +The compilation of Compact code creates some restrictions: the circuits are compiled into fixed ZK circuits, so every aspect must be determined at compile time. Because of this, abstract data structures with arbitrary or undetermined values are not accepted. Our workaround for these constraints was to hard code the clauses and expected commitments into the circuits. This represents a limitation because the conditions and commitments cannot be changed without recompiling and redeploying the contract. ## Authorization workflow @@ -78,6 +75,10 @@ Each person who needs to authorize operations runs `make-commitment` to generate pnpm make-commitment -o my-pair.json ``` +Optional parameters: +- `-s, --seed ` — 64-character hex seed (deterministic secret generation) +- `-o, --output ` — Path to write the result as JSON (if omitted, prints to stdout) + This produces a file like: ```json @@ -114,7 +115,7 @@ Commitments from participants Native script definition **Step 1 — Collect commitments** -Ask each participant to run `make-commitment` and share the `commitment` hex with you. +Ask each participant to run `pnpm make-commitment` (or `warden-tool make-commitment`) and share the `commitment` hex with you. **Step 2 — Build the native script** @@ -124,6 +125,7 @@ Write a JSON file describing the authorization conditions. You can either: ```bash pnpm script-wizard ``` + Walks through script node types — commitment (`cmt`), time locks (`after`/`before`), and composites (`any`/`all`/`atLeast`) — and writes the result to a JSON file (defaults to `script.json`). - Write the JSON manually (see [the schema docs](docs/schema.md) and the [examples directory](examples/inputs/)). The script references each commitment by its hex hash: @@ -144,8 +146,14 @@ The script references each commitment by its hex hash: pnpm generate-code -i script.json -o generated/ ``` +Equivalently, using the global CLI: `warden-tool generate-code -i script.json -o generated/` + This creates `generated/Warden.compact` — a Compact module with the authorization logic baked in. +Optional parameters: +- `-o, --output ` — Output directory (default: `generated/`) +- `-t, --test` — Include import/export boilerplate for unit testing + **Step 4 — Import into your project** ```compact @@ -154,51 +162,40 @@ import "generated/Warden"; Use `verify()` as a guard before any protected operation. -## Commands +## Prerequisites + +- Compact Devtools 0.4.0 (check with `compact --version`) +- Compact Toolchain 0.31.0 (check with `compact compile --version`) +- PNPM 10.30.1 -### Commitment generator +## Setup + +Install dependencies ```bash -pnpm make-commitment -s -o +pnpm install ``` -Generates a SecretPair comprised of a `secret` and a `randomness`, and the corresponding `commitment` product of these two. -Optional parameters are: - -- -s, --seed 64-character hex seed for the secret -- -o, --output Path to write the result as JSON +### Install CLI globally -### Script wizard +Build the project, then register the `warden-tool` binary on your PATH: ```bash -pnpm script-wizard +pnpm build +pnpm link --global ``` -Launches an interactive wizard that builds a native script schema JSON file -that can be used as input to the `generate-code` command. -Walks through script node types — commitment (`cmt`), time locks (`after`/`before`), -and composites (`any`/`all`/`atLeast`) — and writes the result to a JSON file -(defaults to `script.json`). -Ensure all commitments required have been gathered prior to running this command. - -## Usage - -### Compact code generator +Now `warden-tool` is available as a system-wide command: ```bash -pnpm generate-code -i -pnpm generate-code -i -o -pnpm generate-code -i -o -t +warden-tool --help +warden-tool generate-code -i +warden-tool make-commitment -s ``` -Generates a Compact contract module (`Warden.compact`) from a JSON input file. -The input file defines the script tree (commitment hashes, composite conditions, -time locks) following the schema documented below. - -Optional parameters are: +> **Note:** `pnpm link --global` creates a symlink to your local build. After pulling changes or rebuilding, the command reflects updates automatically. -- `-o, --output ` Directory to write the generated Compact code (default: `generated/`) -- `-t, --test` Include import/export boilerplate for unit testing +## Usage ### Compile @@ -206,7 +203,12 @@ Optional parameters are: pnpm compact ``` -Compiles the generated Compact code and writes the artifacts into `generated/managed`. +Compiles `generated/Warden.compact` and writes the artifacts into `generated/managed`. +To compile from or to a different location, use `compact compile` directly: + +```bash +compact compile path/to/Contract.compact path/to/output-dir/contract +``` ### Test @@ -222,11 +224,38 @@ To use another example, the command must be run like: TEST_INPUT=examples/inputs/ pnpm test ``` -## E2E example +## E2E Example: TokenSupply + +A complete end-to-end example demonstrating how any application contract can import and use the Warden access-control module. + +The top-level contract is **TokenSupply**, which exposes `mint` and `burn` circuits. Both call `Warden_verify()` to check that the required ledger commitments satisfy the configured policy before proceeding. This separation keeps the application logic entirely agnostic to the authorization strategy. + +**Authorization flow:** + +``` + 1. Deploy: TokenSupply.constructor() → Warden.init() populates authorized users + 2. Commit: Users call TokenSupply.commit() which delegates to Warden.commit() + to register their secret commitments on-ledger + 3. Mint/Burn: TokenSupply.mint(amount, recipient) calls Warden_verify() internally. + Only if the committed users satisfy the native script policy + does the mint proceed +``` + +The **initial policy** is an `all` of four commitments — all four must be registered before any mint/burn is allowed. + +The example includes: +- **Contract** — `TokenSupply.compact` imports `Warden.compact` and wires the guards +- **Witness + private state** — TypeScript implementations for `localSecret()` and `randomness()` +- **Wallet** — Account setup and node connection via the Midnight Wallet SDK +- **API** — Layer that wraps deployment, commit, mint, burn, and state observation +- **CLI** — Interactive menu to drive the dApp end-to-end on a local devnet or Midnight Preview testnet + +See the [e2e README](e2e/README.md) for full setup and usage instructions. + +## Future work + +There are a few ideas on how the project can be improved. First, the inclusion of an "owner" that is the only one authorized to perform operations like initializing the contract and verifying. The current implementation allows anyone who can commit to call the other circuits, which is consistent with how multisignature scripts work in other blockchains. An optional owner role could be added for use cases that need a designated administrator. + +Another line of work is evaluating alternative implementations to guarantee the best performance. -A complete [end-to-end example](e2e/) demonstrating Warden in action — a token -supply contract that uses Warden to gate mint and burn operations by an -authorization policy (any, all, atLeast, time locks, or combinations). -The flow: **deploy** → participants **commit** → **mint/burn** guarded by -`verify()`. See the [e2e README](e2e/README.md) for setup and usage. From 866417c4668dddd9a8e52f06f0f3dad6aad3f4ff Mon Sep 17 00:00:00 2001 From: Lola Aimar Date: Mon, 29 Jun 2026 15:03:22 -0300 Subject: [PATCH 2/4] add license --- LICENSE | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4eb8325 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 TxPipe + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file From 871870a08c5dadae8cc0793d8ef32c33e086ce0a Mon Sep 17 00:00:00 2001 From: Lola Aimar Date: Tue, 30 Jun 2026 14:43:22 -0300 Subject: [PATCH 3/4] improves design doc and runs format --- README.md | 13 ++++++------ docs/design.md | 54 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 5082bc6..38b7422 100644 --- a/README.md +++ b/README.md @@ -37,11 +37,11 @@ There are two other core concerns as well: making the solution simple for the en The tool reads a JSON file describing the authorization policy (commitment hashes, time locks, composite conditions) and generates a Compact module called **Warden** with three exported circuits. See [docs/schema.md](docs/schema.md) for the complete schema design documentation, and [docs/design.md](docs/design.md) for a description of the generated Compact code and usage instructions. -| Circuit | Purpose | -|---------|---------| -| `init()` | Initializes the on-ledger maps that encode the policy | +| Circuit | Purpose | +| ---------- | ----------------------------------------------------------------------------- | +| `init()` | Initializes the on-ledger maps that encode the policy | | `commit()` | A user registers their commitment (proves knowledge of a secret + randomness) | -| `verify()` | Checks that the set of committed commitments satisfies the policy conditions | +| `verify()` | Checks that the set of committed commitments satisfies the policy conditions | Any contract that needs access control **imports** `Warden`, calls `init()` in its constructor, exposes `commit()` to participants, and calls `verify()` before any protected operation: @@ -76,6 +76,7 @@ pnpm make-commitment -o my-pair.json ``` Optional parameters: + - `-s, --seed ` — 64-character hex seed (deterministic secret generation) - `-o, --output ` — Path to write the result as JSON (if omitted, prints to stdout) @@ -151,6 +152,7 @@ Equivalently, using the global CLI: `warden-tool generate-code -i script.json -o This creates `generated/Warden.compact` — a Compact module with the authorization logic baked in. Optional parameters: + - `-o, --output ` — Output directory (default: `generated/`) - `-t, --test` — Include import/export boilerplate for unit testing @@ -244,6 +246,7 @@ The top-level contract is **TokenSupply**, which exposes `mint` and `burn` circu The **initial policy** is an `all` of four commitments — all four must be registered before any mint/burn is allowed. The example includes: + - **Contract** — `TokenSupply.compact` imports `Warden.compact` and wires the guards - **Witness + private state** — TypeScript implementations for `localSecret()` and `randomness()` - **Wallet** — Account setup and node connection via the Midnight Wallet SDK @@ -257,5 +260,3 @@ See the [e2e README](e2e/README.md) for full setup and usage instructions. There are a few ideas on how the project can be improved. First, the inclusion of an "owner" that is the only one authorized to perform operations like initializing the contract and verifying. The current implementation allows anyone who can commit to call the other circuits, which is consistent with how multisignature scripts work in other blockchains. An optional owner role could be added for use cases that need a designated administrator. Another line of work is evaluating alternative implementations to guarantee the best performance. - - diff --git a/docs/design.md b/docs/design.md index d1c33bd..f1d0e31 100644 --- a/docs/design.md +++ b/docs/design.md @@ -23,14 +23,15 @@ A field declared with `ledger` signifies that it is a part of the contract's pub #### `ledger idsToCommitments` -This ledger field stores the actual commitments that users have submitted. The keys are `16-byte` hashes that uniquely identify each composite node within the input script. The values are `sets of 32-byte` commitment hashes: one per user who has committed to that specific composite node. +This ledger field stores the actual commitments that users have submitted. The keys are `8-byte` identifiers that uniquely represent each composite node's position within the script tree. The values are `sets of 32-byte` commitment hashes: one per user who has committed to that specific composite node. -This structure directly supports nested scripts: each composite script (any, all, atLeast) gets its own entry in the map so that its set of commitments can be evaluated independently. The field starts empty and is populated incrementally by the commit circuit as users submit their commitments. +Each key is derived by padding the node's dot-separated tree path (e.g. `"0"`, `"0_1"`) to 8 bytes. This structure directly supports nested scripts: each composite node (any, all, atLeast) gets its own entry so its set of commitments can be evaluated independently. The field starts empty and is populated incrementally by the `commit` circuit as users submit their commitments. #### `ledger commitmentsToIds` -This ledger field encodes the static authorization rules derived from the input script. The keys are the `32-byte` hashes of every commitment that the contract authorizes (each `cmt` field in the input JSON). The values are the `sets of 16-byte` script identifiers where each commitment is expected to appear. -A single commitment hash may map to multiple set IDs if it appears as a child of more than one composite node within the script tree. +This ledger field encodes the static authorization rules derived from the input script. The keys are `32-byte` hashes of every commitment that the contract authorizes (each `cmt` field in the input JSON). The values are `sets of 8-byte` script-tree position identifiers (padded paths) where each commitment is expected to appear. + +A single commitment hash may map to multiple path identifiers if it appears as a child of more than one composite node within the script tree. This field is populated once during `init` and remains read-only thereafter. ### Witness @@ -40,22 +41,32 @@ The witness function `localSecret` fetches a secret `Bytes<32>` value from the w #### `init circuit` -This circuit initializes the ledger for the module. For each commitment hash, `commitmentsToIds` is pre-populated with hardcoded `insert` calls, mapping each hash to the set of IDs where that commitment is expected. The ID values match the keys in `idsToCommitments`. Meanwhile, `idsToCommitments` is initialized empty. +This circuit initializes the ledger for the module. For each commitment hash, `commitmentsToIds` is pre-populated with hardcoded `insert` calls, mapping each hash to the set of IDs where that commitment is expected. The ID values match the keys in `idsToCommitments`. Meanwhile, `idsToCommitments` is initialized with the default empty set for each path ID. #### `commit circuit` -This circuit adds a users commitment to the contract's ledger. This aims to mimic the behavior of a multisignature script in which each wallet adds their signature to a transaction. The circuit checks a commitment against the ledger's `commitmentsToIds`: if it belongs, it adds the commitment to the respective sets in `idsToCommitments`, if it doesn't, the commitment wasn't authorized and nothing is added. +This circuit adds a user's commitment to the contract's ledger. It mimics the behavior of a multisignature script in which each wallet adds their signature to a transaction. The circuit performs these checks: + +1. **Contract initialized**: asserts the `init` circuit has been called (`commitmentsToIds` is not empty). +2. **Authorization**: computes the commitment from the user's secret and randomness via `getCommitment`, then asserts it exists in `commitmentsToIds`. +3. **Known path**: asserts the commitment maps to a non-empty set of path identifiers. +4. **No double-commit**: asserts the commitment hasn't already been registered in the target `idsToCommitments` entry. + +If all checks pass, the commitment is inserted into the corresponding set in `idsToCommitments`. If any check fails, the circuit aborts so nothing is added. ##### `getCommitment circuit` -This circuit generates a commitment based on a given secret and randomness. It is used by the `commit` circuit to create the commitment that will be stored on the ledger. +This pure circuit generates a commitment from a given secret and randomness using `persistentCommit>`. It is called by `commit` to produce the on-chain commitment hash. #### `verify circuit` -This circuit verifies that all of the expected conditions are met. These conditions include: +This circuit verifies that all expected conditions are met: -- the commitments present in the ledger satisfy the predetermined clauses, -- and the block corresponds with the desired height, if any. +- **Commitment clauses**:checks the commitments exist in the corresponding `idsToCommitments` set via `member()`. +- **Time-lock clauses**: compares the current block height against the `after`/`before` thresholds using `blockTimeGte()` / `blockTimeLt()`. +- **Composite clauses**: combines child results with `&&` (all), `||` (any), or a summed ternary counter against the required threshold (`atLeast`). + +If the root condition passes, the circuit resets all `idsToCommitments` entries to their default (empty) state via `resetToDefault()`. This ensures the same verified state cannot be replayed. ## Timelock-only contract @@ -78,3 +89,26 @@ import "/Warden"; ``` You can use the `prefix` keyword to have the circuits accessible as , e.g. `import "/Warden" prefix Warden_;` means "Warden_init", "Warden_commit" and "Warden_verify" are in scope. + +## Test Mode + +Passing the `-t` (or `--test`) flag to `generate-code` produces the same Compact module but with additional exports that make the contract testable from an external test harness. + +Two changes are made: + +1. **Ledger visibility**: both `commitmentsToIds` and `idsToCommitments` are declared with the `export` modifier so they can be read and asserted in tests. +2. **Re-export block**: the module self-imports and re-exports all circuits and ledger fields: + +```compact +import Warden; + +export { getCommitment, init, commit, verify, idsToCommitments, commitmentsToIds }; +``` + +When there are no commitment clauses (timelock-only), the re-export is limited to: + +```compact +import Warden; + +export { getCommitment, verify }; +``` From 5678186127277cecf1ce26370b7eae5a08816cef Mon Sep 17 00:00:00 2001 From: Lola Aimar Date: Tue, 30 Jun 2026 17:49:51 -0300 Subject: [PATCH 4/4] fix inaccuracy about time constraint checks --- docs/design.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/design.md b/docs/design.md index f1d0e31..ef24e90 100644 --- a/docs/design.md +++ b/docs/design.md @@ -63,7 +63,7 @@ This pure circuit generates a commitment from a given secret and randomness usin This circuit verifies that all expected conditions are met: - **Commitment clauses**:checks the commitments exist in the corresponding `idsToCommitments` set via `member()`. -- **Time-lock clauses**: compares the current block height against the `after`/`before` thresholds using `blockTimeGte()` / `blockTimeLt()`. +- **Time-lock clauses**: compares the current block's time against the `after`/`before` thresholds using `blockTimeGte()` / `blockTimeLt()`. - **Composite clauses**: combines child results with `&&` (all), `||` (any), or a summed ternary counter against the required threshold (`atLeast`). If the root condition passes, the circuit resets all `idsToCommitments` entries to their default (empty) state via `resetToDefault()`. This ensures the same verified state cannot be replayed. @@ -78,7 +78,7 @@ When the input script contains only time-lock clauses (`after` / `before`) with #### `verify` circuit -This circuit checks only the time-lock conditions of the input script. It evaluates the clauses for the desired block height. On success, the circuit does not need to reset any state. +This circuit checks only the time-lock conditions of the input script. It evaluates the clauses for the desired block time. On success, the circuit does not need to reset any state. ## Usage