Skip to content

Commit ba15a6a

Browse files
feat(taiko-client): check if the block is preconfirmed before calling setHead (#18864)
Co-authored-by: maskpp <[email protected]>
1 parent 61fff4e commit ba15a6a

File tree

13 files changed

+306
-41
lines changed

13 files changed

+306
-41
lines changed

Diff for: .release-please-manifest.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"packages/relayer": "0.12.0",
1010
"packages/snaefell-ui": "1.1.0",
1111
"packages/supplementary-contracts": "1.0.0",
12-
"packages/taiko-client": "0.43.1",
12+
"packages/taiko-client": "1.0.1",
1313
"packages/taikoon-ui": "1.3.0",
1414
"packages/ui-lib": "1.0.0"
1515
}

Diff for: go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ require (
318318
sigs.k8s.io/yaml v1.3.0 // indirect
319319
)
320320

321-
replace github.com/ethereum/go-ethereum v1.14.11 => github.com/taikoxyz/taiko-geth v1.12.1-0.20250204084657-54ca2198a63b
321+
replace github.com/ethereum/go-ethereum v1.14.11 => github.com/taikoxyz/taiko-geth v1.12.1-0.20250205040456-832713781df8
322322

323323
replace github.com/ethereum-optimism/optimism v1.7.4 => github.com/taikoxyz/optimism v0.0.0-20250128202721-1b986d622e1a
324324

Diff for: go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -888,8 +888,8 @@ github.com/taikoxyz/hive v0.0.0-20240827015317-405b241dd082 h1:ymZR+Y88LOnA8i3Ke
888888
github.com/taikoxyz/hive v0.0.0-20240827015317-405b241dd082/go.mod h1:RHnIu3EFehrWX3JhFAMQSXD5uz7l0xaNroTzXrap7EQ=
889889
github.com/taikoxyz/optimism v0.0.0-20250128202721-1b986d622e1a h1:33/0g4tyyoxFoengwPv6sEseEcEGITqfXbCyruSyXxY=
890890
github.com/taikoxyz/optimism v0.0.0-20250128202721-1b986d622e1a/go.mod h1:V0VCkKtCzuaJH6qcL75SRcbdlakM9LhurMEJUhO6VXA=
891-
github.com/taikoxyz/taiko-geth v1.12.1-0.20250204084657-54ca2198a63b h1:KEXW+q9KzW7VPAiPvxbGrY3Ian30tPCVmjgkHpNDeZc=
892-
github.com/taikoxyz/taiko-geth v1.12.1-0.20250204084657-54ca2198a63b/go.mod h1:+l/fr42Mma+xBnhefL/+z11/hcmJ2egl+ScIVPjhc7E=
891+
github.com/taikoxyz/taiko-geth v1.12.1-0.20250205040456-832713781df8 h1:cgZhkcg8yi6CkhMQUX9Z/Au2qQFcDwm6isibeq/iMMM=
892+
github.com/taikoxyz/taiko-geth v1.12.1-0.20250205040456-832713781df8/go.mod h1:+l/fr42Mma+xBnhefL/+z11/hcmJ2egl+ScIVPjhc7E=
893893
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
894894
github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo=
895895
github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4=

Diff for: packages/taiko-client/bindings/pacaya/.githead

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
f5938d4b6e54069d11df6b40ede3ad83012cf0e6
1+
1713be26d6226695f393da883a8cf22d4152dcc9

Diff for: packages/taiko-client/driver/chain_syncer/blob/blocks_inserter/common.go

+154-14
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
package blocksinserter
22

