This repository builds a Merkle verifier and its local client.
The canonical Merkle rules are documented in
docs/strk-merkle-tree-parity.md.
Everything in this repo is expected to match StandardMerkleTree from that
package.
This contract keeps only one Merkle root on-chain:
activeRootis the latest valid stateownermanages the list of root updatersgetRootUpdaters()returns the currently allowed updater addressessetActiveRoot(newRoot)replaces the previous rootcontains(...)verifies a raw hash for trees built with["felt252"]containsLeafHash(...)verifies a canonical leaf hash directlycontainsMany(...)verifies only againstactiveRoot
This means:
- when you add hashes, rebuild the full tree off-chain and call
setActiveRoot - when you revoke hashes, rebuild the full tree without those hashes and call
setActiveRoot - old proofs stop working as soon as a new root replaces the previous one
- on-chain storage does not grow with the number of updates; only the current root is stored
Simple example:
- Build a tree from
H1, H2and set that root. - Later rebuild from
H1, H2, H3and set the new root. - Later rebuild from
H1, H3to revokeH2and set the new root.
Only the last root is valid.
Root rotation is not restricted to a single key. The contract keeps an
owner-managed allowlist of updater addresses. Any allowed updater may publish
the next activeRoot, and the owner can add or remove updater addresses as
operational keys change.
For hash-only expiration sets, keep insertion and expiry data off-chain and build the tree from raw hashes:
{
"leafEncoding": ["felt252"],
"values": [["0xHASH_1"], ["0xHASH_2"]]
}Then clients can verify directly by raw hash:
contains(hash, proof) -> bool
Use containsLeafHash(leafHash, proof) only when the caller already has the
canonical Merkle leaf hash.
This repository now uses conventional-commit semantic versioning on the main release branch.
- pushes to
mastertrigger the release workflow - this checkout also accepts
main, because the current default branch ismain semantic-releasecomputes the next SemVer version from conventional commitsCHANGELOG.mdis updated automatically- a GitHub release and Git tag are created automatically
The Solidity build also embeds the current project version:
scripts/generate-build-info.mjswritessrc/generated/BuildInfo.sol- Forge targets regenerate that file before compiling
MerkleRootRegistry.version()returns the embedded version string
Clients can call:
version() -> string
to read which build version of the contract they are talking to.
make setup
npm ciGenerate canonical vectors:
make generate-vectorsRun the Node client tests:
make test-clientRun the Solidity tests:
make test-solidityThe registry tests include root rotation checks, which prove that proofs for an
old root fail after activeRoot is updated.
Regenerate vectors and prove Solidity parity from the generated fixtures:
make test-parityRun the local Anvil smoke flow:
make test-smokeRun the full local test surface:
make test-allmake test is an alias for make test-all, and the GitHub Actions workflow uses
that same full target.
Regenerate the Solidity build version file only:
make generate-build-infoRun the full end-to-end flow against the live SwissLedger chain (id 110):
./scripts/on-chain-smoke # deploy fresh contract
./scripts/on-chain-smoke 0x20f89...1888b # reuse existing contractCompiles for pre-Shanghai EVM, deploys MerkleRootRegistry, sets a Merkle root,
and verifies both valid and invalid proofs on-chain. Every operation is timed.
Typical timings (ledger.swiss, Jun 2026):
| step | ms | status |
|---|---|---|
| build (istanbul) | 165 | ok |
| proxy start | 674 | ok |
| tree + proof | 451 | ok |
| setActiveRoot | 6,550 | ok |
| verify valid proof | 287 | pass |
| verify invalid proof | 300 | pass |
Swissledger Merkle is Copyright (C) 2026 PlanB Foundation
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.
Includes code that is (C) 2022-2023 zOS Global Limited and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.