1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package contracts
import (
"context"
_ "embed"
"fmt"
"math/big"
"time"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
)
//go:embed abis/FaultDisputeGame-0.8.0.json
var faultDisputeGameAbi020 []byte
var resolvedBondAmount = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 128), big.NewInt(1))
var (
methodGameDuration = "gameDuration"
)
type FaultDisputeGameContract080 struct {
FaultDisputeGameContractLatest
}
// GetGameMetadata returns the game's L1 head, L2 block number, root claim, status, and max clock duration.
func (f *FaultDisputeGameContract080) GetGameMetadata(ctx context.Context, block rpcblock.Block) (GameMetadata, error) {
defer f.metrics.StartContractRequest("GetGameMetadata")()
results, err := f.multiCaller.Call(ctx, block,
f.contract.Call(methodL1Head),
f.contract.Call(methodL2BlockNumber),
f.contract.Call(methodRootClaim),
f.contract.Call(methodStatus),
f.contract.Call(methodGameDuration))
if err != nil {
return GameMetadata{}, fmt.Errorf("failed to retrieve game metadata: %w", err)
}
if len(results) != 5 {
return GameMetadata{}, fmt.Errorf("expected 5 results but got %v", len(results))
}
l1Head := results[0].GetHash(0)
l2BlockNumber := results[1].GetBigInt(0).Uint64()
rootClaim := results[2].GetHash(0)
status, err := gameTypes.GameStatusFromUint8(results[3].GetUint8(0))
if err != nil {
return GameMetadata{}, fmt.Errorf("failed to convert game status: %w", err)
}
duration := results[4].GetUint64(0)
return GameMetadata{
L1Head: l1Head,
L2BlockNum: l2BlockNumber,
RootClaim: rootClaim,
Status: status,
MaxClockDuration: duration / 2,
L2BlockNumberChallenged: false,
}, nil
}
func (f *FaultDisputeGameContract080) GetMaxClockDuration(ctx context.Context) (time.Duration, error) {
defer f.metrics.StartContractRequest("GetMaxClockDuration")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodGameDuration))
if err != nil {
return 0, fmt.Errorf("failed to fetch game duration: %w", err)
}
return time.Duration(result.GetUint64(0)) * time.Second / 2, nil
}
func (f *FaultDisputeGameContract080) GetClaim(ctx context.Context, idx uint64) (types.Claim, error) {
claim, err := f.FaultDisputeGameContractLatest.GetClaim(ctx, idx)
if err != nil {
return types.Claim{}, err
}
// Replace the resolved sentinel with what the bond would have been
if claim.Bond.Cmp(resolvedBondAmount) == 0 {
bond, err := f.GetRequiredBond(ctx, claim.Position)
if err != nil {
return types.Claim{}, err
}
claim.Bond = bond
}
return claim, nil
}
func (f *FaultDisputeGameContract080) GetAllClaims(ctx context.Context, block rpcblock.Block) ([]types.Claim, error) {
claims, err := f.FaultDisputeGameContractLatest.GetAllClaims(ctx, block)
if err != nil {
return nil, err
}
resolvedClaims := make([]*types.Claim, 0, len(claims))
positions := make([]*big.Int, 0, len(claims))
for i, claim := range claims {
if claim.Bond.Cmp(resolvedBondAmount) == 0 {
resolvedClaims = append(resolvedClaims, &claims[i])
positions = append(positions, claim.Position.ToGIndex())
}
}
bonds, err := f.GetRequiredBonds(ctx, block, positions...)
if err != nil {
return nil, fmt.Errorf("failed to get required bonds for resolved claims: %w", err)
}
for i, bond := range bonds {
resolvedClaims[i].Bond = bond
}
return claims, nil
}
func (f *FaultDisputeGameContract080) IsResolved(ctx context.Context, block rpcblock.Block, claims ...types.Claim) ([]bool, error) {
rawClaims, err := f.FaultDisputeGameContractLatest.GetAllClaims(ctx, block)
if err != nil {
return nil, fmt.Errorf("failed to get raw claim data: %w", err)
}
results := make([]bool, len(claims))
for i, claim := range claims {
results[i] = rawClaims[claim.ContractIndex].Bond.Cmp(resolvedBondAmount) == 0
}
return results, nil
}
func (f *FaultDisputeGameContract080) CallResolveClaim(ctx context.Context, claimIdx uint64) error {
defer f.metrics.StartContractRequest("CallResolveClaim")()
call := f.resolveClaimCall(claimIdx)
_, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, call)
if err != nil {
return fmt.Errorf("failed to call resolve claim: %w", err)
}
return nil
}
func (f *FaultDisputeGameContract080) ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, error) {
call := f.resolveClaimCall(claimIdx)
return call.ToTxCandidate()
}
func (f *FaultDisputeGameContract080) resolveClaimCall(claimIdx uint64) *batching.ContractCall {
return f.contract.Call(methodResolveClaim, new(big.Int).SetUint64(claimIdx))
}
func (f *FaultDisputeGameContract080) IsL2BlockNumberChallenged(_ context.Context, _ rpcblock.Block) (bool, error) {
return false, nil
}
func (f *FaultDisputeGameContract080) ChallengeL2BlockNumberTx(_ *types.InvalidL2BlockNumberChallenge) (txmgr.TxCandidate, error) {
return txmgr.TxCandidate{}, ErrChallengeL2BlockNotSupported
}
func (f *FaultDisputeGameContract080) AttackTx(ctx context.Context, parent types.Claim, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodAttack, big.NewInt(int64(parent.ContractIndex)), pivot)
return f.txWithBond(ctx, parent.Position.Attack(), call)
}
func (f *FaultDisputeGameContract080) DefendTx(ctx context.Context, parent types.Claim, pivot common.Hash) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodDefend, big.NewInt(int64(parent.ContractIndex)), pivot)
return f.txWithBond(ctx, parent.Position.Defend(), call)
}