Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
939 changes: 558 additions & 381 deletions solidity/contracts/peripherals/EscalationGame.sol

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions solidity/contracts/peripherals/MerkleMountainRange.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.35;

import { BinaryOutcomes } from './BinaryOutcomes.sol';

library MerkleMountainRange {
function hashLeaf(address depositor, BinaryOutcomes.BinaryOutcome outcome, uint256 amount, uint256 parentDepositIndex, uint256 cumulativeAmount, uint256 sourceNodeId) internal pure returns (bytes32) {
return keccak256(abi.encode(depositor, outcome, amount, parentDepositIndex, cumulativeAmount, sourceNodeId));
}

function hashParent(bytes32 left, bytes32 right) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(left, right));
}

function bagPeaks(bytes32[] memory peaks, uint256 peakCount) internal pure returns (bytes32 root) {
if (peakCount == 0) return bytes32(0);
root = peaks[peakCount - 1];
for (uint256 peakIndex = peakCount - 1; peakIndex > 0; peakIndex--) {
root = hashParent(peaks[peakIndex - 1], root);
}
}
}
49 changes: 31 additions & 18 deletions solidity/contracts/peripherals/SecurityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { ISecurityPool, SecurityVault, SystemState, QuestionOutcome, ISecurityPo
import { OpenOracle } from './openOracle/OpenOracle.sol';
import { SecurityPoolUtils } from './SecurityPoolUtils.sol';
import { EscalationGameFactory } from './factories/EscalationGameFactory.sol';
import { EscalationGame } from './EscalationGame.sol';
import { EscalationGame, CarriedDepositProof } from './EscalationGame.sol';
import { ZoltarQuestionData } from '../ZoltarQuestionData.sol';
import { SecurityPoolForker } from './SecurityPoolForker.sol';
import { ISecurityPoolForker } from './interfaces/ISecurityPoolForker.sol';
Expand Down Expand Up @@ -410,26 +410,23 @@ contract SecurityPool is ISecurityPool {
emit RedeemRep(msg.sender, vault, repAmount);
}

function withdrawForkedEscalationDeposits(QuestionOutcome outcome, uint256[] memory parentDepositIndexes) external {
function withdrawForkedEscalationDeposits(QuestionOutcome outcome, CarriedDepositProof[] memory proofs) external {
require(address(escalationGame) != address(0x0), 'missing escalation');
require(systemState == SystemState.Operational, 'not operational');
BinaryOutcomes.BinaryOutcome questionOutcome = ISecurityPoolForker(securityPoolForker).getQuestionOutcome(this);
require(questionOutcome != BinaryOutcomes.BinaryOutcome.None, 'question not final');
BinaryOutcomes.BinaryOutcome withdrawalOutcome = BinaryOutcomes.BinaryOutcome(uint8(outcome));
require(withdrawalOutcome != BinaryOutcomes.BinaryOutcome.None, 'invalid none');

EscalationGame escalationGameContract = EscalationGame(payable(address(escalationGame)));
address beneficiaryVault = address(0x0);
uint256 totalAmountToWithdraw = 0;
uint256 totalOriginalDepositAmount = 0;
for (uint256 index = 0; index < parentDepositIndexes.length; index++) {
for (uint256 index = 0; index < proofs.length; index++) {
address depositor;
uint256 amountToWithdraw;
uint256 originalDepositAmount;
if (withdrawalOutcome == questionOutcome) {
(depositor, amountToWithdraw, originalDepositAmount) = escalationGame.withdrawImportedForkDeposit(parentDepositIndexes[index], withdrawalOutcome);
} else {
(depositor, originalDepositAmount) = escalationGame.forfeitImportedForkDeposit(parentDepositIndexes[index], withdrawalOutcome);
}
(depositor, amountToWithdraw, originalDepositAmount) = escalationGameContract.withdrawDeposit(proofs[index], withdrawalOutcome);
if (beneficiaryVault == address(0x0)) {
beneficiaryVault = depositor;
}
Expand All @@ -439,11 +436,7 @@ contract SecurityPool is ISecurityPool {
totalAmountToWithdraw += amountToWithdraw;
totalOriginalDepositAmount += originalDepositAmount;
}
if (totalAmountToWithdraw > totalOriginalDepositAmount) {
securityVaults[beneficiaryVault].poolOwnership += repToPoolOwnership(totalAmountToWithdraw - totalOriginalDepositAmount);
} else if (totalAmountToWithdraw < totalOriginalDepositAmount) {
securityVaults[beneficiaryVault].poolOwnership -= repToPoolOwnership(totalOriginalDepositAmount - totalAmountToWithdraw);
}
_applyForkedEscalationSettlement(beneficiaryVault, totalAmountToWithdraw, totalOriginalDepositAmount);
}

////////////////////////////////////////
Expand Down Expand Up @@ -482,11 +475,7 @@ contract SecurityPool is ISecurityPool {
address depositor;
uint256 amountToWithdraw;
uint256 originalDepositAmount;
if (outcome == questionOutcome) {
(depositor, amountToWithdraw, originalDepositAmount) = escalationGame.withdrawDeposit(depositIndexes[index]);
} else {
(depositor, originalDepositAmount) = escalationGame.forfeitLosingDeposit(depositIndexes[index], outcome);
}
(depositor, amountToWithdraw, originalDepositAmount) = escalationGame.withdrawDeposit(depositIndexes[index], outcome);
if (beneficiaryVault == address(0x0)) {
beneficiaryVault = depositor;
}
Expand Down Expand Up @@ -515,6 +504,21 @@ contract SecurityPool is ISecurityPool {
escalationGame = escalationGameFactory.deployEscalationGameFromFork(startBond, nonDecisionThreshold, elapsedAtFork);
}

function initializeForkCarrySnapshot(
bytes32[64][3] memory inheritedCarryPeaks,
uint256[3] memory inheritedCarryLeafCounts,
uint256[3] memory inheritedCarryTotals,
bytes32[3] memory inheritedNullifierRoots
) external onlyForker {
require(address(escalationGame) != address(0x0), 'missing escalation');
EscalationGame(payable(address(escalationGame))).initializeForkCarrySnapshot(
inheritedCarryPeaks,
inheritedCarryLeafCounts,
inheritedCarryTotals,
inheritedNullifierRoots
);
}

function resumeForkedEscalationGame() external onlyForker {
require(address(escalationGame) != address(0x0), 'missing escalation');
escalationGame.resumeFromFork();
Expand Down Expand Up @@ -551,6 +555,15 @@ contract SecurityPool is ISecurityPool {
totalLockedRepInEscalationGame -= repAmount;
}

function _applyForkedEscalationSettlement(address beneficiaryVault, uint256 totalAmountToWithdraw, uint256 totalOriginalDepositAmount) private {
if (beneficiaryVault == address(0x0)) return;
if (totalAmountToWithdraw > totalOriginalDepositAmount) {
securityVaults[beneficiaryVault].poolOwnership += repToPoolOwnership(totalAmountToWithdraw - totalOriginalDepositAmount);
} else if (totalAmountToWithdraw < totalOriginalDepositAmount) {
securityVaults[beneficiaryVault].poolOwnership -= repToPoolOwnership(totalOriginalDepositAmount - totalAmountToWithdraw);
}
}

function _trackVault(address vault) private {
require(vault != address(0x0), 'invalid vault');
if (vaultIndexesPlusOne[vault] != 0) return;
Expand Down
54 changes: 20 additions & 34 deletions solidity/contracts/peripherals/SecurityPoolForker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -209,17 +209,30 @@ contract SecurityPoolForker is ISecurityPoolForker {
function _initializeChildForkedEscalationGameIfNeeded(ISecurityPool parent, ISecurityPool child) private {
ForkData storage parentForkData = forkDataByPool[parent];
if (!parentForkData.unresolvedEscalationAtFork) return;
if (address(child.escalationGame()) != address(0x0)) return;
child.initializeForkedEscalationGame(
parentForkData.escalationStartBondAtFork,
parentForkData.escalationNonDecisionThresholdAtFork,
parentForkData.escalationElapsedAtFork
);
if (address(child.escalationGame()) == address(0x0)) {
child.initializeForkedEscalationGame(
parentForkData.escalationStartBondAtFork,
parentForkData.escalationNonDecisionThresholdAtFork,
parentForkData.escalationElapsedAtFork
);
}
EscalationGame childEscalationGame = EscalationGame(payable(address(child.escalationGame())));
if (!childEscalationGame.forkCarrySnapshotInitialized()) {
EscalationGame parentEscalationGame = EscalationGame(payable(address(parent.escalationGame())));
(bytes32[64][3] memory inheritedCarryPeaks, uint256[3] memory inheritedCarryLeafCounts, uint256[3] memory inheritedCarryTotals, bytes32[3] memory inheritedNullifierRoots) =
parentEscalationGame.getForkCarrySnapshot();
child.initializeForkCarrySnapshot(inheritedCarryPeaks, inheritedCarryLeafCounts, inheritedCarryTotals, inheritedNullifierRoots);
}
if (child.systemState() == SystemState.Operational) {
child.resumeForkedEscalationGame();
}
}

function initializeChildForkedEscalationGameIfNeeded(ISecurityPool parent, ISecurityPool child) external {
require(msg.sender == address(this), 'only self');
_initializeChildForkedEscalationGameIfNeeded(parent, child);
}

function _creditMigratedEscalationPrincipal(ISecurityPool parent, ISecurityPool child, uint256 migratedPrincipal) private {
if (migratedPrincipal == 0) return;
uint256 parentRepAtFork = forkDataByPool[parent].repAtFork;
Expand All @@ -229,27 +242,6 @@ contract SecurityPoolForker is ISecurityPoolForker {
}
}

function _migrateEscalationDeposits(
ISecurityPool parent,
ISecurityPool child,
address vault,
BinaryOutcomes.BinaryOutcome sourceOutcome,
uint256[] memory depositIndexes
) private returns (uint256 migratedPrincipal) {
if (depositIndexes.length == 0) return 0;
EscalationGame escalationGame = parent.escalationGame();
require(address(escalationGame) != address(0x0), 'e4');
for (uint256 index = 0; index < depositIndexes.length; index++) {
(address depositor, uint256 amount, uint256 parentDepositIndex) = escalationGame.exportUnresolvedForkDeposit(depositIndexes[index], sourceOutcome);
require(depositor == vault, 'e5');
parent.clearEscalationLockForForkMigration(vault, amount);
child.addEscalationLockForForkMigration(vault, amount);
child.escalationGame().importForkedDeposit(vault, sourceOutcome, parentDepositIndex, amount);
migratedPrincipal += amount;
}
_creditMigratedEscalationPrincipal(parent, child, migratedPrincipal);
}

function _migrateVaultUnlockedState(ISecurityPool parent, ISecurityPool child, address vault, uint256 lockedRepAlreadyMigrated) private {
uint256 parentRepAtFork = forkDataByPool[parent].repAtFork;
ForkData storage parentForkData = forkDataByPool[parent];
Expand Down Expand Up @@ -352,13 +344,7 @@ contract SecurityPoolForker is ISecurityPoolForker {
_delegateVaultMigrationCall();
}

function migrateVaultWithUnresolvedEscalation(
ISecurityPool,
uint8,
uint256[] memory,
uint256[] memory,
uint256[] memory
) public {
function migrateVaultWithUnresolvedEscalation(ISecurityPool, uint8) public {
_delegateVaultMigrationCall();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import { BinaryOutcomes } from './BinaryOutcomes.sol';
import { SecurityPoolUtils } from './SecurityPoolUtils.sol';
import { SecurityPoolMigrationProxy } from './SecurityPoolMigrationProxy.sol';

interface ISecurityPoolForkerChildEscalationInitializer {
function initializeChildForkedEscalationGameIfNeeded(ISecurityPool parent, ISecurityPool child) external;
}

struct VaultMigrationForkData {
uint256 repAtFork;
UniformPriceDualCapBatchAuction truthAuction;
Expand Down Expand Up @@ -99,17 +103,7 @@ contract SecurityPoolForkerVaultMigrationDelegate {
}

function _initializeChildForkedEscalationGameIfNeeded(ISecurityPool parent, ISecurityPool child) private {
VaultMigrationForkData storage parentForkData = forkDataByPool[parent];
if (!parentForkData.unresolvedEscalationAtFork) return;
if (address(child.escalationGame()) != address(0x0)) return;
child.initializeForkedEscalationGame(
parentForkData.escalationStartBondAtFork,
parentForkData.escalationNonDecisionThresholdAtFork,
parentForkData.escalationElapsedAtFork
);
if (child.systemState() == SystemState.Operational) {
child.resumeForkedEscalationGame();
}
ISecurityPoolForkerChildEscalationInitializer(address(this)).initializeChildForkedEscalationGameIfNeeded(parent, child);
}

function _creditMigratedEscalationPrincipal(ISecurityPool parent, ISecurityPool child, uint256 migratedPrincipal) private {
Expand All @@ -121,27 +115,6 @@ contract SecurityPoolForkerVaultMigrationDelegate {
}
}

function _migrateEscalationDeposits(
ISecurityPool parent,
ISecurityPool child,
address vault,
BinaryOutcomes.BinaryOutcome sourceOutcome,
uint256[] memory depositIndexes
) private returns (uint256 migratedPrincipal) {
if (depositIndexes.length == 0) return 0;
EscalationGame escalationGame = parent.escalationGame();
require(address(escalationGame) != address(0x0), 'e4');
for (uint256 index = 0; index < depositIndexes.length; index++) {
(address depositor, uint256 amount, uint256 parentDepositIndex) = escalationGame.exportUnresolvedForkDeposit(depositIndexes[index], sourceOutcome);
require(depositor == vault, 'e5');
parent.clearEscalationLockForForkMigration(vault, amount);
child.addEscalationLockForForkMigration(vault, amount);
child.escalationGame().importForkedDeposit(vault, sourceOutcome, parentDepositIndex, amount);
migratedPrincipal += amount;
}
_creditMigratedEscalationPrincipal(parent, child, migratedPrincipal);
}

function _migrateVaultUnlockedState(ISecurityPool parent, ISecurityPool child, address vault, uint256 lockedRepAlreadyMigrated) private {
uint256 parentRepAtFork = forkDataByPool[parent].repAtFork;
VaultMigrationForkData storage parentForkData = forkDataByPool[parent];
Expand Down Expand Up @@ -221,27 +194,17 @@ contract SecurityPoolForkerVaultMigrationDelegate {
_migrateVaultUnlockedState(parent, child, msg.sender, 0);
}

function migrateVaultWithUnresolvedEscalation(
ISecurityPool parent,
uint8 childOutcomeIndex,
uint256[] memory invalidDepositIndexes,
uint256[] memory yesDepositIndexes,
uint256[] memory noDepositIndexes
) public {
function migrateVaultWithUnresolvedEscalation(ISecurityPool parent, uint8 childOutcomeIndex) public {
VaultMigrationForkData storage parentForkData = forkDataByPool[parent];
require(parentForkData.unresolvedEscalationAtFork, 'ee');
require(block.timestamp <= zoltar.getForkTime(parent.universeId()) + SecurityPoolUtils.MIGRATION_TIME, 'migration window closed');
parent.updateVaultFees(msg.sender);
(, , , , uint256 parentLockedRepInEscalationGame) = parent.securityVaults(msg.sender);
require(parentLockedRepInEscalationGame > 0, 'ef');
ISecurityPool child = _getOrDeployChildPool(parent, childOutcomeIndex);
uint256 migratedPrincipal = 0;
migratedPrincipal += _migrateEscalationDeposits(parent, child, msg.sender, BinaryOutcomes.BinaryOutcome.Invalid, invalidDepositIndexes);
migratedPrincipal += _migrateEscalationDeposits(parent, child, msg.sender, BinaryOutcomes.BinaryOutcome.Yes, yesDepositIndexes);
migratedPrincipal += _migrateEscalationDeposits(parent, child, msg.sender, BinaryOutcomes.BinaryOutcome.No, noDepositIndexes);
require(migratedPrincipal > 0, 'f0');
(, , , , uint256 remainingLockedRep) = parent.securityVaults(msg.sender);
require(remainingLockedRep == 0, 'f1');
_migrateVaultUnlockedState(parent, child, msg.sender, migratedPrincipal);
parent.clearEscalationLockForForkMigration(msg.sender, parentLockedRepInEscalationGame);
child.addEscalationLockForForkMigration(msg.sender, parentLockedRepInEscalationGame);
_creditMigratedEscalationPrincipal(parent, child, parentLockedRepInEscalationGame);
_migrateVaultUnlockedState(parent, child, msg.sender, parentLockedRepInEscalationGame);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import { EscalationGame } from '../EscalationGame.sol';
contract EscalationGameFactory {
function deployEscalationGame(uint256 startBond, uint256 _nonDecisionThreshold) external returns (EscalationGame) {
ISecurityPool securityPool = ISecurityPool(payable(msg.sender));
EscalationGame game = new EscalationGame{ salt: bytes32(uint256(0x0)) }(securityPool);
game.start(startBond, _nonDecisionThreshold);
return game;
EscalationGame gameImplementation = new EscalationGame{ salt: bytes32(uint256(0x0)) }(securityPool);
gameImplementation.start(startBond, _nonDecisionThreshold);
return EscalationGame(payable(address(gameImplementation)));
}

function deployEscalationGameFromFork(uint256 startBond, uint256 nonDecisionThreshold, uint256 elapsedAtFork) external returns (EscalationGame) {
ISecurityPool securityPool = ISecurityPool(payable(msg.sender));
EscalationGame game = new EscalationGame{ salt: bytes32(uint256(0x0)) }(securityPool);
game.startFromFork(startBond, nonDecisionThreshold, elapsedAtFork);
return game;
EscalationGame gameImplementation = new EscalationGame{ salt: bytes32(uint256(0x0)) }(securityPool);
gameImplementation.startFromFork(startBond, nonDecisionThreshold, elapsedAtFork);
return EscalationGame(payable(address(gameImplementation)));
}
}
4 changes: 3 additions & 1 deletion solidity/contracts/peripherals/interfaces/ISecurityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { IShareToken } from './IShareToken.sol';
import { ReputationToken } from '../../ReputationToken.sol';
import { SecurityPoolOracleCoordinator } from '../SecurityPoolOracleCoordinator.sol';
import { EscalationGame } from '../EscalationGame.sol';
import { CarriedDepositProof } from '../EscalationGame.sol';
import { ZoltarQuestionData } from '../../ZoltarQuestionData.sol';

struct SecurityVault {
Expand Down Expand Up @@ -77,7 +78,7 @@ interface ISecurityPool {
function performWithdrawRep(address vault, uint256 repAmount) external;
function depositRep(uint256 repAmount) external;
function redeemRep(address vault) external;
function withdrawForkedEscalationDeposits(QuestionOutcome outcome, uint256[] memory parentDepositIndexes) external;
function withdrawForkedEscalationDeposits(QuestionOutcome outcome, CarriedDepositProof[] memory proofs) external;
function performLiquidation(address callerVault, address targetVaultAddress, uint256 debtAmount, uint256 snapshotTargetOwnership, uint256 snapshotTargetAllowance, uint256 snapshotTotalRep, uint256 snapshotDenominator) external;
function performSetSecurityBondsAllowance(address callerVault, uint256 amount) external;

Expand All @@ -86,6 +87,7 @@ interface ISecurityPool {

function escalationGame() external view returns (EscalationGame);
function initializeForkedEscalationGame(uint256 startBond, uint256 nonDecisionThreshold, uint256 elapsedAtFork) external;
function initializeForkCarrySnapshot(bytes32[64][3] memory inheritedCarryPeaks, uint256[3] memory inheritedCarryLeafCounts, uint256[3] memory inheritedCarryTotals, bytes32[3] memory inheritedNullifierRoots) external;
function resumeForkedEscalationGame() external;
function setAwaitingForkContinuation(bool shouldAwait) external;
function activateForkMode() external;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,7 @@ interface ISecurityPoolForker {
function migrateRepToZoltar(ISecurityPool securityPool, uint256[] memory outcomeIndices) external;
function createChildUniverse(ISecurityPool securityPool, uint8 outcomeIndex) external;
function migrateVault(ISecurityPool securityPool, uint8 outcomeIndex) external;
function migrateVaultWithUnresolvedEscalation(
ISecurityPool securityPool,
uint8 childOutcomeIndex,
uint256[] memory invalidDepositIndexes,
uint256[] memory yesDepositIndexes,
uint256[] memory noDepositIndexes
) external;
function migrateVaultWithUnresolvedEscalation(ISecurityPool securityPool, uint8 childOutcomeIndex) external;
function migrateFromEscalationGame(ISecurityPool securityPool, address vault, BinaryOutcomes.BinaryOutcome outcomeIndex, uint256[] memory depositIndexes) external;
function startTruthAuction(ISecurityPool securityPool) external;
function finalizeTruthAuction(ISecurityPool securityPool) external;
Expand Down
Loading
Loading