Skip to content

Commit

Permalink
Merge pull request #80 from ethstorage/bill/op-challenger-contract-abi
Browse files Browse the repository at this point in the history
feat(MSFDG): support attackV2 & stepV2 for MSFDG contract
  • Loading branch information
dajuguan authored Nov 1, 2024
2 parents df93efb + 134a07c commit 3e2f67b
Show file tree
Hide file tree
Showing 7 changed files with 1,549 additions and 22 deletions.
48 changes: 47 additions & 1 deletion op-challenger2/game/fault/contracts/faultdisputegame.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ var (
methodL2BlockNumberChallenged = "l2BlockNumberChallenged"
methodL2BlockNumberChallenger = "l2BlockNumberChallenger"
methodChallengeRootL2Block = "challengeRootL2Block"
methodNBits = "nBits"
methodMaxAttackBranch = "maxAttackBranch"
methodAttackV2 = "attackV2"
methodStepV2 = "stepV2"
)

var (
Expand Down Expand Up @@ -81,7 +85,7 @@ type outputRootProof struct {
}

func NewFaultDisputeGameContract(ctx context.Context, metrics metrics.ContractMetricer, addr common.Address, caller *batching.MultiCaller) (FaultDisputeGameContract, error) {
contractAbi := snapshots.LoadFaultDisputeGameABI()
contractAbi := snapshots.LoadFaultDisputeGameNABI()

result, err := caller.SingleCall(ctx, rpcblock.Latest, batching.NewContractCall(contractAbi, addr, methodVersion))
if err != nil {
Expand Down Expand Up @@ -324,6 +328,7 @@ func (f *FaultDisputeGameContractLatest) addLocalDataTx(claimIdx uint64, data *t
data.GetIdent(),
new(big.Int).SetUint64(claimIdx),
new(big.Int).SetUint64(uint64(data.OracleOffset)),
data.OutputRootDAItem,
)
return call.ToTxCandidate()
}
Expand Down Expand Up @@ -592,6 +597,43 @@ func (f *FaultDisputeGameContractLatest) decodeClaim(result *batching.CallResult
}
}

func (f *FaultDisputeGameContractLatest) GetNBits(ctx context.Context) (uint64, error) {
defer f.metrics.StartContractRequest("GetNBits")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodNBits))
if err != nil {
return 0, fmt.Errorf("failed to fetch nbits: %w", err)
}
return result.GetBigInt(0).Uint64(), nil
}

func (f *FaultDisputeGameContractLatest) GetMaxAttackBranch(ctx context.Context) (uint64, error) {
defer f.metrics.StartContractRequest("GetMaxAttackBranch")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodMaxAttackBranch))
if err != nil {
return 0, fmt.Errorf("failed to fetch max attack branch: %w", err)
}
return result.GetBigInt(0).Uint64(), nil
}

func (f *FaultDisputeGameContractLatest) AttackV2Tx(ctx context.Context, parent types.Claim, attackBranch uint64, daType uint64, claims []byte) (txmgr.TxCandidate, error) {
nBits, err := f.GetNBits(ctx)
if err != nil {
return txmgr.TxCandidate{}, fmt.Errorf("failed to retrieve nbits: %w", err)
}
call := f.contract.Call(methodAttackV2,
parent.Value,
big.NewInt(int64(parent.ContractIndex)),
new(big.Int).SetUint64(attackBranch),
new(big.Int).SetUint64(daType),
claims)
return f.txWithBond(ctx, parent.Position.MoveN(nBits, attackBranch), call)
}

func (f *FaultDisputeGameContractLatest) StepV2Tx(claimIdx uint64, attackBranch uint64, stateData []byte, proof types.StepProof) (txmgr.TxCandidate, error) {
call := f.contract.Call(methodStepV2, new(big.Int).SetUint64(claimIdx), new(big.Int).SetUint64(attackBranch), stateData, proof)
return call.ToTxCandidate()
}

