diff --git a/docs/deployment-status.html b/docs/deployment-status.html new file mode 100644 index 00000000..328bedf9 --- /dev/null +++ b/docs/deployment-status.html @@ -0,0 +1,215 @@ + + + + + + Deployment Status Oracle + + + +
+
+

Deployment Status Oracle

+

+ DeploymentStatusOracle is a deployment-progress helper. It does not + measure protocol readiness or initialization success. It only reports which + configured deployment-step addresses currently contain runtime bytecode. +

+

+ The mainnet manifest lists this contract directly, so operators and frontends need a + precise explanation of the returned bitmask and the built-in 256-step ceiling. +

+
+ +
+

What getDeploymentMask() Returns

+

+ The constructor stores one ordered address[] deploymentAddresses. + getDeploymentMask() loops over that array and checks + deploymentAddresses[index].code.length. When code exists, the oracle sets + bit index in the returned uint256. +

+

+ This is a code-presence bitmap. A set bit means code exists at the configured + address. A clear bit means the address currently has no code. The oracle does not + verify constructor args, ownership wiring, or post-deployment setup. +

+
+ + Deployment mask maps ordered deployment steps to bits + A row of deployment steps on the left maps by index into bit positions in a uint256 mask on the right. Each step sets its bit only when code exists at that configured address. + + + + + + + + + step 0 + step 1 + step 2 + + + + + + + + + bit 0 + bit 1 + bit 2 + ... + code? + code? + code? + returned uint256 + +

+ Deployment Status MaskEach configured deployment + step owns one bit position in the returned word. The oracle sets that bit only if + the step's address currently contains code. +

+
+
+ + + deployedMask + = + + + + i + = + 0 + + + + codePresent + ( + i + ) + + 2i + + + +

+ Deployment BitmaskEach configured deployment + step contributes one bit position to the returned word. +

+
+
+ +
+

How Bits Map To Deployment Steps

+

+ Bit positions are array positions. Bit 0 maps to the first constructor + address, bit 1 to the second, and so on. Offchain decoding must use the + same ordered list that seeded the oracle. +

+

+ The current UI and deployment helpers derive the constructor list from + deploymentSteps with one exception: the oracle does not include its own + address in its constructor array. The manifest still lists + deploymentStatusOracle as a deployment step, but the UI tracks that step + out-of-band and starts consuming mask bits from the remaining addresses. +

+

+ With the current mainnet manifest, the low-order mask mapping therefore begins: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BitMainnet step idLabel
0proxyDeployerProxy Deployer
1multicall3Multicall3
2uniformPriceDualCapBatchAuctionFactoryUniform Price Dual Cap Batch Auction Factory
3scalarOutcomesScalar Outcomes
4securityPoolUtilsSecurity Pool Utils
+

+ The same pattern continues through the remaining deployment-status step addresses: + openOracle, zoltarQuestionData, zoltar, + shareTokenFactory, + priceOracleManagerAndOperatorQueuerFactory, + securityPoolForker, escalationGameFactory, and + securityPoolFactory. If that constructor order changes, the bit meanings + change with it. +

+

+ The manifest's derivedContracts list is separate. Those addresses are + not part of the deployment-status mask unless they also appear in the constructor + array. +

+
+ +
+

Why The Cap Is 256 Steps

+

+ The constructor requires + deploymentAddresses.length <= uint256(type(uint8).max) + 1, which is + 256. The limit exists because the result is a single + uint256, and each tracked step consumes one bit. +

+

+ Indices 0...255 are supported. A deployment process with more than + 256 tracked addresses must split status across multiple masks or use a + different representation. +

+
+ +
+

Sources

+ +
+
+ + diff --git a/docs/escalation-game-architecture.html b/docs/escalation-game-architecture.html index 8d85a612..1a025389 100644 --- a/docs/escalation-game-architecture.html +++ b/docs/escalation-game-architecture.html @@ -152,6 +152,11 @@

Module Split

value without touching storage, it belongs in the proof verifier, a free function, or a test helper instead of widening the mutable stack.

+

+ For the carry-proof data structure itself, including leaf fields, peak bagging, the + 64-peak bound, and nullifier replay protection, see + Merkle Mountain Range carry proofs. +

