Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

draft: feat(op-challenger): add stepproof and oracle for MSFDG #69

Closed
wants to merge 9 commits into from
2 changes: 1 addition & 1 deletion op-challenger/game/fault/contracts/delayed_weth.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func NewDelayedWETHContract(metrics metrics.ContractMetricer, addr common.Addres
return &DelayedWETHContract{
metrics: metrics,
multiCaller: caller,
contract: batching.NewBoundContract(contractAbi, addr),
contract: batching.NewBoundContract(contractAbi, addr, caller),
}
}

Expand Down
10 changes: 6 additions & 4 deletions op-challenger/game/fault/contracts/faultdisputegame.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ var (
methodL2BlockNumberChallenged = "l2BlockNumberChallenged"
methodL2BlockNumberChallenger = "l2BlockNumberChallenger"
methodChallengeRootL2Block = "challengeRootL2Block"
subClaimField = "_claim"
)

var (
Expand Down Expand Up @@ -96,7 +97,7 @@ func NewFaultDisputeGameContract(ctx context.Context, metrics metrics.ContractMe
FaultDisputeGameContractLatest: FaultDisputeGameContractLatest{
metrics: metrics,
multiCaller: caller,
contract: batching.NewBoundContract(legacyAbi, addr),
contract: batching.NewBoundContract(legacyAbi, addr, caller),
},
}, nil
} else if strings.HasPrefix(version, "0.18.") || strings.HasPrefix(version, "1.0.") {
Expand All @@ -106,7 +107,7 @@ func NewFaultDisputeGameContract(ctx context.Context, metrics metrics.ContractMe
FaultDisputeGameContractLatest: FaultDisputeGameContractLatest{
metrics: metrics,
multiCaller: caller,
contract: batching.NewBoundContract(legacyAbi, addr),
contract: batching.NewBoundContract(legacyAbi, addr, caller),
},
}, nil
} else if strings.HasPrefix(version, "1.1.") {
Expand All @@ -116,14 +117,14 @@ func NewFaultDisputeGameContract(ctx context.Context, metrics metrics.ContractMe
FaultDisputeGameContractLatest: FaultDisputeGameContractLatest{
metrics: metrics,
multiCaller: caller,
contract: batching.NewBoundContract(legacyAbi, addr),
contract: batching.NewBoundContract(legacyAbi, addr, caller),
},
}, nil
} else {
return &FaultDisputeGameContractLatest{
metrics: metrics,
multiCaller: caller,
contract: batching.NewBoundContract(contractAbi, addr),
contract: batching.NewBoundContract(contractAbi, addr, caller),
}, nil
}
}
Expand Down Expand Up @@ -624,4 +625,5 @@ type FaultDisputeGameContract interface {
ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, error)
CallResolve(ctx context.Context) (gameTypes.GameStatus, error)
ResolveTx() (txmgr.TxCandidate, error)
GetSubValues(ctx context.Context, block rpcblock.Block, aggClaim *types.Claim) ([]common.Hash, error)
}
87 changes: 87 additions & 0 deletions op-challenger/game/fault/contracts/faultdisputegame2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package contracts

import (
"context"
"fmt"
"math/big"

"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-e2e/bindings"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
)

func (f *FaultDisputeGameContractLatest) GetAllClaimsWithSubValues(ctx context.Context, block rpcblock.Block) ([]types.Claim, error) {
defer f.metrics.StartContractRequest("GetAllClaims")()
results, err := batching.ReadArray(ctx, f.multiCaller, block, f.contract.Call(methodClaimCount), func(i *big.Int) *batching.ContractCall {
return f.contract.Call(methodClaim, i)
})
if err != nil {
return nil, fmt.Errorf("failed to load claims: %w", err)
}

var claims []types.Claim
for idx, result := range results {
claim := f.decodeClaim(result, idx)
subValues, err := f.GetSubValues(ctx, block, &claim)
if err != nil {
return nil, fmt.Errorf("failed to load subValues for claim %s: %w", claim, err)
}
claim.SetSubValues(&subValues)
claims = append(claims, claim)
}
return claims, nil
}

