// Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; import "../state/Machine.sol"; import "../state/GlobalState.sol"; library ChallengeLib { using MachineLib for Machine; using ChallengeLib for Challenge; /// @dev It's assumed that that uninitialzed challenges have mode NONE enum ChallengeMode { NONE, BLOCK, EXECUTION } struct Participant { address addr; uint256 timeLeft; } struct Challenge { Participant current; Participant next; uint256 lastMoveTimestamp; bytes32 wasmModuleRoot; bytes32 challengeStateHash; uint64 maxInboxMessages; ChallengeMode mode; } struct SegmentSelection { uint256 oldSegmentsStart; uint256 oldSegmentsLength; bytes32[] oldSegments; uint256 challengePosition; } function timeUsedSinceLastMove(Challenge storage challenge) internal view returns (uint256) { return block.timestamp - challenge.lastMoveTimestamp; } function isTimedOut(Challenge storage challenge) internal view returns (bool) { return challenge.timeUsedSinceLastMove() > challenge.current.timeLeft; } function getStartMachineHash(bytes32 globalStateHash, bytes32 wasmModuleRoot) internal pure returns (bytes32) { // Start the value stack with the function call ABI for the entrypoint Value[] memory startingValues = new Value[](3); startingValues[0] = ValueLib.newRefNull(); startingValues[1] = ValueLib.newI32(0); startingValues[2] = ValueLib.newI32(0); ValueArray memory valuesArray = ValueArray({inner: startingValues}); ValueStack memory values = ValueStack({proved: valuesArray, remainingHash: 0}); ValueStack memory internalStack; StackFrameWindow memory frameStack; Machine memory mach = Machine({ status: MachineStatus.RUNNING, valueStack: values, internalStack: internalStack, frameStack: frameStack, globalStateHash: globalStateHash, moduleIdx: 0, functionIdx: 0, functionPc: 0, modulesRoot: wasmModuleRoot }); return mach.hash(); } function getEndMachineHash(MachineStatus status, bytes32 globalStateHash) internal pure returns (bytes32) { if (status == MachineStatus.FINISHED) { return keccak256(abi.encodePacked("Machine finished:", globalStateHash)); } else if (status == MachineStatus.ERRORED) { return keccak256(abi.encodePacked("Machine errored:")); } else if (status == MachineStatus.TOO_FAR) { return keccak256(abi.encodePacked("Machine too far:")); } else { revert("BAD_BLOCK_STATUS"); } } function extractChallengeSegment(SegmentSelection calldata selection) internal pure returns (uint256 segmentStart, uint256 segmentLength) { uint256 oldChallengeDegree = selection.oldSegments.length - 1; segmentLength = selection.oldSegmentsLength / oldChallengeDegree; // Intentionally done before challengeLength is potentially added to for the final segment segmentStart = selection.oldSegmentsStart + segmentLength * selection.challengePosition; if (selection.challengePosition == selection.oldSegments.length - 2) { segmentLength += selection.oldSegmentsLength % oldChallengeDegree; } } function hashChallengeState( uint256 segmentsStart, uint256 segmentsLength, bytes32[] memory segments ) internal pure returns (bytes32) { return keccak256(abi.encodePacked(segmentsStart, segmentsLength, segments)); } function blockStateHash(MachineStatus status, bytes32 globalStateHash) internal pure returns (bytes32) { if (status == MachineStatus.FINISHED) { return keccak256(abi.encodePacked("Block state:", globalStateHash)); } else if (status == MachineStatus.ERRORED) { return keccak256(abi.encodePacked("Block state, errored:", globalStateHash)); } else if (status == MachineStatus.TOO_FAR) { return keccak256(abi.encodePacked("Block state, too far:")); } else { revert("BAD_BLOCK_STATUS"); } } }