diff --git a/docs/merkle-mountain-range.html b/docs/merkle-mountain-range.html new file mode 100644 index 00000000..efac1dec --- /dev/null +++ b/docs/merkle-mountain-range.html @@ -0,0 +1,259 @@ + + + + + + Merkle Mountain Range Carry Proofs + + + +
+
+

Merkle Mountain Range Carry Proofs

+

+ Placeholder uses a Merkle Mountain Range only for inherited escalation carry. + Parent escalation games export unresolved deposits into compact snapshots, and child + continuations later verify withdrawals against those snapshots without replaying the + full parent history onchain. +

+

+ The hashing primitives live in MerkleMountainRange.sol. Snapshot + storage, proof structs, peak bounds, and replay protection live in + EscalationGameTypes.sol, EscalationGameCarry.sol, and + EscalationGameProofVerifier.sol. +

+
+ +
+

Leaf Shape And Hash Order

+

+ Each leaf hashes one unresolved deposit with + depositor, outcome, amount, + parentDepositIndex, cumulativeAmount, and + sourceNodeId. Leaf hashes use keccak256(abi.encode(...)). + Parent hashes use keccak256(abi.encodePacked(left, right)). +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldWhy it is committed
depositorBinds the proof to the beneficiary vault.
outcomeSeparates invalid, yes, and no carry domains.
amountCommits to the source principal.
parentDepositIndexProvides the stable identity later consumed by nullifiers.
cumulativeAmountPreserves payout-order prefix data.
sourceNodeIdDistinguishes otherwise identical leaves copied from different local nodes.
+

+ Hash order matters. Internal nodes always hash left before + right, so proofs are position-sensitive inside each peak. +

+
+ +
+

Peaks And The 64-Peak Bound

+

+ The carry snapshot stores one peak per set bit in the leaf count. The system fixes + MERKLE_MOUNTAIN_RANGE_MAX_PEAKS = 64, and carry initialization requires + each snapshot leaf count to be less than 2^64. +

+

+ That does not cap the snapshot at 64 leaves. It caps the number of peak positions. + Any leaf count from 0 up to but not including 2^64 is + valid because its binary decomposition fits inside 64 peak slots. +

+
+ + Merkle Mountain Range peaks are determined by set bits in the leaf count + A leaf count of thirteen is shown as binary one one zero one, corresponding to occupied peaks at heights zero, two, and three which are later bagged into one root. + + leafCount = 13 + binary 1101 + set bits at heights 0, 2, 3 + + + + peak 0 + peak 2 + peak 3 + occupied peaks + + + root + +

+ MMR PeaksThe occupied peak heights are exactly + the set bits in the snapshot leaf count. Those occupied peaks are later bagged + into one root. +

+
+

+ To form one root, the verifier collects occupied peaks in ascending height order and + then bags them from right to left with bagPeaks(). +

+
+ + + snapshotLeafCount + < + 264 + + +

+ Carry Snapshot BoundThe 64-peak constant means + each inherited snapshot leaf count must fit below 2^64. +

+
+

+ Local carry appends behave like binary addition with carries: a new leaf merges + upward through occupied lower peaks until it finds the first empty peak slot. If that + upward carry would reach height 64, the append reverts with + MMR too tall. +

+
+ +
+

Proof Structure

+

+ A carried-deposit proof has two parts. First it proves membership in the inherited + MMR snapshot for one outcome. Then it proves that the same + parentDepositIndex has not already been consumed in this continuation's + nullifier tree. +

+ + + + + + + + + + + + + + + + + +
ComponentPurpose
leafIndex, merkleMountainRangePeakIndex, merkleMountainRangeSiblingsProves the carried deposit belongs to the inherited snapshot root for that outcome.
nullifierSiblingsProves the nullifier leaf is still empty so the carried deposit cannot be replayed.
+

+ The index semantics are stricter than the field names might suggest. + merkleMountainRangePeakIndex is the occupied peak height, not the + ordinal position of that peak among occupied peaks. leafIndex is the + leaf's offset inside that selected peak, so the verifier requires + leafIndex < 2^merkleMountainRangePeakIndex. For example, if the full + snapshot has 13 leaves, its occupied peak heights are + 0, 2, and 3. A proof for a leaf inside the + height-2 peak uses merkleMountainRangePeakIndex = 2 and a + peak-local leafIndex in 0...3, not the leaf's global index + across all 13 leaves. +

