-
Notifications
You must be signed in to change notification settings - Fork 52
[Certora] OfferTree Soundness #816
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
167 commits
Select commit
Hold shift + click to select a range
21739ee
added rules inspired form URD
bhargavbh 030c175
tuned
bhargavbh 8e2e5ee
merged main
bhargavbh 4898d72
take correctness passes; added some diagnostic tests
bhargavbh 215698b
removed the diagnostic rules
bhargavbh b62eae7
cleaned up comments
bhargavbh b9d943e
add completeness rule
bhargavbh 9b92e50
abstract properties instantiated to midnight
bhargavbh 0cd4f3e
cvl fmt
bhargavbh 07d95e3
fmt
bhargavbh bcc5198
renamed spec; minor changes in OfferTree definition
bhargavbh 38c1875
tuned
bhargavbh 8208a49
fmt
bhargavbh 80d2ad2
forge fmt
bhargavbh 7a3e019
Merge remote-tracking branch 'origin/main' into certora/offer-tree
bhargavbh 03ba8cd
resolved errors from merge
bhargavbh dd415d0
updated callback summary signatures
bhargavbh 24140f4
new rule in Ratification.spec: isRatified always calls isLeaf
bhargavbh cfb723f
rhashNode summarised similar to hashOffer
bhargavbh 8187c24
revamped soundness: Offer included in the node
bhargavbh 2d1eb87
tuned comment
bhargavbh d77d0b3
add checker
bhargavbh 939a570
add checker
bhargavbh d9f174e
summarise isRatify
bhargavbh fc758a3
trimmed down to core properties
bhargavbh 253e37e
removed completeness
bhargavbh 89c39d6
comment style adapted to URD
bhargavbh a43f97d
retained only testVerifyCertificate in Checker; split offer into stat…
bhargavbh 65e69d1
removed ratifier
bhargavbh 7091ed9
added justification rules for the hashLeaf trick
bhargavbh 9d0635b
removed assumptions from soundness rule
bhargavbh 4a3f121
tuned
bhargavbh 5dec124
tuned; retained market-level hashLeaf
bhargavbh 956a11f
cleanup ratification spec
bhargavbh ea94696
varibale renaming in create_certificate.py; tuned checker
bhargavbh 7d251ea
Merge origin/main into certora/offer-tree
bhargavbh fa88df5
added high-level verification approach in README
bhargavbh a1b4b8e
fmt
bhargavbh be2a563
acknowledge URD spec in README
bhargavbh 6aaa420
asserts replaced with raising exceptions
bhargavbh bec4e8f
tuned
bhargavbh c5fb0c9
Merge branch 'main' into certora/offer-tree
MathisGD bf8c505
Merge branch 'main' into certora/offer-tree
bhargavbh 6a5f897
updated to match latest midnight
bhargavbh 758bfba
Merge branch 'main' into certora/offer-tree
MathisGD b8c5a24
Improved multiplication overflow check
jhoenicke 86fd2d1
Remove investigation of all reverting mulDiv
jhoenicke a613bc8
Rewording of comments
jhoenicke 8da8e32
More comment refinement
jhoenicke 80eec16
blue fallback rolling first iteration
MathisGD 2fe1897
nit
MathisGD 58cf26e
lint
MathisGD f112dec
refactor: roll event
peyha 99aefcc
refactor: undo
peyha 7942e0d
doc
peyha 9ba4fdb
feat: auction
peyha fc42bf1
feat: auction end
peyha 059e09f
test: remove wrong test
peyha dff0e87
refactor: error name
peyha 9b4a0cb
fix: require end time
peyha fed6519
refactor: inline incentive()
peyha 01a91b3
test: lltv test
peyha eb46443
fix: remove increasing incentive requirement
peyha bf81ea9
doc
peyha 098e1e3
fix: incentive at start max value check
peyha 1a1f195
doc
peyha b5aa320
doc
peyha ffb0dca
Update src/periphery/blue-fallback-rolling/BlueFallbackRolling.sol
peyha a729282
Add files via upload
MathisGD 367e97b
newest file
MathisGD 9c8831f
Add files via upload
MathisGD cea79ab
Rename 2026-05-19-midnight-core-stermi-audit.pdf to 2026-05-19-stermi…
MathisGD 73755a4
refactor: interface folder
peyha 79a30b8
fix: decreasing incentive
peyha f89c785
test: coverage
peyha 1e52be2
test: midnight coverage
peyha 4b9d2e7
test: periphery coverage
peyha bac71f7
chore: fmt
peyha c02787f
feat: min rollable debt
peyha df175fe
fix: partial roll when below minrollableassets
peyha 9c1031a
feat: revert to assets=debt condition
peyha 642713a
doc: readme
peyha 7670ea8
Remove lltv check
claude 88344c5
Reword lltv warning comment
claude 82ff177
Move lltv warning to contract natspec
claude 38fa6f8
chore: lint
peyha 77188c9
doc: readme
peyha b8508cd
[rolling fallback] append LTV note to rounding comment
claude aa0d660
test: cover authorized fallback config setters
prd-carapulse[bot] d4e32d3
feat: allow authorized fallback config setters
prd-carapulse[bot] 8297909
test: require fallback config authorization first
prd-carapulse[bot] 6a129c4
fix: check fallback config authorization first
prd-carapulse[bot] 35143e5
feat: equality case for end start comp
peyha f65b91e
Revert "feat: equality case for end start comp"
peyha 7d727f8
doc: periphery readme
peyha 631c286
Update src/periphery/blue-fallback-rolling/BlueFallbackRolling.sol
peyha c614e85
refactor: inline interface
peyha db3f600
refactor: rolled assets too low
peyha b5d2d62
Add Certora spec: realizable bad debt cannot increase without price u…
claude 9a50fa9
Apply suggestion from @MathisGD
MathisGD e003af3
Fix RealizableBadDebt rules: health bridge for take/withdrawCollatera…
claude 9446625
Tighten mulDiv ghosts in liquidateRealizesBadDebt to close seize-path…
claude d0051d2
Prove mulDivUp >= mulDivDown in MulDiv.spec
claude bfe5d11
Merge take/withdrawCollateral into parametric rule; assert liquidate …
claude eed485c
Help prover on liquidateRealizesBadDebt: import LIF seize bound + spl…
claude 7f77776
Isolate hard liquidate realizable-bad-debt rule with getter-form seiz…
claude 024a893
Split RealizableBadDebtLiquidate by liquidate input branch; lighten s…
claude bcdccd1
Bound realizable bad debt after liquidation by 2 (mulDivUp rounding d…
claude aad45e1
Bound realizable bad debt after liquidation by 3; enable case-splitti…
claude c3f8d3a
Pin collateral invariant for full bitmap loop in RealizableBadDebtLiq…
claude a1f624f
Use deterministic mulDiv ghost in RealizableBadDebtLiquidate to kill …
claude 8e0a58f
Prove double-mulDivUp subadditivity lemma; apply in RealizableBadDebt…
claude 94b10cc
Materialize seize-bound term in RealizableBadDebtLiquidate repaid bra…
claude 28406dd
Restrict RealizableBadDebtLiquidate repaid branch to single collatera…
claude 9c246c1
Try -depth 0 (match passing peers) + restore 2 collaterals on Realiza…
claude 1e21778
Try cvc5-heavy portfolio + collateralIndex/single-collateral pins on …
claude aca534c
Drop forall quantifiers from RealizableBadDebtLiquidate repaid branch…
claude b105ad9
Spec improvements
jochencertora 58f86a2
Fix formatting
jochencertora 085246a
Apply suggestions from code review
jhoenicke bb98ee9
Some more review issues:
jochencertora 5c05e7f
Fix summary
jochencertora c2d82fd
remove comment
jhoenicke b3c76af
nit
MathisGD 5754a84
nit
MathisGD 1940aeb
Remove some require
jochencertora 7360f87
Remove the rules no longer needed
jochencertora dc3a82a
Apply suggestions from code review
MathisGD 6eba1a5
Apply suggestions from code review
MathisGD 88ec1d4
Inline axioms into rules and reference their MulDiv lemmas
claude 4a86303
Prove liquidate does not increase another position's realizable bad debt
claude c60091c
Fix inlined axiomAddUpUp antecedent to match original
claude f10a7bd
Update certora/confs/RealizableBadDebt.conf
MathisGD 8300f7c
Update certora/confs/RealizableBadDebtLiquidate.conf
MathisGD aeac0d0
Use general forall form for inlined axiom requires
claude 18f8179
Pin AddUpUp/InverseUpDown/UpZero axioms to call args (forall form tim…
claude 8ea0155
Test: make ERC20NoReturn.approve genuinely return nothing
claude 7997fc5
Apply suggestion from @MathisGD
MathisGD 314b913
[tests] BlueFallbackRolling: safe approve on loan token
claude 86551f6
Port NONDET summaries for tickToPrice and mulDiv to ReentrancyView spec
claude aef0c2a
Separate MulDiv axiom spec file
jochencertora 42ad512
fix assertion
jochencertora 29b71a3
Make ghost persistent (they never change)
jochencertora e113fc0
[blue fallback rolling] require non-decreasing incentive
claude 5277f60
[blue fallback rolling] remove redundant incentive check
claude 56f27bf
[blue fallback rolling] add configId to Roll event
claude 7f9a1e1
Apply suggestion from @peyha
peyha 34b1ce0
ci: pin foundry-toolchain to v1.9.1
prd-carapulse[bot] cbdbe1d
Merge branch 'main' into certora/offer-tree
bhargavbh 008b787
reduced scope to soundness and reference implementation generating roo
bhargavbh a52e7ac
trimmed comments
bhargavbh a258ed6
removed leafHashDisjointFromNodeHash and nodeHashInjective
bhargavbh f6cbe2f
fmt
bhargavbh c0e2d92
tuned
bhargavbh a539e66
removed hashLeafReproducesHashOffer
bhargavbh b5870a9
factored out generate root; restored empty leaf condition
bhargavbh 5ce2a55
Merge branch 'main' into certora/offer-tree
bhargavbh 588da4a
remove smt_timeout from configs
bhargavbh 98c9332
model simplified: node ID is the hash
bhargavbh 0ec785d
make offerTree creation idempotent
bhargavbh a325670
GenerateRoot now does not inherit from OfferTree
bhargavbh cd9458a
duplicate lead test added
bhargavbh bee5afa
Disable caching for OfferTree verification
bhargavbh 8104167
all fields zero in isEmpty
bhargavbh 4b669c0
use newLeaf and newInternalNode in generateRoot; don't inherit Offert…
bhargavbh 36c3a5b
remove testGenerateRootOnlyPopulatesConfiguredTree
bhargavbh 78fbdc9
removed GenerateRoot links from wellformed config
bhargavbh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| { | ||
| "files": [ | ||
| "certora/helpers/OfferTree.sol" | ||
| ], | ||
| "verify": "OfferTree:certora/specs/OfferTreeMembership.spec", | ||
| "solc": "solc-0.8.34", | ||
| "solc_via_ir": true, | ||
| "solc_optimize": "200", | ||
| "solc_evm_version": "osaka", | ||
| "optimistic_loop": true, | ||
| "loop_iter": 4, | ||
| "optimistic_hashing": true, | ||
| "hashing_length_bound": 1024, | ||
| "prover_args": [ | ||
| "-splitParallel true", | ||
| "-s [z3:def{randomSeed=1},z3:def{randomSeed=2},z3:def{randomSeed=3},z3:def{randomSeed=4},z3:def{randomSeed=5}]" | ||
| ], | ||
| "msg": "Offer Tree Membership" | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| { | ||
| "files": [ | ||
| "certora/helpers/OfferTree.sol" | ||
| ], | ||
| "verify": "OfferTree:certora/specs/OfferTreeWellFormed.spec", | ||
| "solc": "solc-0.8.34", | ||
| "solc_via_ir": true, | ||
| "solc_optimize": "200", | ||
| "solc_evm_version": "osaka", | ||
| "optimistic_loop": true, | ||
| "loop_iter": 4, | ||
| "optimistic_hashing": true, | ||
| "hashing_length_bound": 1024, | ||
| "disable_auto_cache_key_gen": true, | ||
|
bhargavbh marked this conversation as resolved.
|
||
| "prover_args": [ | ||
| "-splitParallel true", | ||
| "-s [z3:def{randomSeed=1},z3:def{randomSeed=2},z3:def{randomSeed=3},z3:def{randomSeed=4},z3:def{randomSeed=5}]" | ||
| ], | ||
| "msg": "OfferTreeWellFormed" | ||
| } | ||
|
bhargavbh marked this conversation as resolved.
bhargavbh marked this conversation as resolved.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
| // Copyright (c) 2025 Morpho Association | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| import {Offer} from "../../src/interfaces/IMidnight.sol"; | ||
| import {OfferTree} from "./OfferTree.sol"; | ||
|
|
||
| contract GenerateRoot { | ||
| OfferTree public immutable offerTree; | ||
|
|
||
| constructor(OfferTree _offerTree) { | ||
| offerTree = _offerTree; | ||
| } | ||
|
|
||
| // Populate the supplied model and return the root for a non-empty, power-of-two list of offers. | ||
| function generateRoot(Offer[] memory leaves) public returns (bytes32) { | ||
| require(leaves.length > 0 && (leaves.length & (leaves.length - 1)) == 0, "invalid leaves length"); | ||
|
|
||
| bytes32[] memory level = new bytes32[](leaves.length); | ||
| for (uint256 i = 0; i < leaves.length; i++) { | ||
| level[i] = offerTree.newLeaf(leaves[i]); | ||
| } | ||
|
|
||
| uint256 levelLength = level.length; | ||
| while (levelLength > 1) { | ||
| levelLength /= 2; | ||
| for (uint256 i = 0; i < levelLength; i++) { | ||
| level[i] = offerTree.newInternalNode(level[2 * i], level[2 * i + 1]); | ||
| } | ||
| } | ||
|
|
||
| return level[0]; | ||
|
bhargavbh marked this conversation as resolved.
|
||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
| // Copyright (c) 2025 Morpho Association | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| import {Offer} from "../../src/interfaces/IMidnight.sol"; | ||
| import {HashLib, OFFER_TYPEHASH} from "../../src/ratifiers/libraries/HashLib.sol"; | ||
|
|
||
| // Fixed-size pre-image of HashLib.hashOffer. Dynamic fields are stored as hashes so CVL can re-hash a leaf without iterating over dynamic data. | ||
| struct Leaf { | ||
| bytes32 marketHash; // = HashLib.hashMarket(offer.market) | ||
| bool buy; | ||
| address maker; | ||
| uint256 start; | ||
| uint256 expiry; | ||
| uint256 tick; | ||
| bytes32 group; | ||
| address callback; | ||
| bytes32 callbackDataHash; // = keccak256(offer.callbackData) | ||
| address receiverIfMakerIsSeller; | ||
| address ratifier; | ||
| bool reduceOnly; | ||
| uint256 maxUnits; | ||
| uint256 maxAssets; | ||
| uint256 continuousFeeCap; | ||
| } | ||
|
|
||
| contract OfferTree { | ||
| struct Node { | ||
| bytes32 left; | ||
| bytes32 right; | ||
| Leaf leaf; | ||
| // Offer hash for leaves and hash of the children for internal nodes. | ||
| bytes32 hash; | ||
| } | ||
|
|
||
| // Every populated node is keyed by its hash. | ||
| mapping(bytes32 => Node) internal tree; | ||
|
|
||
| // Create a leaf or return its existing hash ID. | ||
| function newLeaf(Offer memory offer) public returns (bytes32 id) { | ||
| id = HashLib.hashOffer(offer); | ||
| require(id != 0, "id is the zero bytes"); | ||
| Node storage n = tree[id]; | ||
| if (!isEmpty(n)) return id; | ||
| Leaf storage l = n.leaf; | ||
| l.marketHash = HashLib.hashMarket(offer.market); | ||
| l.buy = offer.buy; | ||
| l.maker = offer.maker; | ||
| l.start = offer.start; | ||
| l.expiry = offer.expiry; | ||
| l.tick = offer.tick; | ||
| l.group = offer.group; | ||
| l.callback = offer.callback; | ||
| l.callbackDataHash = keccak256(offer.callbackData); | ||
| l.receiverIfMakerIsSeller = offer.receiverIfMakerIsSeller; | ||
| l.ratifier = offer.ratifier; | ||
| l.reduceOnly = offer.reduceOnly; | ||
| l.maxUnits = offer.maxUnits; | ||
| l.maxAssets = offer.maxAssets; | ||
| l.continuousFeeCap = offer.continuousFeeCap; | ||
| n.hash = id; | ||
| } | ||
|
|
||
| // Create an internal node or return its existing hash ID. | ||
| function newInternalNode(bytes32 left, bytes32 right) public returns (bytes32 id) { | ||
| bytes32 leftHash = tree[left].hash; | ||
| bytes32 rightHash = tree[right].hash; | ||
| require(leftHash != 0, "left empty"); | ||
| require(rightHash != 0, "right empty"); | ||
| id = HashLib.hashNode(leftHash, rightHash); | ||
| require(id != 0, "zero hash"); | ||
| Node storage n = tree[id]; | ||
| if (!isEmpty(n)) return id; | ||
| n.left = left; | ||
| n.right = right; | ||
| n.hash = id; | ||
| } | ||
|
|
||
| function isEmpty(Node storage n) internal view returns (bool) { | ||
| return n.hash == 0; | ||
| } | ||
|
|
||
| function isEmpty(bytes32 id) public view returns (bool) { | ||
| return isEmpty(tree[id]); | ||
| } | ||
|
|
||
| function isLeafNode(bytes32 id) public view returns (bool) { | ||
| return tree[id].left == 0 && tree[id].right == 0 && tree[id].hash != 0; | ||
| } | ||
|
|
||
| function hashOffer(Offer memory offer) public pure returns (bytes32) { | ||
| return HashLib.hashOffer(offer); | ||
| } | ||
|
|
||
| function isLeaf(bytes32 root, bytes32 leafHash, uint256 leafIndex, bytes32[] memory proof) | ||
| public | ||
| pure | ||
| returns (bool) | ||
| { | ||
| return HashLib.isLeaf(root, leafHash, leafIndex, proof); | ||
| } | ||
|
|
||
| // Reconstruct HashLib.hashOffer from the stored pre-image. | ||
| function hashLeaf(Leaf storage l) internal view returns (bytes32) { | ||
| return keccak256( | ||
| abi.encode( | ||
| OFFER_TYPEHASH, | ||
| l.marketHash, | ||
| l.buy, | ||
| l.maker, | ||
| l.start, | ||
| l.expiry, | ||
| l.tick, | ||
| l.group, | ||
| l.callback, | ||
| l.callbackDataHash, | ||
| l.receiverIfMakerIsSeller, | ||
| l.ratifier, | ||
| l.reduceOnly, | ||
| l.maxUnits, | ||
| l.maxAssets, | ||
| l.continuousFeeCap | ||
| ) | ||
|
bhargavbh marked this conversation as resolved.
|
||
| ); | ||
| } | ||
|
|
||
| // Empty nodes are fully zeroed. Populated nodes have their hash as identifier and hash their leaf data or their two non-empty children. | ||
| function isWellFormed(bytes32 id) public view returns (bool) { | ||
| Node storage n = tree[id]; | ||
| if (isEmpty(n)) { | ||
| Leaf storage l = n.leaf; | ||
| return n.left == 0 && n.right == 0 && l.marketHash == 0 && !l.buy && l.maker == address(0) && l.start == 0 | ||
| && l.expiry == 0 && l.tick == 0 && l.group == 0 && l.callback == address(0) && l.callbackDataHash == 0 | ||
| && l.receiverIfMakerIsSeller == address(0) && l.ratifier == address(0) && !l.reduceOnly | ||
| && l.maxUnits == 0 && l.maxAssets == 0 && l.continuousFeeCap == 0; | ||
| } | ||
| if (n.left == 0 && n.right == 0) { | ||
| bytes32 expected = hashLeaf(n.leaf); | ||
| return n.hash == expected && id == expected; | ||
| } | ||
| if (n.left != 0 && n.right != 0) { | ||
| bytes32 leftHash = tree[n.left].hash; | ||
| bytes32 rightHash = tree[n.right].hash; | ||
| return leftHash != 0 && rightHash != 0 && n.hash == id && n.hash == HashLib.hashNode(leftHash, rightHash); | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| // Check the path selected by leafIndex. | ||
| function wellFormedPath(bytes32 id, uint256 leafIndex, uint256 depth) public view returns (bool) { | ||
| for (uint256 i = depth;;) { | ||
| require(isWellFormed(id)); | ||
|
|
||
| if (i == 0) break; | ||
|
|
||
| --i; | ||
| id = ((leafIndex >> i) & 1 == 0) ? tree[id].left : tree[id].right; | ||
| } | ||
| return true; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| methods { | ||
| function isEmpty(bytes32) external returns (bool) envfree; | ||
| function hashOffer(OfferTree.Offer) external returns (bytes32) envfree; | ||
| function isLeaf(bytes32, bytes32, uint256, bytes32[]) external returns (bool) envfree; | ||
| function isLeafNode(bytes32) external returns (bool) envfree; | ||
| function wellFormedPath(bytes32, uint256, uint256) external returns (bool) envfree; | ||
| } | ||
|
|
||
| // Soundness: if a Merkle proof verifies along a well-formed path, hashOffer(offer) must be a leaf node. | ||
| rule membershipSoundness(OfferTree.Offer offer, bytes32 root, uint256 leafIndex, bytes32[] proof) { | ||
| require !isEmpty(root), "root is a populated node"; | ||
| require wellFormedPath(root, leafIndex, proof.length), "the path from the root to the leaf is well-formed"; | ||
|
bhargavbh marked this conversation as resolved.
|
||
| bytes32 leafId = hashOffer(offer); | ||
| require isLeaf(root, leafId, leafIndex, proof), "Merkle proof verifies the offer"; | ||
|
|
||
| assert isLeafNode(leafId); | ||
| } | ||
|
QGarchery marked this conversation as resolved.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| // Leaves store a fixed-size pre-image so isWellFormed can re-hash them without dynamic loops. | ||
|
|
||
| methods { | ||
| function isEmpty(bytes32) external returns (bool) envfree; | ||
| function isWellFormed(bytes32) external returns (bool) envfree; | ||
| } | ||
|
|
||
| // The zero node remains empty. | ||
| strong invariant zeroIsEmpty() | ||
| isEmpty(to_bytes32(0)); | ||
|
|
||
| // Every node stays well-formed within the configured Solidity loop bounds. | ||
| strong invariant wellFormed(bytes32 id) | ||
| isWellFormed(id) | ||
| { | ||
| preserved { | ||
| requireInvariant zeroIsEmpty(); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.