type FaultDisputeGameContract interface {
GetBalance(ctx context.Context, block rpcblock.Block) (*big.Int, common.Address, error)
GetBlockRange(ctx context.Context) (prestateBlock uint64, poststateBlock uint64, retErr error)
Expand Down Expand Up @@ -624,4 +666,8 @@ type FaultDisputeGameContract interface {
ResolveClaimTx(claimIdx uint64) (txmgr.TxCandidate, error)
CallResolve(ctx context.Context) (gameTypes.GameStatus, error)
ResolveTx() (txmgr.TxCandidate, error)
AttackV2Tx(ctx context.Context, parent types.Claim, attackBranch uint64, daType uint64, claims []byte) (txmgr.TxCandidate, error)
StepV2Tx(claimIdx uint64, attackBranch uint64, stateData []byte, proof types.StepProof) (txmgr.TxCandidate, error)
GetNBits(ctx context.Context) (uint64, error)
GetMaxAttackBranch(ctx context.Context) (uint64, error)
}
106 changes: 85 additions & 21 deletions op-challenger2/game/fault/contracts/faultdisputegame_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,27 +50,9 @@ const (
)

var versions = []contractVersion{
{
version: vers080,
loadAbi: func() *abi.ABI {
return mustParseAbi(faultDisputeGameAbi020)
},
},
{
version: vers0180,
loadAbi: func() *abi.ABI {
return mustParseAbi(faultDisputeGameAbi0180)
},
},
{
version: vers111,
loadAbi: func() *abi.ABI {
return mustParseAbi(faultDisputeGameAbi111)
},
},
{
version: versLatest,
loadAbi: snapshots.LoadFaultDisputeGameABI,
loadAbi: snapshots.LoadFaultDisputeGameNABI,
},
}

Expand Down Expand Up @@ -158,6 +140,24 @@ func TestSimpleGetters(t *testing.T) {
return game.CallResolve(context.Background())
},
},
{
methodAlias: "nBits",
method: methodNBits,
result: big.NewInt(2),
expected: uint64(2),
call: func(game FaultDisputeGameContract) (any, error) {
return game.GetNBits(context.Background())
},
},
{
methodAlias: "maxAttackBranch",
method: methodMaxAttackBranch,
result: big.NewInt(3),
expected: uint64(3),
call: func(game FaultDisputeGameContract) (any, error) {
return game.GetMaxAttackBranch(context.Background())
},
},
}
for _, version := range versions {
version := version
Expand Down Expand Up @@ -556,21 +556,30 @@ func TestGetStartingRootHash(t *testing.T) {
func TestFaultDisputeGame_UpdateOracleTx(t *testing.T) {
for _, version := range versions {
version := version
outputRootDAItem := faultTypes.DAItem{
DaType: faultTypes.CallDataType,
DataHash: common.Hash{},
Proof: []byte{},
}
vmStateDA := faultTypes.DAData{
PreDA: outputRootDAItem,
PostDA: outputRootDAItem,
}
t.Run(version.version, func(t *testing.T) {
t.Run("Local", func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
data := faultTypes.NewPreimageOracleData(common.Hash{0x01, 0xbc}.Bytes(), []byte{1, 2, 3, 4, 5, 6, 7}, 16)
data := faultTypes.NewPreimageOracleDAData(common.Hash{0x01, 0xbc}.Bytes(), []byte{1, 2, 3, 4, 5, 6, 7}, 16, vmStateDA, outputRootDAItem)
claimIdx := uint64(6)
stubRpc.SetResponse(fdgAddr, methodAddLocalData, rpcblock.Latest, []interface{}{
data.GetIdent(),
new(big.Int).SetUint64(claimIdx),
new(big.Int).SetUint64(uint64(data.OracleOffset)),
outputRootDAItem,
}, nil)
tx, err := game.UpdateOracleTx(context.Background(), claimIdx, data)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
})

t.Run("Global", func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
data := faultTypes.NewPreimageOracleData(common.Hash{0x02, 0xbc}.Bytes(), []byte{1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15}, 16)
Expand Down Expand Up @@ -787,3 +796,58 @@ func setupFaultDisputeGameTest(t *testing.T, version contractVersion) (*batching
require.NoError(t, err)
return stubRpc, game
}

func TestAttackV2Tx(t *testing.T) {
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
bond := big.NewInt(1044)
nBits := uint64(2)
claims := make([]byte, ((1<<nBits)-1)*32)
for i := range claims {
claims[i] = common.Hash{0xaa}[i%32]
}
attackBranch := big.NewInt(0)
daType := big.NewInt(1)
parent := faultTypes.Claim{ClaimData: faultTypes.ClaimData{Value: common.Hash{0xbb}}, ContractIndex: 111}
stubRpc.SetResponse(fdgAddr, methodNBits, rpcblock.Latest, nil, []interface{}{new(big.Int).SetUint64(nBits)})
stubRpc.SetResponse(fdgAddr, methodRequiredBond, rpcblock.Latest, []interface{}{parent.Position.MoveN(nBits, attackBranch.Uint64()).ToGIndex()}, []interface{}{bond})
stubRpc.SetResponse(fdgAddr, methodAttackV2, rpcblock.Latest, []interface{}{parent.Value, big.NewInt(111), attackBranch, daType, claims[:]}, nil)
tx, err := game.AttackV2Tx(context.Background(), parent, attackBranch.Uint64(), daType.Uint64(), claims[:])
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
require.Equal(t, bond, tx.Value)
})
}
}

func TestStepV2Tx(t *testing.T) {
for _, version := range versions {
version := version
t.Run(version.version, func(t *testing.T) {
stubRpc, game := setupFaultDisputeGameTest(t, version)
stateData := []byte{1, 2, 3}
vmProof := []byte{4, 5, 6, 7, 8, 9}
pre := faultTypes.DAItem{
DaType: faultTypes.CallDataType,
DataHash: [32]byte{0x01, 0x02, 0x03},
Proof: []byte("pre state proof"),
}
post := faultTypes.DAItem{
DaType: faultTypes.CallDataType,
DataHash: [32]byte{0x04, 0x05, 0x06},
Proof: []byte("post state proof"),
}
proofData := faultTypes.StepProof{
PreStateItem: pre,
PostStateItem: post,
VmProof: vmProof,
}
stubRpc.SetResponse(fdgAddr, methodStepV2, rpcblock.Latest, []interface{}{big.NewInt(111), big.NewInt(1), stateData, proofData}, nil)
tx, err := game.StepV2Tx(111, 1, stateData, proofData)
require.NoError(t, err)
stubRpc.VerifyTxCandidate(tx)
})
}
}
14 changes: 14 additions & 0 deletions op-challenger2/game/fault/types/position.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,17 @@ func bigMSB(x *big.Int) Depth {
}
return Depth(x.BitLen() - 1)
}

func (p Position) MoveN(depth uint64, branch uint64) Position {
return Position{
depth: p.depth + Depth(depth),
indexAtDepth: new(big.Int).Add(p.IndexAtDepth(), new(big.Int).Lsh(big.NewInt(int64(branch)), uint(depth))),
}
}

func (p Position) MoveRightN(number uint64) Position {
return Position{
depth: p.depth,
indexAtDepth: new(big.Int).Add(p.IndexAtDepth(), big.NewInt(int64(number))),
}
}
67 changes: 67 additions & 0 deletions op-challenger2/game/fault/types/position_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,70 @@ func TestRelativeMoves(t *testing.T) {
})
}
}

