Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2ef8056
chore: increment crate versions to v0.16.0
bobbinth May 22, 2026
85caa18
feat: implement `Clone` for `TransactionContextBuilder` (#2979)
PhilippGackstatter May 27, 2026
b6f12bb
feat(agglayer): reject duplicate GER insertions (#2983)
Fumuran May 27, 2026
e1a066e
docs: remove MASM format & style guide (#2994)
mmagician May 28, 2026
73243a3
docs: discourage multiple attachments with the same scheme (#2992)
mmagician May 28, 2026
84240a0
Make `BURN` note ID unique (#2987)
Fumuran May 28, 2026
a1be171
Extend execution-error assertion macros with patter and any arms (#2897)
marijamijailovic May 28, 2026
75df7c5
fix: Reject circular note dependencies at transaction, batch and bloc…
PhilippGackstatter May 28, 2026
9b9a2e3
feat: Rename `AccountStorageDelta` to `AccountStoragePatch` (#3004)
PhilippGackstatter May 28, 2026
d9390a5
refactor: move `TransactionVerifier` into `miden-protocol` crate (#3001)
mmagician May 29, 2026
570928b
chore(reviewers): don't block on documented, intentional incompletene…
mmagician May 29, 2026
24dcb9b
fix(agglayer): enforce NoteType::Public for B2AGG bridge-out notes (#…
partylikeits1983 May 29, 2026
5f43e07
feat: AccountTreeBackendReader and NullifierTreeBackendReader traits …
sergerad May 29, 2026
956f4f2
docs(overview): clarify transaction definition and execution vs provi…
BrianSeong99 Jun 1, 2026
d306ee6
fix(hooks): diff the integration branch / PR across review + changelo…
mmagician Jun 1, 2026
8aba66f
docs(claude): require new commits (not force-push) when addressing PR…
mmagician Jun 1, 2026
2b5c9c4
feat: batch kernel skeleton (#2904)
mmagician Jun 3, 2026
269590f
test(active-note): pin get_metadata to a single output word (#3031) (…
partylikeits1983 Jun 3, 2026
1e7d0b0
refactor: unify account/nullifier SMT backends into shared `SmtBacken…
mmagician Jun 3, 2026
2c42324
feat(batch-prover): verify batch kernel execution proofs (#2998)
mmagician Jun 3, 2026
0b662ad
refactor: change `TransferPolicy` from `enum` to `struct` (#2974)
onurinanc Jun 3, 2026
2c4afd5
feat: Introduce `AccountPatch` and `AccountVaultPatch` (#3010)
PhilippGackstatter Jun 3, 2026
2537a48
Add `active_account::has_storage_slot` (#3037)
onurinanc Jun 3, 2026
dc272b0
refactor: cycle count golfing (#3041)
partylikeits1983 Jun 4, 2026
2c6ec9d
feat(standards): add min burn amount policy (#3021)
onurinanc Jun 5, 2026
7d09abc
refactor: Add indirection for send/receive policies (#3047)
onurinanc Jun 5, 2026
215ddab
feat: hash `AssetVaultKey` before insertion into asset vault SMT (#2912)
partylikeits1983 Jun 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
- **Worktree visibility:** Always tell the user which worktree (full path) you will work in as part of the plan. When finished, state where the changes live (worktree path and branch name).
- **Commit authorship:** Always commit as Claude, not as the user. Use: `git -c user.name="Claude (Opus)" -c user.email="noreply@anthropic.com" -c commit.gpgsign=false commit -m "message"`
- **Commit frequency:** Always commit at the end of each task. Avoid single commits that span multiple unrelated changes.
- **Responding to PR review:** When addressing review feedback on a pushed PR, add new commits on top of the branch (e.g. `fix: address review comments`). NEVER amend, squash, or otherwise rewrite already-pushed commits to incorporate review changes, and NEVER force-push the branch to do so - this destroys the diff reviewers rely on to see what changed since their review. The only time a force-push is acceptable is when the branch must be rebased onto an updated base (or a PR lower in a stack changed); that is a base update, not a review response, and should be called out explicitly.

## Output Formatting

Expand Down
9 changes: 5 additions & 4 deletions .claude/agents/changelog-manager.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,20 @@ You receive a prompt like: `Check changelog for PR #N (URL)`
```
gh pr view <N> --json labels --jq '.labels[].name'
```
2. Check if CHANGELOG.md is already modified in the diff:
2. Check if the PR already modifies CHANGELOG.md:
```
git diff origin/next...HEAD -- CHANGELOG.md
gh pr diff <N> --name-only | grep -qx CHANGELOG.md
```

If either condition is met, output `SKIP: already handled` and stop.

## Step 2: Analyze the Diff

Run:
Classify the PR's own diff (not a local `git diff`):
```
git diff origin/next...HEAD -- ':(exclude)CHANGELOG.md'
gh pr diff <N>
```
Disregard changes to CHANGELOG.md itself.

## Step 3: Classify

Expand Down
22 changes: 19 additions & 3 deletions .claude/agents/code-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,16 @@ You are an experienced Staff Engineer conducting a thorough code review with fre

## Step 1: Gather Context

Run `git diff @{upstream}...HEAD`. If no upstream is set, resolve the default
branch with `gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name'`
and run `git diff origin/<branch>...HEAD`.
Diff against the integration branch (the remote's default branch), not the
branch's own upstream:

```
git diff "$(git symbolic-ref --short refs/remotes/origin/HEAD)...HEAD"
```

If `origin/HEAD` is not set, resolve the default branch with
`gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name'` and run
`git diff origin/<branch>...HEAD`.

For every file in the diff, read the **full file** - not just the changed lines. Bugs hide in how new code interacts with existing code.

Expand Down Expand Up @@ -74,6 +81,15 @@ Categorize every finding:

**Nit** - Worth improving (naming, style, minor readability, optional optimization)

### Documented, intentional incompleteness

A change may deliberately ship incomplete behavior as one stage of a larger, planned effort (a skeleton, placeholder, or stub). When the incompleteness is **all** of:
- explicitly documented in the code (a doc comment or module note stating what is not yet implemented),
- clearly scoped and warned about (the docs say what not to rely on and reference the follow-up work), and
- not wired into any path that depends on the missing behavior being correct,

then the incompleteness itself is NOT a Critical or Important finding - the change is complete for what it claims to be. Treat it as a Nit at most, or acknowledge the clear documentation under "What's Done Well". Escalate only when the documentation is missing, inaccurate, or misleading, or when the incomplete code is actually relied upon as if it were complete. Distinguish "incomplete but correct, documented, and self-contained" from "broken or silently incomplete".

## Output Format

```
Expand Down
22 changes: 19 additions & 3 deletions .claude/agents/security-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,16 @@ You are a hostile reviewer. Your job is to break this code before an attacker do

## Step 1: Gather the Changes

Run `git diff @{upstream}...HEAD`. If no upstream is set, resolve the default
branch with `gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name'`
and run `git diff origin/<branch>...HEAD`.
Diff against the integration branch (the remote's default branch), not the
branch's own upstream:

```
git diff "$(git symbolic-ref --short refs/remotes/origin/HEAD)...HEAD"
```

If `origin/HEAD` is not set, resolve the default branch with
`gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name'` and run
`git diff origin/<branch>...HEAD`.

For every file in the diff, read the **full file**. Vulnerabilities hide in how new code interacts with existing code, not just in the diff itself.

Expand Down Expand Up @@ -79,6 +86,15 @@ After both personas report:

**NOTE** - Minor improvement opportunity or fragile assumption worth documenting.

### Documented, intentional incompleteness

Some changes deliberately ship a security-relevant placeholder as one stage of planned work (e.g., a verifier that does not yet bind certain data, a check that is stubbed). When such a limitation is **all** of:
- explicitly documented in the code (a doc comment or module note stating exactly what is not yet enforced),
- accompanied by a clear warning against misuse (e.g., "must not be relied on at a trust boundary") and a reference to the follow-up that will close it, and
- not actually reachable from a trust boundary in this change (no caller relies on the missing guarantee),

then classify it as a NOTE, not CRITICAL or WARNING. Surfacing it keeps it visible without blocking a correctly-staged change. The finding is the ABSENCE or INADEQUACY of that documentation, or the incomplete code being wired into a real trust boundary - not the incompleteness itself. If the limitation is undocumented, the warning is missing or misleading, or a caller already depends on the unenforced guarantee, keep the CRITICAL/WARNING severity.

## Output Format

```
Expand Down
6 changes: 4 additions & 2 deletions .claude/hooks/pre_push_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,11 @@ def main() -> None:


def _diff_base() -> str:
"""Prefer the configured upstream; fall back to origin/next."""
"""Return the integration branch to review against — the remote's
default branch (e.g. `origin/next`).
"""
result = subprocess.run(
["git", "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
["git", "symbolic-ref", "--short", "refs/remotes/origin/HEAD"],
capture_output=True,
text=True,
)
Expand Down
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
# Changelog

## v0.16.0 (TBD)

### Changes
- Added a skeleton batch kernel ([#1122](https://github.com/0xMiden/protocol/issues/1122)) wired through `LocalBatchProver::prove` and attached to `ProvenBatch` as an `ExecutionProof`. It does not yet perform any verification.

- [BREAKING] Renamed `AccountStorageDelta` to `AccountStoragePatch` ([#3002](https://github.com/0xMiden/protocol/pull/3002)).
- [BREAKING] Replaced the per-tree account and nullifier backend traits with shared `SmtBackend` and `SmtBackendReader` traits, split into read-only and read-write capabilities, enabling read-only `LargeSmt`-backed tree views via `reader()` ([#2755](https://github.com/0xMiden/protocol/pull/2755), [#3009](https://github.com/0xMiden/protocol/pull/3009)).
- Added `active_note::is_public` and `active_note::is_private` MASM procedures for checking whether the active note is public or private ([#2988](https://github.com/0xMiden/protocol/pull/2988)).
- Added a `min_burn_amount` fungible faucet burn policy that rejects burns below a configurable, owner-gated minimum burn amount ([#3021](https://github.com/0xMiden/protocol/pull/3021)).
- Added the `active_account::has_storage_slot` MASM procedure for checking whether a storage slot exists on the active account without panicking ([#3037](https://github.com/0xMiden/protocol/pull/3037)).
- Introduced `AccountPatch` and `AccountVaultPatch` ([#3010](https://github.com/0xMiden/protocol/pull/3010)).
- Clarified the transaction definition and the distinction between execution and proving on the architecture overview page ([#3015](https://github.com/0xMiden/protocol/pull/3015)).
- [BREAKING] Refactored `TransferPolicy`, `MintPolicyConfig`, and `BurnPolicyConfig` from enums into structs ([#2974](https://github.com/0xMiden/protocol/pull/2974)).
- Added `AccountComponent::has_procedure(root)` helper ([#2974](https://github.com/0xMiden/protocol/pull/2974)).
- Optimized protocol MASM stack-cleaning sequences, saving 1 cycle per occurrence across 9 single-element-extraction procedures ([#3041](https://github.com/0xMiden/protocol/pull/3041)).
- [BREAKING] Refactored `TokenPolicyManager` by adding `invoke_send_policy` / `invoke_receive_policy` wrappers (stored in the protocol reserved asset callback slots) that read the active policy root from the new `active_send_policy_proc_root` / `active_receive_policy_proc_root` storage slots ([#3047](https://github.com/0xMiden/protocol/pull/3047)).

### Fixes
- Fixed `update_ger` to explicitly reject duplicate GER insertions with `ERR_GER_ALREADY_REGISTERED` instead of silently accepting them ([#2983](https://github.com/0xMiden/protocol/pull/2983)).
- AggLayer `bridge_out` now rejects B2AGG notes whose `NoteType` is not `Public`, preventing a recipient-identical private note from desyncing the Local Exit Tree from AggLayer's off-chain mirror ([#2988](https://github.com/0xMiden/protocol/pull/2988)).
- Fixed `pausable::assert_not_paused` to guard its storage read with `active_account::has_storage_slot`, making it a no-op on accounts without the `Pausable` component instead of panicking on the missing `is_paused` slot ([#3047](https://github.com/0xMiden/protocol/pull/3047)).

## v0.15.1 (TBD)

### Changes

- Reject batches and blocks where an unauthenticated note is consumed before it is created to prevent circular note dependencies ([#2993](https://github.com/0xMiden/protocol/pull/2993)).

## v0.15.0 (2026-05-22)

### Features
Expand Down Expand Up @@ -59,6 +87,7 @@
- Added standardized `NetworkAccountNoteAllowlist` slot for detecting network accounts ([#2883](https://github.com/0xMiden/protocol/pull/2883)).
- [BREAKING] Merged `BasicFungibleFaucet` and `NetworkFungibleFaucet` ([#2890](https://github.com/0xMiden/protocol/pull/2890)).
- [BREAKING] Renamed `NoteMetadata` to `PartialNoteMetadata` and renamed `NoteMetadataHeader` to `NoteMetadata` ([#2887](https://github.com/0xMiden/protocol/pull/2887)).
- [BREAKING] Hashed `AssetVaultKey` before insertion into the asset vault SMT ([#2912](https://github.com/0xMiden/protocol/pull/2912)).
- [BREAKING] Renamed account ID version 0 to version 1 and made encoded version 0 invalid ([#2842](https://github.com/0xMiden/protocol/issues/2842)).
- [BREAKING] Changed note metadata version 1 to encode as `1`, leaving encoded version `0` invalid.
- [BREAKING] Added `NetworkAccount` wrapper for convenient network account identification ([#2915](https://github.com/0xMiden/protocol/pull/2915)).
Expand Down
20 changes: 11 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ homepage = "https://miden.xyz"
license = "MIT"
repository = "https://github.com/0xMiden/protocol"
rust-version = "1.90"
version = "0.15.0"
version = "0.16.0"

[profile.release]
codegen-units = 1
Expand All @@ -36,13 +36,13 @@ lto = true

[workspace.dependencies]
# Workspace crates
miden-agglayer = { default-features = false, path = "crates/miden-agglayer", version = "0.15" }
miden-block-prover = { default-features = false, path = "crates/miden-block-prover", version = "0.15" }
miden-protocol = { default-features = false, path = "crates/miden-protocol", version = "0.15" }
miden-standards = { default-features = false, path = "crates/miden-standards", version = "0.15" }
miden-testing = { default-features = false, path = "crates/miden-testing", version = "0.15" }
miden-tx = { default-features = false, path = "crates/miden-tx", version = "0.15" }
miden-tx-batch-prover = { default-features = false, path = "crates/miden-tx-batch-prover", version = "0.15" }
miden-agglayer = { default-features = false, path = "crates/miden-agglayer", version = "0.16" }
miden-block-prover = { default-features = false, path = "crates/miden-block-prover", version = "0.16" }
miden-protocol = { default-features = false, path = "crates/miden-protocol", version = "0.16" }
miden-standards = { default-features = false, path = "crates/miden-standards", version = "0.16" }
miden-testing = { default-features = false, path = "crates/miden-testing", version = "0.16" }
miden-tx = { default-features = false, path = "crates/miden-tx", version = "0.16" }
miden-tx-batch-prover = { default-features = false, path = "crates/miden-tx-batch-prover", version = "0.16" }

# Miden dependencies
miden-assembly = { default-features = false, version = "0.23" }
Expand Down
15 changes: 11 additions & 4 deletions crates/miden-agglayer/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,19 +113,24 @@ on Miden. The bridge consumes these notes:
1. Asserts the note sender is the designated GER manager.
2. Computes `KEY = poseidon2::merge(GER_LOWER, GER_UPPER)`.
3. Stores `KEY -> [1, 0, 0, 0]` in the `ger_map`, marking the GER as known.
4. Reverts if the GER was already present in the map (duplicate insertions are rejected).

Subsequent CLAIM notes reference a GER that must be present in this map for the claim
to be valid.

> **Note on Solidity divergence:** The Solidity `GlobalExitRootManager` contract treats a
> duplicate GER insertion as an idempotent no-op. Miden intentionally diverges: a duplicate
> `UPDATE_GER` note causes the consuming transaction to revert. Because `UPDATE_GER` is a
> network note (consumed by the note nullifier mechanism), a duplicate would become
> permanently unconsumable rather than silently accepted. Rejecting duplicates makes the
> failure explicit and prevents the GER manager from accidentally creating unconsumed notes.
TODO: GERs cannot be removed once inserted
([#2702](https://github.com/0xMiden/protocol/issues/2702)).

TODO: No hash chain tracks GER insertions for proof generation
([#2707](https://github.com/0xMiden/protocol/issues/2707)).

TODO: Duplicate GER insertions are silently accepted
([#2708](https://github.com/0xMiden/protocol/issues/2708)).

### 2.4 Faucet Registration

![Faucet registration flow](diagrams/faucet-registration.png)
Expand Down Expand Up @@ -237,12 +242,14 @@ Asserts the note sender matches the bridge admin stored in
| **Inputs** | `[GER_LOWER(4), GER_UPPER(4), pad(8)]` |
| **Outputs** | `[pad(16)]` |
| **Context** | Consuming an `UPDATE_GER` note on the bridge account |
| **Panics** | Note sender is not the GER manager |
| **Panics** | Note sender is not the GER manager; GER has already been registered in storage |

Asserts the note sender matches the GER manager stored in
`agglayer::bridge::ger_manager_account_id`, then computes
`KEY = poseidon2::merge(GER_LOWER, GER_UPPER)` and stores
`KEY -> [1, 0, 0, 0]` in the `ger_map` map slot. This marks the GER as "known".
Duplicate insertions (same GER value) are explicitly rejected: if the key already exists
in the map the procedure panics with `ERR_GER_ALREADY_REGISTERED`.

#### `bridge_in::claim`

Expand Down
8 changes: 6 additions & 2 deletions crates/miden-agglayer/asm/agglayer/bridge/bridge_config.masm
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use miden::core::crypto::hashes::poseidon2
use miden::core::word
use miden::protocol::account_id
use miden::protocol::active_account
use miden::protocol::active_note
Expand All @@ -9,6 +10,7 @@ use agglayer::common::utils
# =================================================================================================

const ERR_GER_NOT_FOUND = "GER not found in storage"
const ERR_GER_ALREADY_REGISTERED = "GER is already registered in storage"
const ERR_FAUCET_NOT_REGISTERED = "faucet is not registered in the bridge's faucet registry"
const ERR_TOKEN_NOT_REGISTERED = "(origin token address, origin network) pair is not registered in the bridge's token registry"
const ERR_SENDER_NOT_BRIDGE_ADMIN = "note sender is not the bridge admin"
Expand Down Expand Up @@ -60,6 +62,7 @@ const FAUCET_METADATA_SUBKEY_HASH_HI = 3 # METADATA_HASH_HI[4]
#!
#! Panics if:
#! - the note sender is not the global exit root manager.
#! - the GER has already been registered in storage.
#!
#! Invocation: call
pub proc update_ger
Expand All @@ -83,8 +86,9 @@ pub proc update_ger

exec.native_account::set_map_item
# => [OLD_VALUE, pad(12)]

dropw

# assert OLD_VALUE is EMPTY_WORD, i.e. the GER was not previously registered
exec.word::eqz assert.err=ERR_GER_ALREADY_REGISTERED
# => [pad(16)]
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ pub proc unlock_and_send
# Remove the asset from the bridge's vault. Panics if the vault does not contain enough of
# the asset, which is the desired failure mode for an invalid / double-spent claim.
exec.native_account::remove_asset
# => [REMAINING_ASSET_VALUE]
# => [FINAL_ASSET_VALUE]

dropw
# => []
Expand Down
Loading
Loading