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
9 changes: 7 additions & 2 deletions op-bindings/bindings/faultdisputegame.go

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions op-challenger/game/fault/types/position.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ func (p Position) Defend() Position {
return p.parent().move(true).move(false)
}

func (p Position) MoveN(bits uint, branch uint64) Position {
move := new(big.Int)
move.Or(big.NewInt(0).SetUint64(branch), p.ToGIndex())
move.Lsh(move, bits)
return NewPositionFromGIndex(move)
}

func (p Position) Print(maxDepth Depth) {
fmt.Printf("GIN: %4b\tTrace Position is %4b\tTrace Depth is: %d\tTrace Index is: %d\n", p.ToGIndex(), p.indexAtDepth, p.depth, p.TraceIndex(maxDepth))
}
Expand Down
5 changes: 5 additions & 0 deletions op-e2e/e2eutils/disputegame/claim_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ func (c *ClaimHelper) Defend(ctx context.Context, value common.Hash, opts ...Mov
return c.WaitForCounterClaim(ctx)
}

func (c *ClaimHelper) AttackAt(ctx context.Context, value common.Hash, branch uint64, opts ...MoveOpt) *ClaimHelper {
c.game.AttackAt(ctx, c.index, value, branch, opts...)
return c.WaitForCounterClaim(ctx)
}

func (c *ClaimHelper) RequireDifferentClaimValue(other *ClaimHelper) {
c.require.NotEqual(c.claim, other.claim, "should have posted different claims")
}
Expand Down
3 changes: 3 additions & 0 deletions op-e2e/e2eutils/disputegame/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,13 @@ func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, l2Node string
logger := testlog.Logger(h.t, log.LevelInfo).New("role", "OutputCannonGameHelper")
rollupClient := h.system.RollupClient(l2Node)

// 区块号 l2BlockNumber
extraData := h.createBisectionGameExtraData(l2Node, l2BlockNumber, cfg)

ctx, cancel := context.WithTimeout(ctx, 1*time.Minute)
defer cancel()

// 调用DisputeGameFactory合约的Create方法, 创建一个DisputeGame合约
tx, err := transactions.PadGasEstimate(h.opts, 2, func(opts *bind.TransactOpts) (*types.Transaction, error) {
return h.factory.Create(opts, cannonGameType, rootClaim, extraData)
})
Expand All @@ -181,6 +183,7 @@ func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, l2Node string
h.require.Len(rcpt.Logs, 2, "should have emitted a single DisputeGameCreated event")
createdEvent, err := h.factory.ParseDisputeGameCreated(*rcpt.Logs[1])
h.require.NoError(err)

game, err := bindings.NewFaultDisputeGame(createdEvent.DisputeProxy, h.client)
h.require.NoError(err)

Expand Down
92 changes: 91 additions & 1 deletion op-e2e/e2eutils/disputegame/output_game_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func (g *OutputGameHelper) GenesisBlockNum(ctx context.Context) uint64 {
}

func (g *OutputGameHelper) DisputeLastBlock(ctx context.Context) *ClaimHelper {
return g.DisputeBlock(ctx, g.L2BlockNum(ctx))
return g.DisputeBlockV2(ctx, g.L2BlockNum(ctx))
}

// DisputeBlock posts claims from both the honest and dishonest actor to progress the output root part of the game
Expand Down Expand Up @@ -142,6 +142,71 @@ func (g *OutputGameHelper) DisputeBlock(ctx context.Context, disputeBlockNum uin
return claim
}