33
import (
4+
"bytes"
45
"context"
56
"errors"
67
"fmt"
8+
"math/big"
79

810
"github.com/ethereum/go-ethereum/beacon/engine"
911
"github.com/ethereum/go-ethereum/common"
1012
consensus "github.com/ethereum/go-ethereum/consensus/taiko"
1113
"github.com/ethereum/go-ethereum/core/types"
14+
"github.com/ethereum/go-ethereum/crypto"
1215
"github.com/ethereum/go-ethereum/log"
16+
"github.com/ethereum/go-ethereum/miner"
1317
"github.com/ethereum/go-ethereum/rlp"
1418

19+
"github.com/taikoxyz/taiko-mono/packages/taiko-client/bindings/encoding"
1520
"github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/rpc"
1621
"github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/utils"
1722
)
@@ -30,32 +35,68 @@ func createPayloadAndSetHead(
3035
"parentHash", meta.Parent.Hash(),
3136
"l1Origin", meta.L1Origin,
3237
)
38+
// Insert a TaikoL2.anchorV2 / TaikoAnchor.anchorV3 transaction at transactions list head,
39+
// then encode the transactions list.
40+
txListBytes, err := rlp.EncodeToBytes(append([]*types.Transaction{anchorTx}, meta.Txs...))
41+
if err != nil {
42+
log.Error("Encode txList error", "blockID", meta.BlockID, "error", err)
43+
return nil, err
44+
}
45+
46+
// If the Pacaya block is preconfirmed, we don't need to insert it again.
47+
if meta.BlockID.Cmp(new(big.Int).SetUint64(rpc.PacayaClients.ForkHeight)) >= 0 {
48+
header, err := isBlockPreconfirmed(ctx, rpc, meta, txListBytes, anchorTx)
49+
if err != nil {
50+
log.Debug("Failed to check if the block is preconfirmed", "error", err)
51+
} else if header != nil {
52+
// Update the l1Origin and headL1Origin cursor for that preconfirmed block.
53+
meta.L1Origin.L2BlockHash = header.Hash()
54+
if _, err := rpc.L2.UpdateL1Origin(ctx, meta.L1Origin); err != nil {
55+
return nil, fmt.Errorf("failed to update L1 origin: %w", err)
56+
}
57+
if _, err := rpc.L2.SetHeadL1Origin(ctx, meta.L1Origin.BlockID); err != nil {
58+
return nil, fmt.Errorf("failed to write head L1 origin: %w", err)
59+
}
60+
61+
log.Info(
62+
"🧬 The block is preconfirmed",
63+
"blockID", meta.BlockID,
64+
"hash", header.Hash(),
65+
"coinbase", header.Coinbase,
66+
"timestamp", header.Time,
67+
"anchorBlockID", meta.AnchorBlockID,
68+
"anchorBlockHash", meta.AnchorBlockHash,
69+
"baseFee", utils.WeiToEther(header.BaseFee),
70+
)
71+
72+
return encoding.ToExecutableData(header), nil
73+
}
74+
}
3375

3476
payload, err := createExecutionPayloads(
3577
ctx,
3678
rpc,
3779
meta.createExecutionPayloadsMetaData,
80+
txListBytes,
3881
anchorTx,
3982
)
4083
if err != nil {
4184
return nil, fmt.Errorf("failed to create execution payloads: %w", err)
4285
}
4386

