1
1
package blocksinserter
2
2
3
3
import (
4
+ "bytes"
4
5
"context"
5
6
"errors"
6
7
"fmt"
8
+ "math/big"
7
9
8
10
"github.com/ethereum/go-ethereum/beacon/engine"
9
11
"github.com/ethereum/go-ethereum/common"
10
12
consensus "github.com/ethereum/go-ethereum/consensus/taiko"
11
13
"github.com/ethereum/go-ethereum/core/types"
14
+ "github.com/ethereum/go-ethereum/crypto"
12
15
"github.com/ethereum/go-ethereum/log"
16
+ "github.com/ethereum/go-ethereum/miner"
13
17
"github.com/ethereum/go-ethereum/rlp"
14
18
19
+ "github.com/taikoxyz/taiko-mono/packages/taiko-client/bindings/encoding"
15
20
"github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/rpc"
16
21
"github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/utils"
17
22
)
@@ -30,32 +35,68 @@ func createPayloadAndSetHead(
30
35
"parentHash" , meta .Parent .Hash (),
31
36
"l1Origin" , meta .L1Origin ,
32
37
)
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
+ }
33
75
34
76
payload , err := createExecutionPayloads (
35
77
ctx ,
36
78
rpc ,
37
79
meta .createExecutionPayloadsMetaData ,
80
+ txListBytes ,
38
81
anchorTx ,
39
82
)
40
83
if err != nil {
41
84
return nil , fmt .Errorf ("failed to create execution payloads: %w" , err )
42
85
}
43
86
44
- var (
45
- lastVerifiedBlockHash common.Hash
46
- )
87
+ var lastVerifiedBlockHash common.Hash
47
88
lastVerifiedTS , err := rpc .GetLastVerifiedTransitionPacaya (ctx )
48
89
if err != nil {
49
90
lastVerifiedBlockInfo , err := rpc .GetLastVerifiedBlockOntake (ctx )
50
91
if err != nil {
51
92
return nil , fmt .Errorf ("failed to fetch last verified block: %w" , err )
52
93
}
53
94
54
- if payload . Number > lastVerifiedBlockInfo .BlockId {
95
+ if meta . BlockID . Uint64 () > lastVerifiedBlockInfo .BlockId {
55
96
lastVerifiedBlockHash = lastVerifiedBlockInfo .BlockHash
56
97
}
57
98
} else {
58
- if payload . Number > lastVerifiedTS .BlockId {
99
+ if meta . BlockID . Uint64 () > lastVerifiedTS .BlockId {
59
100
lastVerifiedBlockHash = lastVerifiedTS .Ts .BlockHash
60
101
}
61
102
}
@@ -84,16 +125,9 @@ func createExecutionPayloads(
84
125
ctx context.Context ,
85
126
rpc * rpc.Client ,
86
127
meta * createExecutionPayloadsMetaData ,
128
+ txListBytes []byte ,
87
129
anchorTx * types.Transaction ,
88
130
) (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 }
97
131
attributes := & engine.PayloadAttributes {
98
132
Timestamp : meta .Timestamp ,
99
133
Random : meta .Difficulty ,
@@ -128,7 +162,11 @@ func createExecutionPayloads(
128
162
)
129
163
130
164
// 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
+ )
132
170
if err != nil {
133
171
return nil , fmt .Errorf ("failed to update fork choice: %w" , err )
134
172
}
@@ -168,3 +206,105 @@ func createExecutionPayloads(
168
206
169
207
return payload , nil
170
208
}
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
+ }
0 commit comments