+

+ merkleMountainRangeSiblings is also ordered in two phases. The first + merkleMountainRangePeakIndex entries are the bottom-up Merkle siblings + inside the selected peak. The remaining entries are the other occupied peak roots in + ascending peak-height order, skipping the selected peak height. That exact ordering is + why the verifier checks + merkleMountainRangeSiblings.length == peakHeight + peakCount - 1. +

+

+ The verifier requires + merkleMountainRangeSiblings.length == peakHeight + peakCount - 1 and + nullifierSiblings.length == NULLIFIER_DEPTH with + NULLIFIER_DEPTH = 64. +

+

+ Nullifier paths are keyed by + uint256(keccak256(abi.encode(parentDepositIndex))), so the stable parent + deposit index is the replay-prevention identity. +

+
+ +
+

Snapshots In Child Continuations

+

+ Each continuation outcome stores both an inherited snapshot baseline and a mutable + current state. +

+ + + + + + + + + + + + + + + + + + + + + + + + + +
FieldMeaning
snapshotLeafCount and snapshotPeaksThe immutable inherited carry commitment.
currentLeafCount and currentPeaksThe descendant carry snapshot after inherited snapshot initialization plus local carry appends and local carry-leaf removal. Inherited proof consumption is tracked by currentNullifierRoot and unresolved totals rather than by mutating these peak fields directly.
inheritedUnresolvedTotal and localUnresolvedTotalThe principal totals the current carry state must still represent.
currentNullifierRootThe replay-protection root after any carried proof has been consumed.
+

+ The continuation API exposes the current descendant carry snapshot through + getForkCarrySnapshot() and pages only local unresolved leaves through + getCarryLeafPageByOutcome(). The immutable inherited baseline remains in + snapshotPeaks and snapshotLeafCount inside outcome state, + while getForkCarrySnapshot() reports the current carry peaks, current + leaf counts, current totals, and current nullifier roots after local appends and + proof consumption. +

+
+ +
+

Sources