44-
var (
45-
lastVerifiedBlockHash common.Hash
46-
)
87+
var lastVerifiedBlockHash common.Hash
4788
lastVerifiedTS, err := rpc.GetLastVerifiedTransitionPacaya(ctx)
4889
if err != nil {
4990
lastVerifiedBlockInfo, err := rpc.GetLastVerifiedBlockOntake(ctx)
5091
if err != nil {
5192
return nil, fmt.Errorf("failed to fetch last verified block: %w", err)
5293
}
5394

54-
if payload.Number > lastVerifiedBlockInfo.BlockId {
95+
if meta.BlockID.Uint64() > lastVerifiedBlockInfo.BlockId {
5596
lastVerifiedBlockHash = lastVerifiedBlockInfo.BlockHash
5697
}
5798
} else {
58-
if payload.Number > lastVerifiedTS.BlockId {
99+
if meta.BlockID.Uint64() > lastVerifiedTS.BlockId {
59100
lastVerifiedBlockHash = lastVerifiedTS.Ts.BlockHash
60101
}
61102
}
@@ -84,16 +125,9 @@ func createExecutionPayloads(
84125
ctx context.Context,
85126
rpc *rpc.Client,
86127
meta *createExecutionPayloadsMetaData,
128+
txListBytes []byte,
87129
anchorTx *types.Transaction,
88130
) (payloadData *engine.ExecutableData, err error) {
89-
// Insert a TaikoL2.anchor / TaikoL2.anchorV2 transaction at transactions list head
90-
txListBytes, err := rlp.EncodeToBytes(append([]*types.Transaction{anchorTx}, meta.Txs...))
91-
if err != nil {
92-
log.Error("Encode txList error", "blockID", meta.BlockID, "error", err)
93-
return nil, err
94-
}
95-
96-
fc := &engine.ForkchoiceStateV1{HeadBlockHash: meta.ParentHash}
97131
attributes := &engine.PayloadAttributes{
98132
Timestamp: meta.Timestamp,
99133
Random: meta.Difficulty,
@@ -128,7 +162,11 @@ func createExecutionPayloads(
128162
)
129163

130164
// Step 1, prepare a payload
131-
fcRes, err := rpc.L2Engine.ForkchoiceUpdate(ctx, fc, attributes)
165+
fcRes, err := rpc.L2Engine.ForkchoiceUpdate(
166+
ctx,
167+
&engine.ForkchoiceStateV1{HeadBlockHash: meta.ParentHash},
168+
attributes,
169+
)
132170
if err != nil {
133171
return nil, fmt.Errorf("failed to update fork choice: %w", err)
134172
}
@@ -168,3 +206,105 @@ func createExecutionPayloads(
168206

169207
return payload, nil
170208
}
209+
210+
// isBlockPreconfirmed checks if the block is preconfirmed.
211+
func isBlockPreconfirmed(
212+
ctx context.Context,
213+
rpc *rpc.Client,
214+
meta *createPayloadAndSetHeadMetaData,
215+
txListBytes []byte,
216+
anchorTx *types.Transaction,
217+
) (*types.Header, error) {
218+
var blockID = new(big.Int).Add(meta.Parent.Number, common.Big1)
219+
block, err := rpc.L2.BlockByNumber(ctx, blockID)
220+
if err != nil {
221+
return nil, fmt.Errorf("failed to get block by number %d: %w", blockID, err)
222+
}
223+
224+
if block == nil {
225+
return nil, fmt.Errorf("block not found by number %d", blockID)
226+
}
227+
228+
var (
229+
txListHash = crypto.Keccak256Hash(txListBytes[:])
230+
args = &miner.BuildPayloadArgs{
231+
Parent: meta.Parent.Hash(),
232+
Timestamp: block.Time(),
233+
FeeRecipient: block.Coinbase(),
234+
Random: block.MixDigest(),
235+
Withdrawals: make([]*types.Withdrawal, 0),
236+
Version: engine.PayloadV2,
237+
TxListHash: &txListHash,
238+
}
239+
id = args.Id()
240+
)
241+
executableData, err := rpc.L2Engine.GetPayload(ctx, &id)
242+
if err != nil {
243+
return nil, fmt.Errorf("failed to get payload: %w", err)
244+
}
245+
246+
defer func() {
247+
if err != nil {
248+
log.Warn("Invalid preconfirmed block", "blockID", blockID, "coinbase", executableData.FeeRecipient, "reason", err)
249+
}
250+
}()
251+
252+
if executableData.BlockHash != block.Hash() {
253+
err = fmt.Errorf("block hash mismatch: %s != %s", executableData.BlockHash, block.Hash())
254+
return nil, err
255+
}
256+
if block.ParentHash() != meta.ParentHash {
257+
err = fmt.Errorf("parent hash mismatch: %s != %s", block.ParentHash(), meta.ParentHash)
258+
return nil, err
259+
}
260+
if block.Transactions().Len() == 0 {
261+
err = errors.New("transactions list is empty")
262+
return nil, err
263+
}
264+
if block.Transactions()[0].Hash() != anchorTx.Hash() {
265+
err = fmt.Errorf("anchor transaction mismatch: %s != %s", block.Transactions()[0].Hash(), anchorTx.Hash())
266+
return nil, err
267+
}
268+
if block.UncleHash() != types.EmptyUncleHash {
269+
err = fmt.Errorf("uncle hash mismatch: %s != %s", block.UncleHash(), types.EmptyUncleHash)
270+
return nil, err
271+
}
272+
if block.Coinbase() != meta.SuggestedFeeRecipient {
273+
err = fmt.Errorf("coinbase mismatch: %s != %s", block.Coinbase(), meta.SuggestedFeeRecipient)
274+
return nil, err
275+
}
276+
if block.Difficulty().Cmp(common.Big0) != 0 {
277+
err = fmt.Errorf("difficulty mismatch: %s != 0", block.Difficulty())
278+
return nil, err
279+
}
280+
if block.MixDigest() != meta.Difficulty {
281+
err = fmt.Errorf("mixDigest mismatch: %s != %s", block.MixDigest(), meta.Difficulty)
282+
return nil, err
283+
}
284+
if block.Number().Uint64() != meta.BlockID.Uint64() {
285+
err = fmt.Errorf("block number mismatch: %d != %d", block.Number(), meta.BlockID)
286+
return nil, err
287+
}
288+
if block.GasLimit() != meta.GasLimit+consensus.AnchorGasLimit {
289+
err = fmt.Errorf("gas limit mismatch: %d != %d", block.GasLimit(), meta.GasLimit+consensus.AnchorGasLimit)
290+
return nil, err
291+
}
292+
if block.Time() != meta.Timestamp {
293+
err = fmt.Errorf("timestamp mismatch: %d != %d", block.Time(), meta.Timestamp)
294+
return nil, err
295+
}
296+
if !bytes.Equal(block.Extra(), meta.ExtraData) {
297+
err = fmt.Errorf("extra data mismatch: %s != %s", block.Extra(), meta.ExtraData)
298+
return nil, err
299+
}
300+
if block.BaseFee().Cmp(meta.BaseFee) != 0 {
301+
err = fmt.Errorf("base fee mismatch: %s != %s", block.BaseFee(), meta.BaseFee)
302+
return nil, err
303+
}
304+
if block.Withdrawals().Len() != 0 {
305+
err = fmt.Errorf("withdrawals mismatch: %d != 0", block.Withdrawals().Len())
306+
return nil, err
307+
}
308+
309+
return block.Header(), nil
310+
}

Diff for: packages/taiko-client/driver/chain_syncer/blob/blocks_inserter/pacaya.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ func (i *BlocksInserterPacaya) InsertBlocks(
190190

191191
// Get transactions in the block.
192192
txs := types.Transactions{}
193-
if txListCursor+int(blockInfo.NumTransactions) > len(allTxs) {
193+
if txListCursor+int(blockInfo.NumTransactions) <= len(allTxs) {
194194
txs = allTxs[txListCursor : txListCursor+int(blockInfo.NumTransactions)]
195195
}
196196

@@ -234,6 +234,7 @@ func (i *BlocksInserterPacaya) InsertBlocks(
234234
"blockID", blockID,
235235
"hash", lastPayloadData.BlockHash,
236236
"transactions", len(lastPayloadData.Transactions),
237+
"timestamp", lastPayloadData.Timestamp,
237238
"baseFee", utils.WeiToGWei(lastPayloadData.BaseFeePerGas),
238239
"withdrawals", len(lastPayloadData.Withdrawals),
239240
"batchID", meta.GetBatchID(),
@@ -312,7 +313,10 @@ func (i *BlocksInserterPacaya) InsertPreconfBlockFromTransactionsBatch(
312313
if err != nil {
313314
return nil, fmt.Errorf("failed to calculate difficulty: %w", err)
314315
}
315-
extraData := encoding.EncodeBaseFeeConfig(baseFeeConfig)
316+
var (
317+
extraData = encoding.EncodeBaseFeeConfig(baseFeeConfig)
318+
txs = i.txListDecompressor.TryDecompress(i.rpc.L2.ChainID, executableData.Transactions, true, true)
319+
)
316320

317321
payloadData, err := createPayloadAndSetHead(
318322
ctx,
@@ -332,10 +336,7 @@ func (i *BlocksInserterPacaya) InsertPreconfBlockFromTransactionsBatch(
332336
L1BlockHeight: nil,
333337
L1BlockHash: common.Hash{},
334338
},
335-
Txs: append(
336-
types.Transactions{anchorTx},
337-
i.txListDecompressor.TryDecompress(i.rpc.L2.ChainID, executableData.Transactions, true, true)...,
338-
),
339+
Txs: txs,
339340
Withdrawals: make([]*types.Withdrawal, 0),
340341
BaseFee: baseFee,
341342
},
@@ -354,7 +355,7 @@ func (i *BlocksInserterPacaya) InsertPreconfBlockFromTransactionsBatch(
354355
"⏰ New preconfirmation L2 block inserted",
355356
"blockID", executableData.Number,
356357
"hash", payloadData.BlockHash,
357-
"transactions", len(payloadData.Transactions[0]),
358+
"transactions", txs.Len(),
358359
"baseFee", utils.WeiToGWei(payloadData.BaseFeePerGas),
359360
"withdrawals", len(payloadData.Withdrawals),
360361
)

Diff for: packages/taiko-client/driver/chain_syncer/blob/syncer.go

+1
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ func (s *Syncer) onBlockProposed(
265265
"l1Hash", meta.GetRawBlockHash(),
266266
"batchID", meta.Pacaya().GetBatchID(),
267267
"lastBlockID", lastBlockID,
268+
"lastTimestamp", meta.Pacaya().GetLastBlockTimestamp(),
268269
"blocks", len(meta.Pacaya().GetBlocks()),
269270
)
270271
if err := s.blocksInserterPacaya.InsertBlocks(ctx, meta, tx, endIter); err != nil {

0 commit comments

Comments
 (0)