func TestMoveN(t *testing.T) {
tests0 := []struct {
startGIndex *big.Int
postGIndex *big.Int
}{
{bi(1), bi(4)},
{bi(2), bi(8)},
{bi(4), bi(16)},
}
depth := uint64(2)
branch := uint64(0)
for _, test := range tests0 {
pos := NewPositionFromGIndex(test.startGIndex)
result := pos.MoveN(depth, branch)
require.Equalf(t, test.postGIndex, result.ToGIndex(), "move GIndex %s, expected=%s, got=%s", test.startGIndex, test.postGIndex, result.ToGIndex())
}

tests1 := []struct {
startGIndex *big.Int
postGIndex *big.Int
}{
{bi(2), bi(12)},
{bi(4), bi(20)},
{bi(8), bi(36)},
}
depth = uint64(2)
branch = uint64(1)
for _, test := range tests1 {
pos := NewPositionFromGIndex(test.startGIndex)
result := pos.MoveN(depth, branch)
require.Equalf(t, test.postGIndex, result.ToGIndex(), "move GIndex %s, expected=%s, got=%s", test.startGIndex, test.postGIndex, result.ToGIndex())
}

tests3 := []struct {
startGIndex *big.Int
postGIndex *big.Int
}{
{bi(4), bi(28)},
{bi(8), bi(44)},
}
depth = uint64(2)
branch = uint64(3)
for _, test := range tests3 {
pos := NewPositionFromGIndex(test.startGIndex)
result := pos.MoveN(depth, branch)
require.Equalf(t, test.postGIndex, result.ToGIndex(), "move GIndex %s, expected=%s, got=%s", test.startGIndex, test.postGIndex, result.ToGIndex())
}
}