func (g *OutputGameHelper) DisputeBlockV2(ctx context.Context, disputeBlockNum uint64) *ClaimHelper {
dishonestValue := g.GetClaimValue(ctx, 0)
correctRootClaim := g.correctOutputRoot(ctx, types.NewPositionFromGIndex(big.NewInt(1)))
rootIsValid := dishonestValue == correctRootClaim
if rootIsValid {
// Ensure that the dishonest actor is actually posting invalid roots.
// Otherwise, the honest challenger will defend our counter and ruin everything.
dishonestValue = common.Hash{0xff, 0xff, 0xff}
}
pos := types.NewPositionFromGIndex(big.NewInt(1))
getClaimValue := func(parentClaim *ClaimHelper, claimPos types.Position) common.Hash {
claimBlockNum, err := g.correctOutputProvider.ClaimedBlockNumber(claimPos)
g.require.NoError(err, "failed to calculate claim block number")
if claimBlockNum < disputeBlockNum {
// Use the correct output root for all claims prior to the dispute block number
// This pushes the game to dispute the last block in the range
return g.correctOutputRoot(ctx, claimPos)
}
if rootIsValid == parentClaim.AgreesWithOutputRoot() {
// We are responding to a parent claim that agrees with a valid root, so we're being dishonest
return dishonestValue
} else {
// Otherwise we must be the honest actor so use the correct root
return g.correctOutputRoot(ctx, claimPos)
}
}

/*
*g.t.Logf("TestLog SplitDepth:%v, MaxDepth:%v", g.SplitDepth(ctx), g.MaxDepth(ctx))
*g.t.Logf("TestLog disputeBlockNum: %v", disputeBlockNum)
*tmp_pos := types.NewPositionFromGIndex(big.NewInt(2))
*g.t.Logf("TestLog tmp_pos.Attack befor pos: %v", tmp_pos)
*g.t.Logf("TestLog tmp_pos.MoveN after pos: %v", tmp_pos.MoveN(1, big.NewInt(0)))
*tmp_pos = tmp_pos.Attack()
*g.t.Logf("TestLog tmp_pos.Attack after pos: %v", tmp_pos)
*g.t.Logf("TestLog tmp_pos.MoveN after pos: %v", tmp_pos.MoveN(1, big.NewInt(1)))
*tmp_pos = tmp_pos.Defend()
*g.t.Logf("TestLog tmp_pos.Defend after tmp_pos: %v", tmp_pos)
*/

// FaultDisputeGame 获取根节点
claim := g.RootClaim(ctx)
g.t.Logf("TestLog init pos: %v", pos)
g.t.Logf("TestLog root claim: %v", claim)
// 通过pos的深度判断是否为output叶节点
g.t.Logf("TestLog claim.position.Depth(): %v, position:%v", claim.position.Depth(), claim.position)
for !claim.IsOutputRootLeaf(ctx) {
parentClaimBlockNum, err := g.correctOutputProvider.ClaimedBlockNumber(pos)
g.require.NoError(err, "failed to calculate parent claim block number")
if parentClaimBlockNum >= disputeBlockNum {
g.t.Logf("TestLog pos.Attack MoveN befor pos: %v", pos)
pos = pos.MoveN(2, 0)
g.t.Logf("TestLog pos.Attack MoveN after pos: %v", pos)
claim = claim.AttackAt(ctx, getClaimValue(claim, pos), 0)
} else {
g.t.Logf("TestLog pos.Defend MoveN befor pos: %v", pos)
pos = pos.MoveN(2, 3)
g.t.Logf("TestLog pos.Defend MoveN after pos: %v", pos)
claim = claim.AttackAt(ctx, getClaimValue(claim, pos), 3)
}
g.t.Logf("TestLog claim.position.Depth(): %v, position:%v", claim.position.Depth(), claim.position)
}
return claim
}

func (g *OutputGameHelper) RootClaim(ctx context.Context) *ClaimHelper {
claim := g.getClaim(ctx, 0)
return newClaimHelper(g, 0, claim)
Expand Down Expand Up @@ -574,6 +639,27 @@ func (g *OutputGameHelper) Defend(ctx context.Context, claimIdx int64, claim com
}
}

func (g *OutputGameHelper) AttackAt(ctx context.Context, claimIdx int64, claim common.Hash, branch uint64, opts ...MoveOpt) {
g.t.Logf("AttackAt claim %v with value %v, branch %v", claimIdx, claim, branch)
cfg := g.moveCfg(opts...)

claimData, err := g.game.ClaimData(&bind.CallOpts{Context: ctx}, big.NewInt(claimIdx))
g.require.NoError(err, "Failed to get claim data")
pos := types.NewPositionFromGIndex(claimData.Position)
attackPos := pos.MoveN(2, branch)
transactOpts := g.makeBondedTransactOpts(ctx, attackPos.ToGIndex(), cfg.opts)

err = g.sendMove(ctx, func() (*gethtypes.Transaction, error) {
return g.game.AttackAt(transactOpts, big.NewInt(claimIdx), claim, branch)
})
if err != nil {
if cfg.ignoreDupes && g.hasClaim(ctx, claimIdx, attackPos, claim) {
return
}
g.require.NoErrorf(err, "AttackAt transaction failed. Game state: \n%v", g.gameData(ctx))
}
}

