这篇文章上次修改于 315 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

整体逻辑

Staker 源码

func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) {
    // 处理超时的挑战,将 loser 的 stake 罚没一半,奖励给 winner
    arbTx, err := s.resolveTimedOutChallenges(ctx)
    
    // 解决冲突,执行 func (m *ChallengeManager) Act()
    s.handleConflict(ctx, rawInfo)
    
    // 质押
    s.advanceStake(ctx, &info, effectiveStrategy)
    
    // 提出挑战
    s.createConflict(ctx, rawInfo)
}
  • 处理超时的挑战

    timedOutChallenges() -> timeout() -> _nextWin():将 loser 的 stake 罚没一半,奖励给 winner

  • 解决冲突

    执行 func (m *ChallengeManager) Act()

    • LoadExecChallengeIfExists():通过解析 event ExecutionChallengeBegun 检查挑战是否已经存在。如果已经存在,但是没有 execution challenge backend,则创建;否则,如果有 backend,则删除该 backend
    • IsMyTurn():查看是不是轮到自己回应了,不是的话结束:currentResponder()
    • GetChallengeState():通过 challengeInfo() 获取挑战,根据挑战的 state hash,通过解析 event Bisected 得到完整的挑战状态,如 segments
    • ScanChallengeState():当前的 responder 应选择一对相邻的 segment 进行挑战,所以需要获得意见不同的 segment 的前一个 segment
    • bisect():如果挑战没有缩减到只剩一步,则缩减挑战, bisectExecution() -> emit Bisected(selection.challengeStart, selection.challengeLength)
    • 否则

      • IssueOneStepProof():如果有 execution challenge backend,说明挑战缩减到只剩一步,则执行 oneStepProveExecution()

        当前的 responder 需要提供 proof,以还原出执行前的状态,执行 one step 后会得到执行后的状态,并与提交的状态进行比对,如果不同,则当前的 responder 赢 _currentWin()

        _currentWin() 中仅仅将 state hash 设成 0,这样下次轮到对手时,对手无法进行任何合法的操作,c从而导致 challenge 超时,从而执行 _nextWin()

      • IssueExecChallenge():否则,说明挑战只是缩减到了某个 block,执行 challengeExecution() -> emit Bisected(challengeStart = 0, challengeLength = machineFinalStepCount, mode = ChallengeLib.ChallengeMode.EXECUTION), emit ExecutionChallengeBegun

        当前的 responder 需要提供 global state 和 machine state

  • 提出挑战

    • 如果当前 staker 已经在挑战中了,结束
    • 获取 staker 列表:getStakers
    • 找到没有处于挑战中且质押的 node 不同的 staker:findStakerConflict()
    • 找到冲突的两个 node 信息:getNodeCreationBlockForLogLookup() / getNode(),event NodeCreated
    • 根据 node 信息解出 globalState()
    • 提出挑战:createChallenge() -> emit Bisected(challengeStart = 0, challengeLength = numBlocks, mode = ChallengeLib.ChallengeMode.BLOCK)

      第一个 Bisected event 只包含 2 个 segment

      // 节点
      _, err = s.rollup.CreateChallenge(
          auth,
          [2]common.Address{staker1, staker2},
          [2]uint64{conflictInfo.Node1, conflictInfo.Node2},
          node1Info.MachineStatuses(),
          node1Info.GlobalStates(),
          node1Info.Assertion.NumBlocks,
          node2Info.Assertion.ExecutionHash(),
          [2]*big.Int{new(big.Int).SetUint64(node1Info.L1BlockProposed), new(big.Int).SetUint64(node2Info.L1BlockProposed)},
          [2][32]byte{node1Info.WasmModuleRoot, node2Info.WasmModuleRoot},
      )
      
      // 合约
      function createChallenge(
          address[2] calldata stakers,
          uint64[2] calldata nodeNums,
          MachineStatus[2] calldata machineStatuses,
          GlobalState[2] calldata globalStates,
          uint64 numBlocks,
          bytes32 secondExecutionHash,
          uint256[2] calldata proposedBlocks,
          bytes32[2] calldata wasmModuleRoots
      ) external onlyValidator whenNotPaused {
          // Start a challenge between staker1 and staker2. Staker1 will defend the correctness of node1, and staker2 will challenge it.
         challengeManager.createChallenge() -> emit Bisected
      }
      
      emit Bisected(
          challengeIndex,
          challengeStateHash,
          challengeStart,
          challengeLength,
          newSegments
      );
      
      // 定义
      event Bisected(
          uint64 indexed challengeIndex,
          bytes32 indexed challengeRoot,
          uint256 challengedSegmentStart,
          uint256 challengedSegmentLength,
          bytes32[] chainHashes
      );