func TestMoveRightN(t *testing.T) {
tests := []struct {
startGIndex *big.Int
moveN uint64
postGIndex *big.Int
}{
{bi(2), 1, bi(3)},
{bi(4), 2, bi(6)},
{bi(8), 4, bi(12)},
{bi(17), 4, bi(21)},
}
for _, test := range tests {
pos := NewPositionFromGIndex(test.startGIndex)
result := pos.MoveRightN(test.moveN)
require.Equalf(t, test.postGIndex, result.ToGIndex(), "move GIndex %s, MoveN %s, expected=%s, got=%s", test.startGIndex, test.moveN, test.postGIndex, result.ToGIndex())
}
}
38 changes: 38 additions & 0 deletions op-challenger2/game/fault/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,33 @@ type PreimageOracleData struct {
BlobFieldIndex uint64
BlobCommitment []byte
BlobProof []byte
// multi-sec proof for VM step
VMStateDA DAData
// daitem for addLocalData
OutputRootDAItem DAItem
}

var (
CallDataType = big.NewInt(0)
BlobDataType = big.NewInt(1)
)

type DAItem struct {
DaType *big.Int
DataHash common.Hash
Proof []byte
}

// Provide DA proof for addLocalData's outputRoot and stepV2's stateRoot
type DAData struct {
PreDA DAItem
PostDA DAItem
}

type StepProof struct {
PreStateItem DAItem
PostStateItem DAItem
VmProof []byte
}

// GetIdent returns the ident for the preimage oracle data.
Expand Down Expand Up @@ -76,6 +103,17 @@ func NewPreimageOracleData(key []byte, data []byte, offset uint32) *PreimageOrac
}
}

func NewPreimageOracleDAData(key []byte, data []byte, offset uint32, vmStateDA DAData, outputRootDAItem DAItem) *PreimageOracleData {
return &PreimageOracleData{
IsLocal: len(key) > 0 && key[0] == byte(preimage.LocalKeyType),
OracleKey: key,
oracleData: data,
OracleOffset: offset,
VMStateDA: vmStateDA,
OutputRootDAItem: outputRootDAItem,
}
}

func NewPreimageOracleBlobData(key []byte, data []byte, offset uint32, fieldIndex uint64, commitment []byte, proof []byte) *PreimageOracleData {
return &PreimageOracleData{
IsLocal: false,
Expand Down
Loading

0 comments on commit 3e2f67b

Please sign in to comment.