From 3d9e698315c5f907202eda54a93d39761ea2e5a7 Mon Sep 17 00:00:00 2001 From: Po Date: Sat, 28 Sep 2024 03:05:29 +0800 Subject: [PATCH] Test: decode tx from TxCall --- .../game/fault/contracts/faultdisputegame.go | 32 ++++++++++++------- .../fault/contracts/faultdisputegame_test.go | 19 +++++++++-- op-service/sources/batching/event_call.go | 2 -- op-service/sources/batching/multicall.go | 4 +-- .../sources/batching/test/event_stub.go | 4 +-- op-service/sources/batching/tx_call.go | 26 ++++++++------- op-service/sources/batching/tx_call_test.go | 31 ++++++++++++------ 7 files changed, 76 insertions(+), 42 deletions(-) diff --git a/op-challenger/game/fault/contracts/faultdisputegame.go b/op-challenger/game/fault/contracts/faultdisputegame.go index 5a6a66a2f014..6618b02bd9d1 100644 --- a/op-challenger/game/fault/contracts/faultdisputegame.go +++ b/op-challenger/game/fault/contracts/faultdisputegame.go @@ -55,6 +55,7 @@ var ( methodL2BlockNumberChallenged = "l2BlockNumberChallenged" methodL2BlockNumberChallenger = "l2BlockNumberChallenger" methodChallengeRootL2Block = "challengeRootL2Block" + subClaimField = "_claim" ) var ( @@ -174,10 +175,6 @@ func (f *FaultDisputeGameContractLatest) GetBlockRange(ctx context.Context) (pre return } -func (f *FaultDisputeGameContractLatest) GetContract() *batching.BoundContract { - return f.contract -} - type GameMetadata struct { L1Head common.Hash L2BlockNum uint64 @@ -450,7 +447,6 @@ func (f *FaultDisputeGameContractLatest) GetAllClaims(ctx context.Context, block func (f *FaultDisputeGameContractLatest) GetSubClaims(ctx context.Context, block rpcblock.Block, aggClaim *types.Claim) ([]common.Hash, error) { defer f.metrics.StartContractRequest("GetAllSubClaims")() - // findMoveTransaction filter, err := bindings.NewFaultDisputeGameFilterer(f.contract.Addr(), f.multiCaller) if err != nil { return nil, err @@ -469,16 +465,30 @@ func (f *FaultDisputeGameContractLatest) GetSubClaims(ctx context.Context, block } txHash := moveIter.Event.Raw.TxHash - // todo: replace hardcoded nary, method name - nary := 1 - result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, batching.NewTxCall(f.contract.Abi(), txHash, "move")) + // todo: replace hardcoded method name + txCall := batching.NewTxCall(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 subClaims []common.Hash - // We should start from 2 du to the signature of move(Claim _disputed, uint256 _challengeIndex, Claim _claim) - for i := 2; i < nary+2; i++ { - subClaims = append(subClaims, result.GetHash(i)) + + if len(txn.BlobHashes()) > 0 { + // todo: fetch Blobs and unpack it into subClaims + } 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-subclaims + claim := *abi.ConvertType(inputMap[subClaimField], new([32]byte)).(*[32]byte) + subClaims = append(subClaims, claim) } return subClaims, nil diff --git a/op-challenger/game/fault/contracts/faultdisputegame_test.go b/op-challenger/game/fault/contracts/faultdisputegame_test.go index 863a9480f66a..387b34b84175 100644 --- a/op-challenger/game/fault/contracts/faultdisputegame_test.go +++ b/op-challenger/game/fault/contracts/faultdisputegame_test.go @@ -324,6 +324,7 @@ 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) { @@ -344,7 +345,7 @@ func TestGetSubClaims(t *testing.T) { block := rpcblock.ByNumber(42) stubRpc.SetResponse(fdgAddr, methodClaimCount, block, nil, []interface{}{big.NewInt(int64(len(expectedClaims)))}) - name := "Move" + eventName := "Move" fdgAbi := version.loadAbi() var challgenIndex []interface{} @@ -354,7 +355,7 @@ func TestGetSubClaims(t *testing.T) { query := [][]interface{}{challgenIndex, claim, address} txHash := common.Hash{0xff} - query = append([][]interface{}{{fdgAbi.Events[name].ID}}, query...) + query = append([][]interface{}{{fdgAbi.Events[eventName].ID}}, query...) topics, err := abi.MakeTopics(query...) var queryTopics []common.Hash @@ -373,7 +374,19 @@ func TestGetSubClaims(t *testing.T) { stubRpc.SetFilterLogResponse(topics, fdgAddr, block, out) contractCall := batching.NewContractCall(fdgAbi, fdgAddr, "move", claim0.ClaimData.Value, challgenIndex[0], claim0.ClaimData.Value, true) - packed, err := contractCall.Pack() + 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) diff --git a/op-service/sources/batching/event_call.go b/op-service/sources/batching/event_call.go index b537cfd003df..01e09b9fc359 100644 --- a/op-service/sources/batching/event_call.go +++ b/op-service/sources/batching/event_call.go @@ -13,8 +13,6 @@ type EventCall struct { to []common.Address } -var _ Call = (*EventCall)(nil) - func NewEventCall(q ethereum.FilterQuery) *EventCall { return &EventCall{ topics: q.Topics, diff --git a/op-service/sources/batching/multicall.go b/op-service/sources/batching/multicall.go index e2d708764960..88b500791c7a 100644 --- a/op-service/sources/batching/multicall.go +++ b/op-service/sources/batching/multicall.go @@ -84,8 +84,7 @@ func (m *MultiCaller) Call(ctx context.Context, block rpcblock.Block, calls ...C return callResults, nil } -// FilterLogs filters contract logs for past blocks, returning the necessary -// channels to construct a strongly typed bound iterator on top of them. +// implment LogFilterer interface func (m *MultiCaller) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { call := NewEventCall(q) results, err := m.SingleCall(ctx, rpcblock.ByNumber(q.FromBlock.Uint64()), call) @@ -97,6 +96,7 @@ func (m *MultiCaller) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([ return out, nil } +// implment LogFilterer interface func (m *MultiCaller) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { return nil, errors.New("unimplemented") } diff --git a/op-service/sources/batching/test/event_stub.go b/op-service/sources/batching/test/event_stub.go index c09f2f257fd9..970ec7ced092 100644 --- a/op-service/sources/batching/test/event_stub.go +++ b/op-service/sources/batching/test/event_stub.go @@ -33,13 +33,13 @@ func (c *expectedFilterLogsCall) Matches(rpcMethod string, args ...interface{}) to := args[1].(common.Address) if to != c.to { - return fmt.Errorf("expected contract address %v but was %v", c.topics, topics) + return fmt.Errorf("expected contract address %v but was %v", c.to, to) } return c.err } func (c *expectedFilterLogsCall) Execute(t *testing.T, out interface{}) error { - j, err := json.Marshal((c.outputs)) + j, err := json.Marshal(c.outputs) require.NoError(t, err) json.Unmarshal(j, out) return c.err diff --git a/op-service/sources/batching/tx_call.go b/op-service/sources/batching/tx_call.go index 4b4ad02fb4ff..b1a4b60bcdf9 100644 --- a/op-service/sources/batching/tx_call.go +++ b/op-service/sources/batching/tx_call.go @@ -1,12 +1,11 @@ package batching import ( - "fmt" - "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" ) @@ -16,8 +15,6 @@ type TxCall struct { Method string } -var _ Call = (*TxCall)(nil) - func NewTxCall(abi *abi.ABI, txhash common.Hash, method string) *TxCall { return &TxCall{ Abi: abi, @@ -38,8 +35,8 @@ func (b *TxCall) ToBatchElemCreator() (BatchElementCreator, error) { } func (c *TxCall) HandleResult(result interface{}) (*CallResult, error) { - out, err := c.Unpack(*result.(*hexutil.Bytes)) - return out, err + res := result.(*hexutil.Bytes) + return &CallResult{out: []interface{}{*res}}, nil } func (c *TxCall) DecodeTxParams(data []byte) (map[string]interface{}, error) { @@ -54,12 +51,17 @@ func (c *TxCall) DecodeTxParams(data []byte) (map[string]interface{}, error) { return v, nil } -func (c *TxCall) Unpack(hex hexutil.Bytes) (*CallResult, error) { - inputs := c.Abi.Methods[c.Method].Inputs - - out, err := inputs.UnpackValues(hex[4:]) +func (c *TxCall) DecodeToTx(res *CallResult) (*types.Transaction, error) { + txn := new(types.Transaction) + hex := res.out[0].(hexutil.Bytes) + err := txn.UnmarshalBinary(hex) if err != nil { - return nil, fmt.Errorf("failed to unpack inputs: %w", err) + return nil, err } - return &CallResult{out: out}, nil + return txn, nil +} + +func (c *TxCall) UnpackCallData(txn *types.Transaction) (map[string]interface{}, error) { + input := txn.Data() + return c.DecodeTxParams(input) } diff --git a/op-service/sources/batching/tx_call_test.go b/op-service/sources/batching/tx_call_test.go index 1154266376c9..a3ba4eebe4f8 100644 --- a/op-service/sources/batching/tx_call_test.go +++ b/op-service/sources/batching/tx_call_test.go @@ -8,10 +8,11 @@ import ( "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/ethereum-optimism/optimism/op-service/sources/batching/test" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" ) -func TestTxCall_ToCallArgs(t *testing.T) { +func TestDecodeTxCall(t *testing.T) { addr := common.Address{0xbd} testAbi, err := test.ERC20MetaData.GetAbi() require.NoError(t, err) @@ -26,14 +27,9 @@ func TestTxCall_ToCallArgs(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedAmount, unpackedMap["amount"]) require.Equal(t, expectedSpender, unpackedMap["spender"]) - - unpacked, err := call.Unpack(packed) - require.NoError(t, err) - require.Equal(t, expectedSpender, unpacked.GetAddress(0)) - require.Equal(t, expectedAmount, unpacked.GetBigInt(1)) } -func TestGetTxCalldata(t *testing.T) { +func TestUnpackTxCalldata(t *testing.T) { expectedSpender := common.Address{0xcc} expectedAmount := big.NewInt(1234444) txHash := common.Hash{0x11} @@ -42,7 +38,17 @@ func TestGetTxCalldata(t *testing.T) { testAbi, err := test.ERC20MetaData.GetAbi() require.NoError(t, err) contractCall := NewContractCall(testAbi, addr, "approve", expectedSpender, expectedAmount) - packed, err := contractCall.Pack() + inputData, err := contractCall.Pack() + tx := types.NewTx(&types.LegacyTx{ + Nonce: 0, + GasPrice: big.NewInt(11111), + Gas: 1111, + To: &addr, + Value: big.NewInt(111), + Data: inputData, + }) + require.NoError(t, err) + packed, err := tx.MarshalBinary() require.NoError(t, err) stub := test.NewRpcStub(t) @@ -52,6 +58,11 @@ func TestGetTxCalldata(t *testing.T) { txCall := NewTxCall(testAbi, txHash, "approve") result, err := caller.SingleCall(context.Background(), rpcblock.Latest, txCall) require.NoError(t, err) - require.Equal(t, expectedSpender, result.GetAddress(0)) - require.Equal(t, expectedAmount, result.GetBigInt(1)) + + decodedTx, err := txCall.DecodeToTx(result) + require.NoError(t, err) + unpackedMap, err := txCall.UnpackCallData(decodedTx) + require.NoError(t, err) + require.Equal(t, expectedSpender, unpackedMap["spender"]) + require.Equal(t, expectedAmount, unpackedMap["amount"]) }