+ +
+
+ + diff --git a/docs/operator-reference.md b/docs/operator-reference.md index a022a1f1..7f129b64 100644 --- a/docs/operator-reference.md +++ b/docs/operator-reference.md @@ -51,11 +51,14 @@ contract-first form. | Share-token salt squatting | Direct `ShareTokenFactory` callers cannot reserve the canonical origin-pool share-token address. `CREATE2` includes constructor arguments in the init-code hash, and the share token owner is `msg.sender`, so a direct caller using the canonical salt deploys a caller-owned token at a different address than the `SecurityPoolFactory` deployment. | [ShareTokenFactory.sol](../solidity/contracts/peripherals/factories/ShareTokenFactory.sol), [ShareToken.sol](../solidity/contracts/peripherals/tokens/ShareToken.sol) | | Complete-set minting | Complete-set minting checks the next collateral amount against available bond capacity before minting shares, then updates pool accounting before the share-token mint call. | [`SecurityPool.createCompleteSet`](../solidity/contracts/peripherals/SecurityPool.sol) | | Fee accrual clamp | Fee accrual is clamped to the question end time while the universe is unforked; after a universe fork, the accumulator is clamped to the fork time. | [SecurityPool.sol](../solidity/contracts/peripherals/SecurityPool.sol) | +| Fee-index and fee-owed dust | Fee accrual intentionally carries denominator-sensitive remainder in `feeIndexRemainder` and sub-wei ETH remainder in `totalFeesOwedRemainder`. Those values preserve exact value across later accrual steps instead of dropping division dust, and the allowance-scoped `feeIndexRemainder` is cleared whenever allowance ownership changes so old-denominator dust is not reassigned to new allowance holders. | [`SecurityPool.updateCollateralAmount`](../solidity/contracts/peripherals/SecurityPool.sol), [`SecurityPool._clearFeeIndexRemainder`](../solidity/contracts/peripherals/SecurityPool.sol) | | Retention-rate updates | Retention-rate updates no-op when total bond allowance is zero or when the pool is not `Operational`. | [SecurityPool.sol](../solidity/contracts/peripherals/SecurityPool.sol) | | Escrowed REP withdrawal lock | `performWithdrawRep` rejects withdrawal while the vault still has REP escrowed in an escalation game; the vault must settle those locks first. | [SecurityPool.sol](../solidity/contracts/peripherals/SecurityPool.sol) | | External fork withdrawal lock | If the universe forked before the local escalation game ended and non-decision was not reached, parent-pool escalation withdrawal reverts and the vault must migrate forked locks. | [SecurityPool.sol](../solidity/contracts/peripherals/SecurityPool.sol) | +| REP-to-ownership round-up | `repToPoolOwnershipRoundUp` uses ceiling division when the pool removes vault ownership for an escalation deposit. That intentionally burns enough ownership to cover the requested REP even when the ownership conversion is fractional. | [`SecurityPool.repToPoolOwnershipRoundUp`](../solidity/contracts/peripherals/SecurityPool.sol), [`SecurityPool.depositToEscalationGame`](../solidity/contracts/peripherals/SecurityPool.sol) | | Escalation deposit wrapper | `depositToEscalationGame` deploys the game on the first valid post-end deposit, previews the accepted amount, removes vault REP ownership with round-up accounting, checks local and global solvency, transfers REP into the game, and records the deposit. | [`SecurityPool.depositToEscalationGame`](../solidity/contracts/peripherals/SecurityPool.sol), [`EscalationGame.recordDepositFromSecurityPool`](../solidity/contracts/peripherals/EscalationGame.sol) | | Direct ETH receiver | The pool accepts direct ETH only from the forker, its truth auction, or its parent pool. | [SecurityPool.sol](../solidity/contracts/peripherals/SecurityPool.sol) | +| Vault enumeration | `getVaults(startIndex, count)` pages the append-only vault list in insertion order. `getActiveVaults(startIndex, count)` pages only active vaults in newest-first order by walking the active-vault linked list from `latestActiveVault`. Both return an empty array when `count == 0` or the start index is out of range. | [`SecurityPool.getVaults`](../solidity/contracts/peripherals/SecurityPool.sol), [`SecurityPool.getActiveVaults`](../solidity/contracts/peripherals/SecurityPool.sol) | ## Share Migration @@ -150,3 +153,18 @@ callback behavior, stale-operation handling, and liquidation boundaries. | Consumed failures | Expired operations, stale liquidations, zero-effect withdrawals, and liquidations too close to threshold are consumed and emitted as failed executions rather than retried forever. | [OpenOraclePriceCoordinator.sol](../solidity/contracts/peripherals/OpenOraclePriceCoordinator.sol) | | Liquidation snapshot | A staged liquidation becomes stale if target ownership decreases or target allowance changes. A target vault can deposit more REP without invalidating the staged liquidation. | [OpenOraclePriceCoordinator.sol](../solidity/contracts/peripherals/OpenOraclePriceCoordinator.sol) | | Liquidation distance | A staged liquidation must remain at least `minLiquidationPriceDistanceBps` beyond the liquidation threshold when it executes. | [OpenOraclePriceCoordinator.sol](../solidity/contracts/peripherals/OpenOraclePriceCoordinator.sol) | + +## Support Module Inventory + +The whitepapers focus on protocol flow. The table below names the remaining +support contracts that matter for integrators, code reviewers, and interface +inventory work. + +| Area | Named contracts and modules | Role | +| --- | --- | --- | +| Deployment tracking | [`DeploymentStatusOracle.sol`](../solidity/contracts/DeploymentStatusOracle.sol) | Reports one code-presence bit per configured deployment-step address. See [Deployment Status Oracle](./deployment-status.html). | +| Carry proof hashing | [`MerkleMountainRange.sol`](../solidity/contracts/peripherals/MerkleMountainRange.sol), [`EscalationGameProofVerifier.sol`](../solidity/contracts/peripherals/EscalationGameProofVerifier.sol), [`EscalationGameTypes.sol`](../solidity/contracts/peripherals/EscalationGameTypes.sol) | Defines the carried-deposit leaf shape, the 64-peak limit, proof-length rules, and nullifier-root replay protection. See [Merkle Mountain Range carry proofs](./merkle-mountain-range.html). | +| ERC-20 support | [`ERC20.sol`](../solidity/contracts/ERC20.sol), [`IERC20.sol`](../solidity/contracts/IERC20.sol), [`IERC20Metadata.sol`](../solidity/contracts/IERC20Metadata.sol), [`Context.sol`](../solidity/contracts/Context.sol), [`SafeERC20Ops.sol`](../solidity/contracts/SafeERC20Ops.sol), [`ReputationToken.sol`](../solidity/contracts/ReputationToken.sol) | Base REP token implementation, interfaces, metadata, execution context, and safe transfer wrappers. | +| ERC-1155 and token ids | [`ERC1155.sol`](../solidity/contracts/peripherals/tokens/ERC1155.sol), [`ShareToken.sol`](../solidity/contracts/peripherals/tokens/ShareToken.sol), [`TokenId.sol`](../solidity/contracts/peripherals/tokens/TokenId.sol), [`IERC1155.sol`](../solidity/contracts/peripherals/interfaces/IERC1155.sol), [`IERC1155Receiver.sol`](../solidity/contracts/peripherals/interfaces/IERC1155Receiver.sol), [`IERC165.sol`](../solidity/contracts/peripherals/interfaces/IERC165.sol), [`IShareToken.sol`](../solidity/contracts/peripherals/interfaces/IShareToken.sol) | Outcome-share token plumbing, interface support, and token-id encoding across universes and outcomes. | +| Factories and deployers | [`EscalationGameFactory.sol`](../solidity/contracts/peripherals/factories/EscalationGameFactory.sol), [`SecurityPoolFactory.sol`](../solidity/contracts/peripherals/factories/SecurityPoolFactory.sol), [`SecurityPoolDeployer.sol`](../solidity/contracts/peripherals/factories/SecurityPoolDeployer.sol), [`ShareTokenFactory.sol`](../solidity/contracts/peripherals/factories/ShareTokenFactory.sol), [`UniformPriceDualCapBatchAuctionFactory.sol`](../solidity/contracts/peripherals/factories/UniformPriceDualCapBatchAuctionFactory.sol), [`PriceOracleManagerAndOperatorQueuerFactory.sol`](../solidity/contracts/peripherals/factories/PriceOracleManagerAndOperatorQueuerFactory.sol) | Deterministic deployment entrypoints for pool, share-token, oracle-coordinator, escalation-game, and auction instances. | +| Migration delegates and storage modules | [`SecurityPoolMigrationProxy.sol`](../solidity/contracts/peripherals/SecurityPoolMigrationProxy.sol), [`SecurityPoolForker.sol`](../solidity/contracts/peripherals/SecurityPoolForker.sol), [`SecurityPoolForkerBase.sol`](../solidity/contracts/peripherals/SecurityPoolForkerBase.sol), [`SecurityPoolForkerStorage.sol`](../solidity/contracts/peripherals/SecurityPoolForkerStorage.sol), [`SecurityPoolForkerTypes.sol`](../solidity/contracts/peripherals/SecurityPoolForkerTypes.sol), [`SecurityPoolForkerVaultMigrationBase.sol`](../solidity/contracts/peripherals/SecurityPoolForkerVaultMigrationBase.sol), [`SecurityPoolForkerVaultMigrationDelegate.sol`](../solidity/contracts/peripherals/SecurityPoolForkerVaultMigrationDelegate.sol) | Split fork-time state, migration execution, and stable proxy identity used while routing parent state into child pools. | diff --git a/docs/start-here.html b/docs/start-here.html index 7cdbed10..5df86088 100644 --- a/docs/start-here.html +++ b/docs/start-here.html @@ -513,6 +513,63 @@