func (g *OutputGameHelper) hasClaim(ctx context.Context, parentIdx int64, pos types.Position, value common.Hash) bool {
claims := g.getAllClaims(ctx)
for _, claim := range claims {
Expand Down Expand Up @@ -742,6 +828,10 @@ func (g *OutputGameHelper) LogGameData(ctx context.Context) {
g.t.Log(g.gameData(ctx))
}

func (g *OutputGameHelper) LogGameDataF(ctx context.Context, flag string) {
g.t.Logf("TestLog GameData %v, %v", flag, g.gameData(ctx))
}

func (g *OutputGameHelper) Credit(ctx context.Context, addr common.Address) *big.Int {
opts := &bind.CallOpts{Context: ctx}
amt, err := g.game.Credit(opts, addr)
Expand Down
85 changes: 85 additions & 0 deletions op-e2e/faultproofs/simple_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package faultproofs

import (
"context"
"testing"

op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/disputegame"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)

func TestSimple_Alphabet_ChallengerWins(t *testing.T) {
op_e2e.InitParallel(t)
ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t)
t.Cleanup(sys.Close)

disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
game := disputeGameFactory.StartOutputAlphabetGame(ctx, "sequencer", 3, common.Hash{0xff})
game.LogGameData(ctx)

// The dispute game should have a zero balance
balance := game.WethBalance(ctx, game.Addr())
require.Zero(t, balance.Uint64())

//alice := sys.Cfg.Secrets.Addresses().Alice

// Grab the root claim
claim := game.RootClaim(ctx)
opts := challenger.WithPrivKey(sys.Cfg.Secrets.Alice)
game.StartChallenger(ctx, "sequencer", "Challenger", opts)
game.LogGameData(ctx)

// Perform a few moves
claim = claim.WaitForCounterClaim(ctx)
game.LogGameData(ctx)

claim = claim.Attack(ctx, common.Hash{})
claim = claim.WaitForCounterClaim(ctx)
game.LogGameData(ctx)

/*
*claim = claim.Attack(ctx, common.Hash{})
*game.LogGameData(ctx)
*_ = claim.WaitForCounterClaim(ctx)
*/

sys.TimeTravelClock.AdvanceTime(game.GameDuration(ctx))
require.NoError(t, wait.ForNextBlock(ctx, l1Client))
game.WaitForGameStatus(ctx, disputegame.StatusChallengerWins)
game.LogGameData(ctx)
}

func TestSimple_Cannon_ChallengerWins(t *testing.T) {
op_e2e.InitParallel(t)
ctx := context.Background()
sys, l1Client := startFaultDisputeSystem(t)
t.Cleanup(sys.Close)

disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys)
game := disputeGameFactory.StartOutputCannonGame(ctx, "sequencer", 3, common.Hash{0x01, 0xaa})
require.NotNil(t, game)
claim := game.DisputeLastBlock(ctx)

opts := challenger.WithPrivKey(sys.Cfg.Secrets.Alice)
game.StartChallenger(ctx, "sequencer", "Challenger", opts)
game.LogGameData(ctx)

// Create the root of the cannon trace.
//claim = claim.Attack(ctx, common.Hash{0x01})
claim = claim.AttackAt(ctx, common.Hash{0x01}, 0)
game.LogGameDataF(ctx, "AttackAt[0]")

claim = claim.AttackAt(ctx, common.Hash{0x02}, 2)
game.LogGameDataF(ctx, "AttackAt[2]")

t.Logf("TestLog GameDuration: %v", game.GameDuration(ctx))
sys.TimeTravelClock.AdvanceTime(game.GameDuration(ctx) * 2)
require.NoError(t, wait.ForNextBlock(ctx, l1Client))
game.WaitForGameStatus(ctx, disputegame.StatusChallengerWins)
game.LogGameDataF(ctx, "ChallengerWins")
}
Loading