func (f *FaultDisputeGameContractLatest) GetSubValues(ctx context.Context, block rpcblock.Block, aggClaim *types.Claim) ([]common.Hash, error) {
defer f.metrics.StartContractRequest("GetAllSubValues")()

filter, err := bindings.NewFaultDisputeGameFilterer(f.contract.Addr(), f.multiCaller)
if err != nil {
return nil, err
}

parentIndex := [...]*big.Int{big.NewInt(int64(aggClaim.ParentContractIndex))}
claim := [...][32]byte{aggClaim.ClaimData.ValueBytes()}
claimant := [...]common.Address{aggClaim.Claimant}
moveIter, err := filter.FilterMove(nil, parentIndex[:], claim[:], claimant[:])
if err != nil {
return nil, fmt.Errorf("failed to filter move event log: %w", err)
}
ok := moveIter.Next()
if !ok {
return nil, fmt.Errorf("failed to get move event log: %w", moveIter.Error())
}
txHash := moveIter.Event.Raw.TxHash

// todo: replace hardcoded method name
txCall := batching.NewTxGetByHash(f.contract.Abi(), txHash, "move")
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, txCall)
if err != nil {
return nil, fmt.Errorf("failed to load claim calldata: %w", err)
}

txn, err := txCall.DecodeToTx(result)
if err != nil {
return nil, fmt.Errorf("failed to decode tx: %w", err)
}

var subValues []common.Hash

if len(txn.BlobHashes()) > 0 {
// todo: fetch Blobs and unpack it into subValues
return nil, fmt.Errorf("blob tx hasn't been supported")
} else {
inputMap, err := txCall.UnpackCallData(txn)
if err != nil {
return nil, fmt.Errorf("failed to unpack tx resp: %w", err)
}
// todo: replace claim with nary-subValues
claim := *abi.ConvertType(inputMap[subClaimField], new([32]byte)).(*[32]byte)
subValues = append(subValues, claim)
}

return subValues, nil
}
78 changes: 78 additions & 0 deletions op-challenger/game/fault/contracts/faultdisputegame_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/ethereum-optimism/optimism/packages/contracts-bedrock/snapshots"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
coreTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -321,6 +322,83 @@ func TestGetAllClaims(t *testing.T) {
}
}

func TestGetSubClaims(t *testing.T) {
for _, version := range versions {
// todo: backward and forward support
if version.Is("1.2.0") {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
claim0 := faultTypes.Claim{
ClaimData: faultTypes.ClaimData{
Value: common.Hash{0xaa},
Position: faultTypes.NewPositionFromGIndex(big.NewInt(1)),
Bond: big.NewInt(5),
},
CounteredBy: common.Address{0x01},
Claimant: common.Address{0x02},
Clock: decodeClock(big.NewInt(1234)),
ContractIndex: 0,
ParentContractIndex: math.MaxUint32,
}
expectedClaims := []faultTypes.Claim{claim0}
block := rpcblock.ByNumber(42)
stubRpc.SetResponse(fdgAddr, methodClaimCount, block, nil, []interface{}{big.NewInt(int64(len(expectedClaims)))})

eventName := "Move"
fdgAbi := version.loadAbi()

var challgenIndex []interface{}
challgenIndex = append(challgenIndex, big.NewInt(int64(claim0.ParentContractIndex)))
claim := []interface{}{claim0.ClaimData.ValueBytes()}
address := []interface{}{claim0.Claimant}
query := [][]interface{}{challgenIndex, claim, address}
txHash := common.Hash{0xff}

query = append([][]interface{}{{fdgAbi.Events[eventName].ID}}, query...)

topics, err := abi.MakeTopics(query...)
var queryTopics []common.Hash
for _, item := range topics {
queryTopics = append(queryTopics, item[0])
}
require.NoError(t, err)
out := []coreTypes.Log{
{
Address: fdgAddr,
Topics: queryTopics,
Data: []byte{},
TxHash: txHash,
},
}
stubRpc.SetFilterLogResponse(topics, fdgAddr, block, out)

contractCall := batching.NewContractCall(fdgAbi, fdgAddr, "move", claim0.ClaimData.Value, challgenIndex[0], claim0.ClaimData.Value, true)
inputData, err := contractCall.Pack()
require.NoError(t, err)

tx := coreTypes.NewTx(&coreTypes.LegacyTx{
Nonce: 0,
GasPrice: big.NewInt(11111),
Gas: 1111,
To: &claim0.Claimant,
Value: big.NewInt(111),
Data: inputData,
})
require.NoError(t, err)
packed, err := tx.MarshalBinary()
require.NoError(t, err)
stubRpc.SetTxResponse(txHash, packed)

claims, err := game.GetSubValues(context.Background(), block, &claim0)
require.NoError(t, err)
require.Equal(t, 1, len(claims))
require.Equal(t, claim0.ClaimData.Value, claims[0])
})
}
}
}