Documentation Map

Understand how the local dispute contract is split across Solidity modules. EscalationGame architecture + + Decode the deployment progress bitmask and its 256-step limit. + Deployment Status Oracle + + + Understand carry-proof hashing, snapshot peaks, and nullifier replay protection. + Merkle Mountain Range carry proofs + + + +
+ +
+

Supporting Contract Inventory

+

+ The whitepapers focus on protocol flow. The modules below round out the contract map + for readers who need the support-layer pieces named explicitly. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AreaNamed contracts and modulesRole
ERC-20 infrastructureERC20, IERC20, IERC20Metadata, Context, SafeERC20Ops, ReputationTokenREP token mechanics, token interfaces, and safe transfer wrappers.
ERC-1155 infrastructureERC1155, ShareToken, TokenId, IShareToken, IERC1155, IERC1155Receiver, IERC165Outcome-share token storage, interface support, and token-id encoding.
Factories and deployersEscalationGameFactory, SecurityPoolFactory, SecurityPoolDeployer, ShareTokenFactory, UniformPriceDualCapBatchAuctionFactory, PriceOracleManagerAndOperatorQueuerFactoryDeterministic deployment entrypoints for pool, token, oracle, escalation-game, and auction instances.
Migration delegates and storage modulesSecurityPoolMigrationProxy, SecurityPoolForker, SecurityPoolForkerBase, SecurityPoolForkerStorage, SecurityPoolForkerTypes, SecurityPoolForkerVaultMigrationBase, SecurityPoolForkerVaultMigrationDelegateFork-time state routing, vault migration, child REP splitting, and stable migration identity.
Carry proof structureMerkleMountainRange, EscalationGameTypes, EscalationGameState, EscalationGameCarry, EscalationGameProofVerifierProof structs, storage layout, inherited carry snapshots, and replay-resistant carried-deposit verification.
Deployment trackingDeploymentStatusOracleMainnet deployment-step code-presence bitmap with one bit per configured address.
diff --git a/docs/whitepaper_placeholder.html b/docs/whitepaper_placeholder.html index f7dae2e1..f2a6a66a 100644 --- a/docs/whitepaper_placeholder.html +++ b/docs/whitepaper_placeholder.html @@ -4130,7 +4130,9 @@

