### Start op-challenger Monitoring with Golang Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/05-op-challenger.md Initiates the monitoring component of op-challenger. It subscribes to L1 block changes and processes them to manage the Fault Dispute Game. This function ensures that monitoring is started only once. ```Go func (m *gameMonitor) onNewL1Head(ctx context.Context, sig eth.L1BlockRef) { m.clock.SetTime(sig.Time) if err := m.progressGames(ctx, sig.Hash, sig.Number); err != nil { m.logger.Error("Failed to progress games", "err", err) } if err := m.preimages.Schedule(sig.Hash, sig.Number); err != nil { m.logger.Error("Failed to validate large preimages", "err", err) } } func (m *gameMonitor) resubscribeFunction() event.ResubscribeErrFunc { // The ctx is cancelled as soon as the subscription is returned, // but is only used to create the subscription, and does not affect the returned subscription. return func(ctx context.Context, err error) (event.Subscription, error) { if err != nil { m.logger.Warn("resubscribing after failed L1 subscription", "err", err) } return eth.WatchHeadChanges(ctx, m.l1Source, m.onNewL1Head) } } func (m *gameMonitor) StartMonitoring() { m.runState.Lock() defer m.runState.Unlock() if m.l1HeadsSub != nil { return // already started } m.l1HeadsSub = event.ResubscribeErr(time.Second*10, m.resubscribeFunction()) } ``` -------------------------------- ### Run L2 State Derivation in Op-Program Client (Go) Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/04-op-program.md Implements the execution process of L2 state changes within the op-stack. It initializes L1 and L2 clients, starts a derivation driver, and iterates through derivation steps until EOF is reached, finally validating the L2 claim. ```go func runDerivation(logger log.Logger, cfg *rollup.Config, l2Cfg *params.ChainConfig, l1Head common.Hash, l2OutputRoot common.Hash, l2Claim common.Hash, l2ClaimBlockNum uint64, l1Oracle l1.Oracle, l2Oracle l2.Oracle) error { l1Source := l1.NewOracleL1Client(logger, l1Oracle, l1Head) engineBackend, err := l2.NewOracleBackedL2Chain(logger, l2Oracle, l2Cfg, l2OutputRoot) if err != nil { return fmt.Errorf("failed to create oracle-backed L2 chain: %w", err) } l2Source := l2.NewOracleEngine(cfg, logger, engineBackend) logger.Info("Starting derivation") d := cldr.NewDriver(logger, cfg, l1Source, l2Source, l2ClaimBlockNum) for { if err = d.Step(context.Background()); errors.Is(err, io.EOF) { break } else if err != nil { return err } } return d.ValidateClaim(l2ClaimBlockNum, eth.Bytes32(l2Claim)) } ``` -------------------------------- ### Execute a Single Step in Cannon Off-Chain Execution (Golang) Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/03-cannon.md The Step() function in `instrumented.go` orchestrates a single execution step for the MIPS emulator. It handles the setup of witness data for on-chain verification when the `proof` flag is true, resets memory trackers, and calls `mipsStep()` to process the actual instruction. It also appends memory proofs and preimage data to the witness if required. ```golang func (m *InstrumentedState) Step(proof bool) (wit *mipsevm.StepWitness, err error) { m.preimageOracle.Reset() m.memoryTracker.Reset(proof) if proof { insnProof := m.state.Memory.MerkleProof(m.state.Cpu.PC) encodedWitness, stateHash := m.state.EncodeWitness() wit = &mipsevm.StepWitness{ State: encodedWitness, StateHash: stateHash, ProofData: insnProof[:], } } err = m.mipsStep() if err != nil { return nil, err } if proof { memProof := m.memoryTracker.MemProof() wit.ProofData = append(wit.ProofData, memProof[:]...) lastPreimageKey, lastPreimage, lastPreimageOffset := m.preimageOracle.LastPreimage() if lastPreimageOffset != ^uint32(0) { wit.PreimageOffset = lastPreimageOffset wit.PreimageKey = lastPreimageKey wit.PreimageValue = lastPreimage } } return } ``` -------------------------------- ### Get Preimage Data with Retry Logic (Golang) Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/04-op-program.md Retrieves pre-image data based on a given key from the key-value store. It includes a retry mechanism that attempts to prefetch data if the key is not found, handling cases where pre-images might be temporarily unavailable. This function is central to the Host module's role in supplying data to Cannon. ```golang func (p *Prefetcher) GetPreimage(ctx context.Context, key common.Hash) ([]byte, error) { p.logger.Trace("Pre-image requested", "key", key) pre, err := p.kvStore.Get(key) // Use a loop to keep retrying the prefetch as long as the key is not found // This handles the case where the prefetch downloads a preimage, but it is then deleted unexpectedly // before we get to read it. for errors.Is(err, kvstore.ErrNotFound) && p.lastHint != "" { hint := p.lastHint if err := p.prefetch(ctx, hint); err != nil { return nil, fmt.Errorf("prefetch failed: %w", err) } pre, err = p.kvStore.Get(key) if err != nil { p.logger.Error("Fetched pre-images for last hint but did not find required key", "hint", hint, "key", key) } } return pre, err } ``` -------------------------------- ### Generate Proof Data using VM - Go Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/05-op-challenger.md This Go function initiates the Cannon VM to generate state data and proof data for a step in the fault game. It constructs command-line arguments for the VM, specifying input/output paths, proof formats, and other configuration parameters. Dependencies include 'context', 'strconv', 'filepath', and 'math'. ```Go func (e *Executor) DoGenerateProof(ctx context.Context, dir string, begin uint64, end uint64, extraVmArgs ...string) error { …… args := []string{ "run", "--input", start, "--output", lastGeneratedState, "--meta", "", "--info-at", "%" + strconv.FormatUint(uint64(e.cfg.InfoFreq), 10), "--proof-at", "=" + strconv.FormatUint(end, 10), "--proof-fmt", filepath.Join(proofDir, "%d.json.gz"), "--snapshot-at", "%" + strconv.FormatUint(uint64(e.cfg.SnapshotFreq), 10), "--snapshot-fmt", filepath.Join(snapshotDir, "%d.json.gz"), } if end < math.MaxUint64 { args = append(args, "--stop-at", "=" + strconv.FormatUint(end+1, 10)) } if e.cfg.DebugInfo { args = append(args, "--debug-info", filepath.Join(dataDir, debugFilename)) } args = append(args, extraVmArgs...) args = append(args, "--", e.cfg.Server, "--server", "--l1", e.cfg.L1, "--l1.beacon", e.cfg.L1Beacon, "--l2", e.cfg.L2, "--datadir", dataDir, "--l1.head", e.inputs.L1Head.Hex(), "--l2.head", e.inputs.L2Head.Hex(), "--l2.outputroot", e.inputs.L2OutputRoot.Hex(), "--l2.claim", e.inputs.L2Claim.Hex(), "--l2.blocknumber", e.inputs.L2BlockNumber.Text(10), ) …… err = e.cmdExecutor(ctx, e.logger.New("proof", end), e.cfg.VmBin, args...) …… return err } ``` -------------------------------- ### Create Bidirectional Channel for Host-Cannon Communication (Golang) Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/04-op-program.md Initializes bidirectional communication channels between the Host and Cannon modules. It sets up file descriptors for inter-process communication, essential for passing data and hints during execution. Dependencies include the 'preimage' package for channel creation and 'os' for file operations. ```golang func NewProcessPreimageOracle(name string, args []string) (*ProcessPreimageOracle, error) { if name == "" { return &ProcessPreimageOracle{}, nil } pClientRW, pOracleRW, err := preimage.CreateBidirectionalChannel() if err != nil { return nil, err } hClientRW, hOracleRW, err := preimage.CreateBidirectionalChannel() if err != nil { return nil, err } cmd := exec.Command(name, args...) // nosemgrep cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.ExtraFiles = []*os.File{ hOracleRW.Reader(), hOracleRW.Writer(), pOracleRW.Reader(), pOracleRW.Writer(), } ``` -------------------------------- ### Cannon CLI Help Flags Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/03-cannon.md Displays the available command-line flags for the `cannon run` command, which controls off-chain VM execution and proof generation. These flags allow customization of VM type, input/output paths, and conditions for generating proofs and snapshots. ```shell ./bin/cannon run -h NAME: cannon run - Run VM step(s) and generate proof data to replicate onchain. USAGE: cannon run [command options] [arguments...] DESCRIPTION: Run VM step(s) and generate proof data to replicate onchain. See flags to match when to output a proof, a snapshot, or to stop early. OPTIONS: --type value VM type to run. Options are 'cannon' (default) (default: "cannon") --input value path of input JSON state. Stdin if left empty. (default: "state.json") --output value path of output JSON state. Not written if empty, use - to write to Stdout. (default: "out.json") --proof-at value step pattern to output proof at: 'never' (default), 'always', '=123' at exactly step 123, '%123' for every 123 steps --proof-fmt value format for proof data output file names. Proof data is written to stdout if -. (default: "proof-%d.json") --snapshot-at value step pattern to output snapshots at: 'never' (default), 'always', '=123' at exactly step 123, '%123' for every 123 steps --snapshot-fmt value format for snapshot output file names. (default: "state-%d.json") --stop-at value step pattern to stop at: 'never' (default), 'always', '=123' at exactly step 123, '%123' for every 123 steps --stop-at-preimage value stop at the first preimage request matching this key --stop-at-preimage-type value stop at the first preimage request matching this type --stop-at-preimage-larger-than value stop at the first step that requests a preimage larger than the specified size (in bytes) --meta value path to metadata file for symbol lookup for enhanced debugging info during execution. (default: "meta.json") --info-at value step pattern to print info at: 'never' (default), 'always', '=123' at exactly step 123, '%123' for every 123 steps (default: %100000) --pprof.cpu enable pprof cpu profiling (default: false) --debug enable debug mode, which includes stack traces and other debug info in the output. Requires --meta. (default: false) --debug-info value path to write debug info to --help, -h show help ``` -------------------------------- ### Cannon Integration in op-challenger (Go) Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/03-cannon.md Demonstrates how the `cannon run` command is invoked within the `GenerateProof` function of the op-challenger Go executor. It shows how dynamic arguments like step counts and output paths are constructed for Cannon based on execution context. ```go func (e *Executor) GenerateProof(ctx context.Context, dir string, i uint64) error { snapshotDir := filepath.Join(dir, snapsDir) start, err := e.selectSnapshot(e.logger, snapshotDir, e.absolutePreState, i) if err != nil { return fmt.Errorf("find starting snapshot: %w", err) } proofDir := filepath.Join(dir, proofsDir) dataDir := filepath.Join(dir, preimagesDir) lastGeneratedState := filepath.Join(dir, finalState) args := []string{ "run", "--input", start, "--output", lastGeneratedState, "--meta", "", "--info-at", "%" + strconv.FormatUint(uint64(e.infoFreq), 10), "--proof-at", "=" + strconv.FormatUint(i, 10), "--proof-fmt", filepath.Join(proofDir, "%d.json.gz"), "--snapshot-at", "%" + strconv.FormatUint(uint64(e.snapshotFreq), 10), "--snapshot-fmt", filepath.Join(snapshotDir, "%d.json.gz"), } if i < math.MaxUint64 { args = append(args, "--stop-at", "="+strconv.FormatUint(i+1, 10)) } // ... rest of the function ``` -------------------------------- ### Solidity: MIPS.sol step() function for CANNON execution Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/03-cannon.md Implements the main step() function in MIPS.sol for CANNON's on-chain execution. It includes data verification, unpacking state data using assembly, and initializes instruction fetching. Dependencies include MIPSMemory library. ```solidity function step(bytes calldata _stateData, bytes calldata _proof, bytes32 _localContext) public returns (bytes32) { unchecked { State memory state; //-------------------- Part 1 start -------------------- // Packed calldata is ~6 times smaller than state size assembly { if iszero(eq(state, 0x80)) { // expected state mem offset check revert(0, 0) } if iszero(eq(mload(0x40), shl(5, 48))) { // expected memory check revert(0, 0) } if iszero(eq(_stateData.offset, 132)) { // 32*4+4=132 expected state data offset revert(0, 0) } if iszero(eq(_proof.offset, STEP_PROOF_OFFSET)) { // 132+32+256=420 expected proof offset revert(0, 0) } //-------------------- Part 1 end ---------------------- //-------------------- Part 2 start -------------------- function putField(callOffset, memOffset, size) -> callOffsetOut, memOffsetOut { // calldata is packed, thus starting left-aligned, shift-right to pad and right-align let w := shr(shl(3, sub(32, size)), calldataload(callOffset)) mstore(memOffset, w) callOffsetOut := add(callOffset, size) memOffsetOut := add(memOffset, 32) } // Unpack state from calldata into memory let c := _stateData.offset // calldata offset let m := 0x80 // mem offset c, m := putField(c, m, 32) // memRoot c, m := putField(c, m, 32) // preimageKey c, m := putField(c, m, 4) // preimageOffset c, m := putField(c, m, 4) // pc c, m := putField(c, m, 4) // nextPC c, m := putField(c, m, 4) // lo c, m := putField(c, m, 4) // hi c, m := putField(c, m, 4) // heap c, m := putField(c, m, 1) // exitCode c, m := putField(c, m, 1) // exited c, m := putField(c, m, 8) // step // Unpack register calldata into memory mstore(m, add(m, 32)) // offset to registers m := add(m, 32) for { let i := 0 } lt(i, 32) { i := add(i, 1) } { c, m := putField(c, m, 4) } } //-------------------- Part 2 end ---------------------- // Don't change state once exited if (state.exited) { return outputState(); } state.step += 1; //-------------------- Part 3 start -------------------- // instruction fetch uint256 insnProofOffset = MIPSMemory.memoryProofOffset(STEP_PROOF_OFFSET, 0); ``` -------------------------------- ### Solidity step() function for Dispute Resolution Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/02-fault-dispute-game.md The step() function in FaultDisputeGame.sol allows a participant to execute a step in the dispute game. It takes claim index, attack/defend status, state data, and proof as input. It determines the pre-state and post-state based on whether it's an attack or defense, executes the step in a VM, and verifies the result against the expected post-state. Reverts if the step is valid when it should be invalid, or vice versa. ```solidity function step( uint256 _claimIndex, bool _isAttack, bytes calldata _stateData, bytes calldata _proof ) public virtual { ClaimData storage parent = claimData[_claimIndex]; Position parentPos = parent.position; Position stepPos = parentPos.move(_isAttack); // Determine the expected pre & post states of the step. Claim preStateClaim; ClaimData storage postState; //----------part1 start---------- if (_isAttack) { preStateClaim = (stepPos.indexAtDepth() % (1 << (MAX_GAME_DEPTH - SPLIT_DEPTH))) == 0 ? ABSOLUTE_PRESTATE : _findTraceAncestor(Position.wrap(parentPos.raw() - 1), parent.parentIndex, false).claim; postState = parent; } else { preStateClaim = parent.claim; postState = _findTraceAncestor(Position.wrap(parentPos.raw() + 1), parent.parentIndex, false); } //----------part1 end---------- if (keccak256(_stateData) << 8 != preStateClaim.raw() << 8) revert InvalidPrestate(); // Compute the local preimage context for the step. Hash uuid = _findLocalContext(_claimIndex); //----------part2 start---------- bool validStep = VM.step(_stateData, _proof, uuid.raw()) == postState.claim.raw(); bool parentPostAgree = (parentPos.depth() - postState.position.depth()) % 2 == 0; //----------part2 end---------- //----------part3 start---------- if (parentPostAgree == validStep) revert ValidStep(); parent.counteredBy = msg.sender; //----------part3 end---------- } ``` -------------------------------- ### Create Dispute Game using DisputeGameFactory in Solidity Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/02-fault-dispute-game.md This function creates a new dispute game. It takes the game type, root claim, and extra data as input. It uses minimal proxy contracts for efficiency and ensures that each game can only be created once by checking for existing game UUIDs. ```solidity function create( GameType _gameType, Claim _rootClaim, bytes calldata _extraData ) external payable returns (IDisputeGame proxy_) { … proxy_ = IDisputeGame(address(impl).clone(abi.encodePacked(msg.sender, _rootClaim, parentHash, _extraData))); proxy_.initialize{ value: msg.value }(); // Compute the unique identifier for the dispute game. Hash uuid = getGameUUID(_gameType, _rootClaim, _extraData); // If a dispute game with the same UUID already exists, revert. if (GameId.unwrap(_disputeGames[uuid]) != bytes32(0)) revert GameAlreadyExists(uuid); // Pack the game ID. GameId id = LibGameId.pack(_gameType, Timestamp.wrap(uint64(block.timestamp)), address(proxy_)); _disputeGames[uuid] = id; _disputeGameList.push(id); } ``` -------------------------------- ### Create Preimage Channel for Detached Context (Golang) Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/04-op-program.md Creates a FileChannel for the preimage oracle in a detached context, used by subprocesses. It maps specific file descriptors (PClientRFd, PClientWFd) to read and write operations for preimage data exchange. This function is crucial for enabling communication between different parts of the program. ```golang // CreatePreimageChannel returns a FileChannel for the preimage oracle in a detached context func CreatePreimageChannel() oppio.FileChannel { r := os.NewFile(PClientRFd, "preimage-oracle-read") w := os.NewFile(PClientWFd, "preimage-oracle-write") return oppio.NewReadWritePair(r, w) } ``` -------------------------------- ### Execute a Move in FaultDisputeGame in Solidity Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/02-fault-dispute-game.md The `move` function allows a player to make a move in a dispute game. It calculates the next move's position, verifies the bond amount and clock time, and stores the new claim data. This function is crucial for progressing the game state. ```solidity function move(Claim _disputed, uint256 _challengeIndex, Claim _claim, bool _isAttack) public payable virtual { ClaimData memory parent = claimData[_challengeIndex]; //----------part1 start---------- Position parentPos = parent.position; Position nextPosition = parentPos.move(_isAttack); uint256 nextPositionDepth = nextPosition.depth(); //----------part1 end---------- //----------part2 start---------- if (getRequiredBond(nextPosition) != msg.value) revert IncorrectBondAmount(); if (nextDuration.raw() == MAX_CLOCK_DURATION.raw()) revert ClockTimeExceeded(); if (nextDuration.raw() > MAX_CLOCK_DURATION.raw() - CLOCK_EXTENSION.raw()) { Clock nextClock = LibClock.wrap(nextDuration, Timestamp.wrap(uint64(block.timestamp))); //----------part2 end---------- //----------part3 start---------- claimData.push( ClaimData({ parentIndex: uint32(_challengeIndex), // This is updated during subgame resolution counteredBy: address(0), claimant: msg.sender, bond: uint128(msg.value), claim: _claim, position: nextPosition, clock: nextClock }) ); // Update the subgame rooted at the parent claim. subgames[_challengeIndex].push(claimData.length - 1); // Deposit the bond. WETH.deposit{ value: msg.value }(); //----------part3 end---------- } ``` -------------------------------- ### Solidity: Initialize Checkpoint for Main Claim Resolution Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/02-fault-dispute-game.md This code handles the initialization of a checkpoint for resolving the main claim. It checks if the checkpoint has been initialized. If not, it sets the leftmost position to the maximum u128 value and marks the checkpoint as complete. It also sets _numToResolve if it's zero. ```Solidity // If the checkpoint does not currently exist, initialize the current left most position as max u128. if (!checkpoint.initialCheckpointComplete) { checkpoint.leftmostPosition = Position.wrap(type(uint128).max); checkpoint.initialCheckpointComplete = true; if (_numToResolve == 0) _numToResolve = challengeIndicesLen; } ``` -------------------------------- ### Execute MIPS Core Step Logic Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/03-cannon.md This snippet executes the core MIPS step logic for a given instruction. It retrieves CPU scalar values, then calls the `execMipsCoreStepLogic` function with necessary parameters including CPU state, registers, memory root, and instruction details. Finally, it updates the state and returns the output. ```Solidity st.CpuScalars memory cpu = getCpuScalars(state); (state.memRoot) = ins.execMipsCoreStepLogic({ _cpu: cpu, _registers: state.registers, _memRoot: state.memRoot, _memProofOffset: MIPSMemory.memoryProofOffset(STEP_PROOF_OFFSET, 1), _insn: insn, _opcode: opcode, _fun: fun }); setStateCpuScalars(state, cpu); return outputState(); ``` -------------------------------- ### Schedule Games and Enqueue Sub-Operations - Go Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/05-op-challenger.md The schedule function in the coordinator processes a list of games to create jobs for sub-operations. It iterates through games, creates jobs using createJob, and then enqueues these jobs for transmission using enqueueJob. It collects and returns any errors encountered during job creation or enqueuing. ```Go func (c *coordinator) schedule(ctx context.Context, games []types.GameMetadata, blockNumber uint64) error { …… // Next collect all the jobs to schedule and ensure all games are recorded in the states map. // Otherwise, results may start being processed before all games are recorded, resulting in existing // data directories potentially being deleted for games that are required. for _, game := range games { if j, err := c.createJob(ctx, game, blockNumber); err != nil { err = append(errs, fmt.Errorf("failed to create job for game %v: %w", game.Proxy, err)) } else if j != nil { jobs = append(jobs, *j) c.m.RecordGameUpdateScheduled() } } …… // Finally, enqueue the jobs for _, j := range jobs { if err := c.enqueueJob(ctx, j); err != nil { err = append(errs, fmt.Errorf("failed to enqueue job for game %v: %w", j.addr, err)) } } return errors.Join(errs...) } ``` -------------------------------- ### Claim Solver Attempt Step - Go Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/05-op-challenger.md This Go function, part of a claim solver, attempts to generate step data for a given claim in the fault game. It retrieves pre-state, proof data, and oracle data using trace execution. Dependencies include the 'types' package. ```Go func (s *claimSolver) AttemptStep(ctx context.Context, game types.Game, claim types.Claim, honestClaims *honestClaimTracker) (*StepData, error) { …… preState, proofData, oracleData, err := s.trace.GetStepData(ctx, game, claim, position) if err != nil { return nil, err } return &StepData{ LeafClaim: claim, IsAttack: !claimCorrect, PreState: preState, ProofData: proofData, OracleData: oracleData, }, nil } ``` -------------------------------- ### Progress Games and Schedule Bond Claims - Go Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/05-op-challenger.md The progressGames function in gameMonitor retrieves valid games from the source and schedules them for bond claims. It iterates through games, checks if they are on the allow list, and then uses the claimer.Schedule function to add them to the bond schedule. Errors during game loading or scheduling are handled. ```Go func (m *gameMonitor) progressGames(ctx context.Context, blockHash common.Hash, blockNumber uint64) error { minGameTimestamp := clock.MinCheckedTimestamp(m.clock, m.gameWindow) games, err := m.source.GetGamesAtOrAfter(ctx, blockHash, minGameTimestamp) if err != nil { return fmt.Errorf("failed to load games: %w", err) } var gamesToPlay []types.GameMetadata for _, game := range games { if !m.allowedGame(game.Proxy) { m.logger.Debug("Skipping game not on allow list", "game", game.Proxy) continue } gamesToPlay = append(gamesToPlay, game) } if err := m.claimer.Schedule(blockNumber, gamesToPlay); err != nil { return fmt.Errorf("failed to schedule bond claims: %w", err) } if err := m.scheduler.Schedule(gamesToPlay, blockNumber); errors.Is(err, scheduler.ErrBusy) { m.logger.Info("Scheduler still busy with previous update") } else if err != nil { return fmt.Errorf("failed to schedule games: %w", err) } return nil } ``` -------------------------------- ### Attempt Step in Fault Game - Go Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/05-op-challenger.md This Go function attempts to generate a 'step' action within the fault game. It checks if the claim has been countered and then uses a claim solver to attempt the step, returning the necessary StepData if successful. It depends on the 'types' and 'common' packages. ```Go func (s *GameSolver) calculateStep(ctx context.Context, game types.Game, claim types.Claim, agreedClaims *honestClaimTracker) (*types.Action, error) { if claim.CounteredBy != (common.Address{}) { return nil, nil } step, err := s.claimSolver.AttemptStep(ctx, game, claim, agreedClaims) if err != nil { return nil, err } if step == nil { return nil, nil } return &types.Action{ Type: types.ActionTypeStep, ParentClaim: step.LeafClaim, IsAttack: step.IsAttack, PreState: step.PreState, ProofData: step.ProofData, OracleData: step.OracleData, }, nil } ``` -------------------------------- ### Execute On-Chain Game Actions with PerformAction (Golang) Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/05-op-challenger.md The PerformAction function in the FaultResponder orchestrates the execution of game actions. It first handles the uploading of preimage data, determining if it's local or global and checking for existence. Subsequently, it constructs and sends the appropriate on-chain transaction (Attack, Defend, Step, or ChallengeL2BlockNumber) based on the action type, its associated data, and game state. ```golang func (r *FaultResponder) PerformAction(ctx context.Context, action types.Action) error { if action.OracleData != nil { var preimageExists bool var err error if !action.OracleData.IsLocal { preimageExists, err = r.oracle.GlobalDataExists(ctx, action.OracleData) if err != nil { return fmt.Errorf("failed to check if preimage exists: %w", err) } } // Always upload local preimages if !preimageExists { err := r.uploader.UploadPreimage(ctx, uint64(action.ParentClaim.ContractIndex), action.OracleData) if errors.Is(err, preimages.ErrChallengePeriodNotOver) { r.log.Debug("Large Preimage Squeeze failed, challenge period not over") return nil } else if err != nil { return fmt.Errorf("failed to upload preimage: %w", err) } } } var candidate txmgr.TxCandidate var err error switch action.Type { case types.ActionTypeMove: if action.IsAttack { candidate, err = r.contract.AttackTx(ctx, action.ParentClaim, action.Value) } else { candidate, err = r.contract.DefendTx(ctx, action.ParentClaim, action.Value) } case types.ActionTypeStep: candidate, err = r.contract.StepTx(uint64(action.ParentClaim.ContractIndex), action.IsAttack, action.PreState, action.ProofData) case types.ActionTypeChallengeL2BlockNumber: candidate, err = r.contract.ChallengeL2BlockNumberTx(action.InvalidL2BlockNumberChallenge) } if err != nil { return err } return r.sender.SendAndWaitSimple("perform action", candidate) } ``` -------------------------------- ### Simple Fault Proof Contract (Solidity) Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/01-what-is-fault-proof.md A basic Solidity contract demonstrating state management. It allows anyone to set a state variable, highlighting the need for a more robust verification mechanism to prevent arbitrary modifications. ```solidity contract SimpleFPContract { bytes32 public state; function setState(bytes32 _state) public { state = _state; } } ``` -------------------------------- ### Calculate Next Actions in Fault Game - Go Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/05-op-challenger.md This Go function calculates the next actions to take in a fault game based on the current game state and claims. It iterates through claims, determining whether to calculate a step or a move action based on the game depth. Dependencies include the 'types' package for game and action structures. ```Go func (s *GameSolver) CalculateNextActions(ctx context.Context, game types.Game) ([]types.Action, error) { …… var actions []types.Action agreedClaims := newHonestClaimTracker() for _, claim := range game.Claims() { var action *types.Action if claim.Depth() == game.MaxDepth() { action, err = s.calculateStep(ctx, game, claim, agreedClaims) } else { action, err = s.calculateMove(ctx, game, claim, agreedClaims) } …… if action == nil { continue } actions = append(actions, *action) } return actions, nil } ``` -------------------------------- ### Solidity: Iterate and Verify Subgame Resolution Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/02-fault-dispute-game.md This loop iterates through subgames to verify their resolution status. It ensures that a subgame is not resolved before its parent claim. It also identifies the leftmost claim incentives by updating the `counteredBy` address and `leftmostPosition` if a subgame is uncountered and further left than the current leftmost counter. ```Solidity uint256 lastToResolve = checkpoint.subgameIndex + _numToResolve; uint256 finalCursor = lastToResolve > challengeIndicesLen ? challengeIndicesLen : lastToResolve; for (uint256 i = checkpoint.subgameIndex; i < finalCursor; i++) { uint256 challengeIndex = challengeIndices[i]; // INVARIANT: Cannot resolve a subgame containing an unresolved claim if (!resolvedSubgames[challengeIndex]) revert OutOfOrderResolution(); ClaimData storage claim = claimData[challengeIndex]; // If the child subgame is uncountered and further left than the current left-most counter, // update the parent subgame's `countered` address and the current `leftmostCounter`. // The left-most correct counter is preferred in bond payouts in order to discourage attackers // from countering invalid subgame roots via an invalid defense position. As such positions // cannot be correctly countered. // Note that correctly positioned defense, but invalid claimes can still be successfully countered. if (claim.counteredBy == address(0) && checkpoint.leftmostPosition.raw() > claim.position.raw()) { checkpoint.counteredBy = claim.claimant; checkpoint.leftmostPosition = claim.position; } } ``` -------------------------------- ### Process MIPS Instruction in Cannon Off-Chain (Golang) Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/03-cannon.md The mipsStep() function in `mips.go` executes a single MIPS instruction. It checks if the state has exited, increments the step counter, fetches instruction details, and differentiates between regular instructions and syscalls. For syscalls, it dispatches to `handleSyscall()`; otherwise, it delegates the core logic execution to `exec.ExecMipsCoreStepLogic()`. ```golang func (m *InstrumentedState) mipsStep() error { if m.state.Exited { return nil } m.state.Step += 1 // instruction fetch insn, opcode, fun := exec.GetInstructionDetails(m.state.Cpu.PC, m.state.Memory) // Handle syscall separately // syscall (can read and write) if opcode == 0 && fun == 0xC { return m.handleSyscall() } // Exec the rest of the step logic return exec.ExecMipsCoreStepLogic(&m.state.Cpu, &m.state.Registers, m.state.Memory, insn, opcode, fun, m.memoryTracker, m.stackTracker) } ``` -------------------------------- ### Solidity: Resolve Finest Granularity Subgame and Distribute Bond Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/02-fault-dispute-game.md This snippet handles the resolution of the finest granularity subgame. It checks if the subgame is not the root claim and if it's the finest granularity. If so, it distributes the paid bond to the successful rebuttal's account, marks the subgame as resolved, and returns. ```Solidity if (challengeIndicesLen == 0 && _claimIndex != 0) { address counteredBy = subgameRootClaim.counteredBy; address recipient = counteredBy == address(0) ? subgameRootClaim.claimant : counteredBy; _distributeBond(recipient, subgameRootClaim); resolvedSubgames[_claimIndex] = true; return; } ``` -------------------------------- ### Resolve Subgame and Distribute Bond (Solidity) Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/02-fault-dispute-game.md This function resolves a subgame, updates its status, and distributes the associated bond. It handles a special case where the root claim is challenged, paying the bond to the challenger. Otherwise, it pays the bond to the claimant or the counter-claimant depending on the parent's resolution status. It also updates the `counteredBy` field to percolate the resolution result up the game's DAG. ```solidity resolvedSubgames[_claimIndex] = true; // Distribute the bond to the appropriate party. if (_claimIndex == 0 && l2BlockNumberChallenged) { // Special case: If the root claim has been challenged with the `challengeRootL2Block` function, // the bond is always paid out to the issuer of that challenge. address challenger = l2BlockNumberChallenger; _distributeBond(challenger, subgameRootClaim); subgameRootClaim.counteredBy = challenger; } else { // If the parent was not successfully countered, pay out the parent's bond to the claimant. // If the parent was successfully countered, pay out the parent's bond to the challenger. _distributeBond(countered == address(0) ? subgameRootClaim.claimant : countered, subgameRootClaim); // Once a subgame is resolved, we percolate the result up the DAG so subsequent calls to // resolveClaim will not need to traverse this subgame. subgameRootClaim.counteredBy = countered; } ``` -------------------------------- ### Solidity: Update and Persist Resolution Checkpoint Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/02-fault-dispute-game.md This snippet updates the subgame index in the checkpoint and persists it. It allows for continuing the resolution process in a separate transaction if it's not yet complete. If all children have been traversed, it indicates that the subgame may be resolved. ```Solidity // Increase the checkpoint's cursor position by the number of children that were checked. checkpoint.subgameIndex = uint32(finalCursor); // Persist the checkpoint and allow for continuing in a separate transaction, if resolution is not already // complete. T resolutionCheckpoints[_claimIndex] = checkpoint; // If all children have been traversed in the above loop, the subgame may be resolved. Otherwise, persist the // checkpoint and allow for continuation in a separate transaction. if (checkpoint.subgameIndex == challengeIndicesLen) { address countered = checkpoint.counteredBy; // Mark the subgame as resolved. ``` -------------------------------- ### Resolve Game (Solidity) Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/02-fault-dispute-game.md This function resolves the entire game if it's in progress and the absolute root subgame has been resolved. It determines the winner (defender or challenger) based on whether the root claim was countered and updates the game's status and resolution timestamp. It also emits a `Resolved` event and attempts to update the anchor state. ```solidity function resolve() external returns (GameStatus status_) { // INVARIANT: Resolution cannot occur unless the game is currently in progress. if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress(); // INVARIANT: Resolution cannot occur unless the absolute root subgame has been resolved. if (!resolvedSubgames[0]) revert OutOfOrderResolution(); // Update the global game status; The dispute has concluded. status_ = claimData[0].counteredBy == address(0) ? GameStatus.DEFENDER_WINS : GameStatus.CHALLENGER_WINS; resolvedAt = Timestamp.wrap(uint64(block.timestamp)); // Update the status and emit the resolved event, note that we're performing an assignment here. emit Resolved(status = status_); // Try to update the anchor state, this should not revert. ANCHOR_STATE_REGISTRY.tryUpdateAnchorState(); } ``` -------------------------------- ### Handle MIPS Syscall Instruction Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/03-cannon.md This snippet handles MIPS syscall instructions. It checks if the opcode and function match the syscall signature (0x0, 0xC) and then calls a specific handler function. This is crucial for managing external interactions within the MIPS execution environment. ```Solidity // Handle syscall separately // syscall (can read and write) if (opcode == 0 && fun == 0xC) { return handleSyscall(_localContext); } ``` -------------------------------- ### MIPS State Structure in Solidity Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/03-cannon.md Defines the 'State' structure used in the on-chain MIPS implementation. This structure encapsulates all necessary information for MIPS execution, including memory roots, register values, program counter, and execution status. It is passed into on-chain functions rather than being stored as persistent state. ```solidity struct State { bytes32 memRoot; bytes32 preimageKey; uint32 preimageOffset; uint32 pc; uint32 nextPC; uint32 lo; uint32 hi; uint32 heap; uint8 exitCode; bool exited; uint64 step; uint32[32] registers; } ``` -------------------------------- ### Solidity: Check if Challenge Clock Has Expired Source: https://github.com/joohhnnn/the-book-of-optimism-fault-proof/blob/main/02-fault-dispute-game.md This code snippet checks if the challenge clock duration has passed the maximum allowed duration. If not, it reverts with a 'ClockNotExpired' error. This is a prerequisite for proceeding with claim resolution. ```Solidity if (challengeClockDuration.raw() < MAX_CLOCK_DURATION.raw()) revert ClockNotExpired(); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.