func TestGetBalance(t *testing.T) {
for _, version := range versions {
version := version
Expand Down
2 changes: 1 addition & 1 deletion op-challenger/game/fault/contracts/gamefactory.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func NewDisputeGameFactoryContract(m metrics.ContractMetricer, addr common.Addre
return &DisputeGameFactoryContract{
metrics: m,
multiCaller: caller,
contract: batching.NewBoundContract(factoryAbi, addr),
contract: batching.NewBoundContract(factoryAbi, addr, caller),
abi: factoryAbi,
}
}
Expand Down
2 changes: 1 addition & 1 deletion op-challenger/game/fault/contracts/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func NewPreimageOracleContract(addr common.Address, caller *batching.MultiCaller
return &PreimageOracleContract{
addr: addr,
multiCaller: caller,
contract: batching.NewBoundContract(oracleAbi, addr),
contract: batching.NewBoundContract(oracleAbi, addr, caller),
}
}

Expand Down
2 changes: 1 addition & 1 deletion op-challenger/game/fault/contracts/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func NewVMContract(addr common.Address, caller *batching.MultiCaller) *VMContrac

return &VMContract{
multiCaller: caller,
contract: batching.NewBoundContract(mipsAbi, addr),
contract: batching.NewBoundContract(mipsAbi, addr, caller),
}
}

Expand Down
54 changes: 54 additions & 0 deletions op-challenger/game/fault/solver/solver2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package solver

import (
"context"
"fmt"

"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
)

// AttemptStep determines what step, if any, should occur for a given leaf claim.
// An error will be returned if the claim is not at the max depth.
// Returns nil, nil if no step should be performed.
func (s *claimSolver) AttemptStep2(ctx context.Context, game types.Game, claim types.Claim, honestClaims *honestClaimTracker) (*StepData, error) {
if claim.Depth() != s.gameDepth {
return nil, ErrStepNonLeafNode
}

if counter, err := s.shouldCounter(game, claim, honestClaims); err != nil {
return nil, fmt.Errorf("failed to determine if claim should be countered: %w", err)
} else if !counter {
return nil, nil
}

claimCorrect, err := s.agreeWithClaim(ctx, game, claim)
if err != nil {
return nil, err
}

// prestateItem
// postStateItem

var position types.Position
if !claimCorrect {
// Attack the claim by executing step index, so we need to get the pre-state of that index
position = claim.Position
} else {
// Defend and use this claim as the starting point to execute the step after.
// Thus, we need the pre-state of the next step.
position = claim.Position.MoveRight()
}

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
}
55 changes: 55 additions & 0 deletions op-challenger/game/fault/test/alphabet2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package test

import (
"context"
"math/big"
"testing"

"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
)

type AlphabetWithOutputRootProvider struct {
*alphabet.AlphabetTraceProvider
depth types.Depth
OracleError error
L2BlockChallenge *types.InvalidL2BlockNumberChallenge
pre bool // preOutputRoot if pre is true; postOutputRoot if pre is false
}

func (a *AlphabetWithOutputRootProvider) GetStepData(ctx context.Context, i types.Position) ([]byte, []byte, *types.PreimageOracleData, error) {
preimage, _, _, err := a.AlphabetTraceProvider.GetStepData(ctx, i)
if err != nil {
return nil, nil, nil, err
}
traceIndex := i.TraceIndex(a.depth).Uint64()
var key []byte
if a.pre {
// localPreimageKey(1) + STARTING_OUTPUT_ROOT(2)
key = []byte{0x01, 0x02}
} else {
// localPreimageKey(1) + DISPUTED_OUTPUT_ROOT(3)
key = []byte{0x01, 0x03}
}
data := types.NewPreimageOracleData(key, []byte{byte(traceIndex - 1)}, uint32(traceIndex-1))
return preimage, []byte{byte(traceIndex - 1)}, data, nil
}

func NewAlphabetWithProofProvider2(t *testing.T, startingL2BlockNumber *big.Int, maxDepth types.Depth, rootDepth types.Depth, oracleError error) *AlphabetWithProofProvider {
return &AlphabetWithProofProvider{
alphabet.NewTraceProvider2(startingL2BlockNumber, maxDepth, rootDepth),
maxDepth,
oracleError,
nil,
}
}

func NewAlphabetWithPreoutputRootProvider(t *testing.T, startingL2BlockNumber *big.Int, maxDepth types.Depth, rootDepth types.Depth, pre bool, oracleError error) *AlphabetWithOutputRootProvider {
return &AlphabetWithOutputRootProvider{
alphabet.NewTraceProvider2(startingL2BlockNumber, maxDepth, rootDepth),
maxDepth,
oracleError,
nil,
pre,
}
}
Loading