Escalation Settlement Call Matrix

Continuation withdrawals use Merkle Mountain Range proofs against the inherited carry snapshot. Each accepted proof advances the nullifier root, so a carried parent deposit cannot be replayed in - the child. + the child. The exact leaf fields, peak bagging order, proof-length + rules, and 64-peak bound are documented in + Merkle Mountain Range carry proofs.

If an unrelated external Zoltar fork interrupts the local escalation diff --git a/docs/whitepaper_zoltar.html b/docs/whitepaper_zoltar.html index 41885d78..265451e1 100644 --- a/docs/whitepaper_zoltar.html +++ b/docs/whitepaper_zoltar.html @@ -738,6 +738,12 @@

6. Questions and Outcome Encoding

For scalar questions, there are no categorical labels to include, so the id is determined from the scalar question fields alone.

+

+ Every call to createQuestion also requires + endTime >= startTime. The contract rejects any question whose end + time is earlier than its start time before it reaches scalar or categorical + validation. +

Categorical Questions

@@ -770,7 +776,7 @@

Scalar Questions

Scalar validity rule -

The two 120-bit payout numerators must sum to numTicks; otherwise the answer is malformed, not invalid.

+

Bits 254...240 are reserved and must be zero. Any nonzero reserved bit, or any payout pair whose two 120-bit numerators fail to sum to numTicks, is malformed rather than invalid.

@@ -794,8 +800,8 @@

Scalar Questions

first payout numerator bits 119...0 second payout numerator - Unused high-order padding bits are not part - of this scalar payload. + Bits 254...240 are reserved and must be zero. + Nonzero reserved bits are malformed. @@ -821,7 +827,7 @@

Scalar Questions

First payout Second payout -

Packed Scalar AnswerA scalar answer is easier to read as a namespace flag plus two payout fields: 0 means invalid namespace, while 1 means the payout fields must sum to numTicks.

+

Packed Scalar AnswerA scalar answer is easier to read as reserved bits, a namespace flag, and two payout fields: bits 254...240 must be zero, 0 in bit 255 is the invalid namespace, and 1 means the payout fields must sum to numTicks.

@@ -841,6 +847,12 @@

Scalar Questions

Scalar ValidityA valid scalar answer must allocate all ticks across the two payout numerators.

+

+ The all-zero word is the only canonical scalar Invalid value. Any + other word with the highest bit clear is malformed, and any word with a nonzero + reserved bit in 254...240 is malformed even if the payout fields would + otherwise decode cleanly. +

At the helper and UI level, a scalar tick index named tickIndex is encoded as: