Skip to content
Open
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
102 changes: 82 additions & 20 deletions pkg/contracts/src/proofs/WorldChainProofSystemFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ contract WorldChainProofSystemFactory {
error ParentGameBlacklisted(address parentRef);
error InvalidL2BlockNumber(uint256 expectedL2BlockNumber, uint256 actualL2BlockNumber);
error TargetBlockNotNewer(uint256 currentL2BlockNumber, uint256 targetL2BlockNumber);
error L1OriginInFuture(uint256 l1OriginNumber, uint256 currentBlock);
error L1OriginTooOld(uint256 l1OriginNumber, uint256 currentBlock);
error L1OriginHashMismatch(bytes32 claimed, bytes32 actual);

address public constant EIP2935_CONTRACT = 0x0000F90827F1C53a10cb7A02335B175320002935;
uint256 public constant BLOCKHASH_WINDOW = 256;
uint256 public constant EIP2935_WINDOW = 8191;

event GameCreated(
bytes32 indexed proposalKey,
Expand All @@ -44,6 +51,15 @@ contract WorldChainProofSystemFactory {
uint256 l1OriginNumber;
}

struct ProposalRequest {
address parentRef;
bytes32 rootClaim;
uint256 l2BlockNumber;
bytes32 l1OriginHash;
uint256 l1OriginNumber;
uint8 laneId;
}

WorldChainProofLib.Domain public domain;
bytes32 public immutable domainHash;
IWorldChainAnchorStateRegistry public immutable anchorStateRegistry;
Expand Down Expand Up @@ -107,15 +123,31 @@ contract WorldChainProofSystemFactory {
return allGames[index];
}

function propose(address parentRef, bytes32 rootClaim, uint256 l2BlockNumber)
external
payable
returns (address game, bytes32 id)
{
uint256 l1OriginNumber = block.number == 0 ? 0 : block.number - 1;
bytes32 l1OriginHash = block.number == 0 ? bytes32(0) : blockhash(l1OriginNumber);
function propose(
address parentRef,
bytes32 rootClaim,
uint256 l2BlockNumber,
bytes32 l1OriginHash,
uint256 l1OriginNumber,
uint8 laneId,
bytes calldata proof
) external payable returns (address game, bytes32 id) {
return _propose(
ProposalRequest({
parentRef: parentRef,
rootClaim: rootClaim,
l2BlockNumber: l2BlockNumber,
l1OriginHash: l1OriginHash,
l1OriginNumber: l1OriginNumber,
laneId: laneId
}),
proof
);
}

bytes32 key = WorldChainProofLib.proposalKey(domainHash, parentRef, rootClaim, l2BlockNumber);
function _propose(ProposalRequest memory request, bytes calldata proof) private returns (address game, bytes32 id) {
bytes32 key =
WorldChainProofLib.proposalKey(domainHash, request.parentRef, request.rootClaim, request.l2BlockNumber);
address existing = games[key];
uint256 attempt;
if (existing != address(0)) {
Expand All @@ -132,9 +164,18 @@ contract WorldChainProofSystemFactory {
attempt = existingGame.attempt() + 1;
}

(bytes32 startingRootClaim, uint256 startingL2BlockNumber) = _validateParent(parentRef, l2BlockNumber);
(bytes32 startingRootClaim, uint256 startingL2BlockNumber) =
_validateParent(request.parentRef, request.l2BlockNumber);
_validateL1Origin(request.l1OriginHash, request.l1OriginNumber);

id = WorldChainProofLib.rootId(domainHash, parentRef, rootClaim, l2BlockNumber, l1OriginHash, l1OriginNumber);
id = WorldChainProofLib.rootId(
domainHash,
request.parentRef,
request.rootClaim,
request.l2BlockNumber,
request.l1OriginHash,
request.l1OriginNumber
);

game = address(
new WorldChainProofSystemGame{value: msg.value}(
Expand All @@ -143,13 +184,13 @@ contract WorldChainProofSystemFactory {
anchorStateRegistry: address(anchorStateRegistry),
attempt: attempt,
proposer: msg.sender,
parentRef: parentRef,
parentRef: request.parentRef,
startingRootClaim: startingRootClaim,
startingL2BlockNumber: startingL2BlockNumber,
rootClaim: rootClaim,
l2BlockNumber: l2BlockNumber,
l1OriginHash: l1OriginHash,
l1OriginNumber: l1OriginNumber
rootClaim: request.rootClaim,
l2BlockNumber: request.l2BlockNumber,
l1OriginHash: request.l1OriginHash,
l1OriginNumber: request.l1OriginNumber
}),
WorldChainProofSystemGame.ActivationConfig({
domainHash: domainHash,
Expand All @@ -165,6 +206,8 @@ contract WorldChainProofSystemFactory {
})
)
);
WorldChainProofSystemGame(payable(game)).submitInitialProofLane(request.laneId, proof);

games[key] = game;
isFactoryGame[game] = true;
allGames.push(game);
Expand All @@ -175,11 +218,11 @@ contract WorldChainProofSystemFactory {
rootId: id,
game: game,
proposer: msg.sender,
rootClaim: rootClaim,
l2BlockNumber: l2BlockNumber,
parentRef: parentRef,
l1OriginHash: l1OriginHash,
l1OriginNumber: l1OriginNumber
rootClaim: request.rootClaim,
l2BlockNumber: request.l2BlockNumber,
parentRef: request.parentRef,
l1OriginHash: request.l1OriginHash,
l1OriginNumber: request.l1OriginNumber
})
);
}
Expand Down Expand Up @@ -252,6 +295,25 @@ contract WorldChainProofSystemFactory {
}
}

function _validateL1Origin(bytes32 l1OriginHash, uint256 l1OriginNumber) private view {
if (l1OriginNumber >= block.number) revert L1OriginInFuture(l1OriginNumber, block.number);

uint256 blockAge = block.number - l1OriginNumber;
bytes32 actualHash;
if (blockAge <= BLOCKHASH_WINDOW) {
actualHash = blockhash(l1OriginNumber);
} else if (blockAge <= EIP2935_WINDOW) {
(bool success, bytes memory result) = EIP2935_CONTRACT.staticcall(abi.encode(l1OriginNumber));
if (!success || result.length != 32) revert L1OriginTooOld(l1OriginNumber, block.number);
actualHash = abi.decode(result, (bytes32));
} else {
revert L1OriginTooOld(l1OriginNumber, block.number);
}

if (actualHash == bytes32(0)) revert L1OriginTooOld(l1OriginNumber, block.number);
if (actualHash != l1OriginHash) revert L1OriginHashMismatch(l1OriginHash, actualHash);
}

function _emitGameCreated(GameCreatedLog memory log) private {
emit GameCreated(
log.proposalKey,
Expand Down
19 changes: 19 additions & 0 deletions pkg/contracts/src/proofs/WorldChainProofSystemGame.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ contract WorldChainProofSystemGame is ReentrancyGuardTransient {
error DuplicateChallenge(address challenger);
error InvalidLane(uint8 lane);
error InvalidProof(WorldChainProofLib.ProofLane lane, bytes32 rootId);
error EmptyProof();
error NotFactory(address caller);
error InitialProofAlreadySubmitted();
error NoClaim(address recipient);
error TransferFailed(address recipient, uint256 amount);

Expand Down Expand Up @@ -212,6 +215,22 @@ contract WorldChainProofSystemGame is ReentrancyGuardTransient {
if (block.timestamp >= proofDeadline) {
revert ProofPeriodElapsed(block.timestamp, proofDeadline);
}
_submitProofLane(laneId, proof);
}

/// @notice Records the mandatory proof supplied atomically while the factory creates this game.
function submitInitialProofLane(uint8 laneId, bytes calldata proof) external {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cant this be internal and on propose? no reason to have it on ABI / external if it's required only for proposing right??

if (msg.sender != factory) revert NotFactory(msg.sender);
if (state != WorldChainProofLib.RootState.PROPOSED) {
revert InvalidState(WorldChainProofLib.RootState.PROPOSED, state);
}
if (proofBitmap != 0) revert InitialProofAlreadySubmitted();
if (proof.length == 0) revert EmptyProof();

_submitProofLane(laneId, proof);
}

function _submitProofLane(uint8 laneId, bytes calldata proof) internal {
if (laneId >= PROOF_LANE_COUNT) revert InvalidLane(laneId);

WorldChainProofLib.ProofLane lane = WorldChainProofLib.ProofLane(laneId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,15 @@ interface IWorldChainProofSystemFactory {
view
returns (uint256 chainId, uint256 proofSystemVersion, bytes32 rollupConfigHash, uint256 blockInterval);

function propose(
address parentRef,
bytes32 rootClaim,
uint256 l2BlockNumber,
bytes32 l1OriginHash,
uint256 l1OriginNumber,
uint8 laneId,
bytes calldata proof
) external payable returns (address game, bytes32 rootId);

function isFactoryGame(address game) external view returns (bool);
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ interface IWorldChainProofSystemGame {
/// @dev The challenger, defender/prover-service automation, or keepers can call this after resolution;
/// the caller cannot redirect funds away from `recipient`.
function withdraw(address payable recipient) external;
function submitInitialProofLane(uint8 laneId, bytes calldata proof) external;
}
Loading
Loading