forked from ledgerwatch/erigon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
opcode_tracer.go
742 lines (635 loc) · 22 KB
/
opcode_tracer.go
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
package commands
import (
"bufio"
"context"
"encoding/gob"
"encoding/json"
"fmt"
"os"
"os/signal"
"strconv"
"syscall"
"time"
"github.com/holiman/uint256"
"github.com/ledgerwatch/log/v3"
"github.com/spf13/cobra"
chain2 "github.com/nebojsa94/erigon/erigon-lib/chain"
libcommon "github.com/nebojsa94/erigon/erigon-lib/common"
"github.com/nebojsa94/erigon/erigon-lib/kv"
"github.com/nebojsa94/erigon/erigon-lib/kv/kvcfg"
"github.com/nebojsa94/erigon/erigon-lib/kv/mdbx"
"github.com/nebojsa94/erigon/common/debug"
"github.com/nebojsa94/erigon/consensus"
"github.com/nebojsa94/erigon/consensus/ethash"
"github.com/nebojsa94/erigon/core"
"github.com/nebojsa94/erigon/core/rawdb"
"github.com/nebojsa94/erigon/core/state"
"github.com/nebojsa94/erigon/core/types"
"github.com/nebojsa94/erigon/core/vm"
"github.com/nebojsa94/erigon/eth/ethconfig"
"github.com/nebojsa94/erigon/turbo/rpchelper"
"github.com/nebojsa94/erigon/turbo/snapshotsync/freezeblocks"
)
var (
numBlocks uint64
saveOpcodes bool
saveBBlocks bool
)
func init() {
withBlock(opcodeTracerCmd)
withDataDir(opcodeTracerCmd)
opcodeTracerCmd.Flags().Uint64Var(&numBlocks, "numBlocks", 1, "number of blocks to run the operation on")
opcodeTracerCmd.Flags().BoolVar(&saveOpcodes, "saveOpcodes", false, "set to save the opcodes")
opcodeTracerCmd.Flags().BoolVar(&saveBBlocks, "saveBBlocks", false, "set to save the basic blocks")
rootCmd.AddCommand(opcodeTracerCmd)
}
var opcodeTracerCmd = &cobra.Command{
Use: "opcodeTracer",
Short: "Re-executes historical transactions in read-only mode and traces them at the opcode level",
RunE: func(cmd *cobra.Command, args []string) error {
logger := log.New("opcode-tracer", genesis.Config.ChainID)
return OpcodeTracer(genesis, block, chaindata, numBlocks, saveOpcodes, saveBBlocks, logger)
},
}
// const MaxUint = ^uint(0)
// const MaxUint64 = ^uint64(0)
const MaxUint16 = ^uint16(0)
type opcode struct {
Pc uint16
Op vm.OpCode
//StackTop *stack.Stack
//StackTop []uint256.Int
//RetStackTop RetStackTop
//MaxStack int
//MaxRStack int
Fault string
}
type RetStackTop []uint32
type tx struct {
From libcommon.Address
To libcommon.Address
TxHash *libcommon.Hash
CodeHash *libcommon.Hash
Opcodes sliceOpcodes
Input sliceBytes //ByteSliceAsHex
Bblocks sliceBblocks
Fault string //a fault set by CaptureEnd
OpcodeFault string //a fault set by CaptureState
TxAddr string
CodeSize int
Depth int
lastPc16 uint16
lastOp vm.OpCode
Create bool
}
// types for slices are necessary for easyjson's generated un/marshalers
type sliceBytes []byte
type sliceOpcodes []opcode
type sliceBblocks []bblock
//easyjson:json
type slicePtrTx []*tx
type opcodeTracer struct {
Txs slicePtrTx
fsumWriter *bufio.Writer
stack slicePtrTx
txsInDepth []int16
saveOpcodes bool
saveBblocks bool
blockNumber uint64
depth int
env *vm.EVM
}
func NewOpcodeTracer(blockNum uint64, saveOpcodes bool, saveBblocks bool) *opcodeTracer {
res := new(opcodeTracer)
res.txsInDepth = make([]int16, 1, 4)
res.stack = make([]*tx, 0, 8)
res.Txs = make([]*tx, 0, 64)
res.saveOpcodes = saveOpcodes
res.saveBblocks = saveBblocks
res.blockNumber = blockNum
return res
}
// prepare to trace a new block
func resetOpcodeTracer(ot *opcodeTracer) {
//sanity check
// at the end of a block, when no transactions are running, depth == 0. Our tracking should reflect that.
if len(ot.txsInDepth) != 1 || len(ot.stack) != 0 {
panic(fmt.Sprintf("At end of block, tracer should be almost reset but isn't: lstack=%d, lTID=%d, TID[0]=%d",
len(ot.stack), len(ot.txsInDepth), ot.txsInDepth[0]))
}
// allocate new storage, allow past storage to be GCed
ot.Txs = make([]*tx, 0, 64)
// reset the counter of Txs at depth 0
ot.txsInDepth[0] = 0
}
type bblock struct {
Start uint16
End uint16
}
type bblockDump struct {
Tx *libcommon.Hash
TxAddr *string
CodeHash *libcommon.Hash
Bblocks *sliceBblocks
OpcodeFault *string
Fault *string
Create bool
CodeSize int
}
type blockTxs struct {
BlockNum uint64
Txs slicePtrTx
}
func (ot *opcodeTracer) CaptureTxStart(gasLimit uint64) {}
func (ot *opcodeTracer) CaptureTxEnd(restGas uint64) {}
func (ot *opcodeTracer) captureStartOrEnter(from, to libcommon.Address, create bool, input []byte) {
//fmt.Fprint(ot.summary, ot.lastLine)
// When a CaptureStart is called, a Tx is starting. Create its entry in our list and initialize it with the partial data available
//calculate the "address" of the Tx in its tree
ltid := len(ot.txsInDepth)
if ltid-1 != ot.depth {
panic(fmt.Sprintf("Wrong addr slice depth: d=%d, slice len=%d", ot.depth, ltid))
}
ot.txsInDepth[ot.depth]++
ot.txsInDepth = append(ot.txsInDepth, 0)
ls := len(ot.stack)
txAddr := ""
if ls > 0 {
txAddr = ot.stack[ls-1].TxAddr + "-" + strconv.Itoa(int(ot.txsInDepth[ot.depth])) // fmt.Sprintf("%s-%d", ot.stack[ls-1].TxAddr, ot.txsInDepth[depth])
} else {
txAddr = strconv.Itoa(int(ot.txsInDepth[ot.depth]))
}
newTx := tx{From: from, To: to, Create: create, Input: input, Depth: ot.depth, TxAddr: txAddr, lastOp: 0xfe, lastPc16: MaxUint16}
ot.Txs = append(ot.Txs, &newTx)
// take note in our own stack that the tx stack has grown
ot.stack = append(ot.stack, &newTx)
}
func (ot *opcodeTracer) CaptureStart(env *vm.EVM, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
ot.env = env
ot.depth = 0
ot.captureStartOrEnter(from, to, create, input)
}
func (ot *opcodeTracer) CaptureEnter(typ vm.OpCode, from libcommon.Address, to libcommon.Address, precompile bool, create bool, input []byte, gas uint64, value *uint256.Int, code []byte) {
ot.depth++
ot.captureStartOrEnter(from, to, create, input)
}
func (ot *opcodeTracer) captureEndOrExit(err error) {
// When a CaptureEnd is called, a Tx has finished. Pop our stack
ls := len(ot.stack)
currentEntry := ot.stack[ls-1]
ot.stack = ot.stack[:ls-1]
ot.txsInDepth = ot.txsInDepth[:ot.depth+1]
// sanity check: depth of stack == depth reported by system
if ls-1 != ot.depth || ot.depth != currentEntry.Depth {
panic(fmt.Sprintf("End of Tx at d=%d but stack has d=%d and entry has d=%d", ot.depth, ls, currentEntry.Depth))
}
// Close the last bblock
if ot.saveBblocks {
lseg := len(currentEntry.Bblocks)
if lseg > 0 {
cee := currentEntry.Bblocks[lseg-1].End
if cee != 0 && cee != currentEntry.lastPc16 {
panic(fmt.Sprintf("CaptureEnd wanted to close last bblock with %d but already contains %d", currentEntry.lastPc16, cee))
}
currentEntry.Bblocks[lseg-1].End = currentEntry.lastPc16
//fmt.Fprintf(ot.fsumWriter,"Bblock %d ends\n", lseg)
}
}
errstr := ""
if err != nil {
errstr = err.Error()
currentEntry.Fault = errstr
}
}
func (ot *opcodeTracer) CaptureEnd(output []byte, usedGas uint64, err error) {
ot.captureEndOrExit(err)
}
func (ot *opcodeTracer) CaptureExit(output []byte, usedGas uint64, err error) {
ot.captureEndOrExit(err)
ot.depth--
}
func (ot *opcodeTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, opDepth int, err error) {
//CaptureState sees the system as it is before the opcode is run. It seems to never get an error.
contract := scope.Contract
//sanity check
if pc > uint64(MaxUint16) {
panic(fmt.Sprintf("PC is bigger than uint16! pc=%d=0x%x", pc, pc))
}
pc16 := uint16(pc)
currentTxHash := ot.env.TxContext().TxHash
currentTxDepth := opDepth - 1
ls := len(ot.stack)
currentEntry := ot.stack[ls-1]
//sanity check
if currentEntry.Depth != currentTxDepth {
panic(fmt.Sprintf("Depth should be the same but isn't: current tx's %d, current entry's %d", currentTxDepth, currentEntry.Depth))
}
// is the Tx entry still not fully initialized?
if currentEntry.TxHash == nil {
// CaptureStart creates the entry for a new Tx, but doesn't have access to EVM data, like the Tx Hash
// here we ASSUME that the tx entry was recently created by CaptureStart
// AND that this is the first CaptureState that has happened since then
// AND that both Captures are for the same transaction
// AND that we can't go into another depth without executing at least 1 opcode
// Note that the only connection between CaptureStart and CaptureState that we can notice is that the current op's depth should be lastTxEntry.Depth+1
// fill in the missing data in the entry
currentEntry.TxHash = new(libcommon.Hash)
currentEntry.TxHash.SetBytes(currentTxHash.Bytes())
currentEntry.CodeHash = new(libcommon.Hash)
currentEntry.CodeHash.SetBytes(contract.CodeHash.Bytes())
currentEntry.CodeSize = len(contract.Code)
if ot.saveOpcodes {
currentEntry.Opcodes = make([]opcode, 0, 200)
}
//fmt.Fprintf(ot.w, "%sFilled in TxHash\n", strings.Repeat("\t",depth))
if ot.saveBblocks {
currentEntry.Bblocks = make(sliceBblocks, 0, 10)
}
}
// prepare the opcode's stack for saving
//stackTop := &stack.Stack{Data: make([]uint256.Int, 0, 7)}//stack.New()
// the most stack positions consumed by any opcode is 7
//for i:= min(7, st.Len()-1); i>=0; i-- {
// stackTop.Push(st.Back(i))
//}
//THIS VERSION SHOULD BE FASTER BUT IS UNTESTED
//stackTop := make([]uint256.Int, 7, 7)
//sl := st.Len()
//minl := min(7, sl)
//startcopy := sl-minl
//stackTop := &stack.Stack{Data: make([]uint256.Int, minl, minl)}//stack.New()
//copy(stackTop.Data, st.Data[startcopy:sl])
//sanity check
if currentEntry.OpcodeFault != "" {
panic(fmt.Sprintf("Running opcodes but fault is already set. txFault=%s, opFault=%v, op=%s",
currentEntry.OpcodeFault, err, op.String()))
}
// if it is a Fault, check whether we already have a record of the opcode. If so, just add the flag to it
errstr := ""
if err != nil {
errstr = err.Error()
currentEntry.OpcodeFault = errstr
}
faultAndRepeated := false
if pc16 == currentEntry.lastPc16 && op == currentEntry.lastOp {
//it's a repeated opcode. We assume this only happens when it's a Fault.
if err == nil {
panic(fmt.Sprintf("Duplicate opcode with no fault. bn=%d txaddr=%s pc=%x op=%s",
ot.blockNumber, currentEntry.TxAddr, pc, op.String()))
}
faultAndRepeated = true
//ot.fsumWriter.WriteString("Fault for EXISTING opcode\n")
//ot.fsumWriter.Flush()
if ot.saveOpcodes {
lo := len(currentEntry.Opcodes)
currentEntry.Opcodes[lo-1].Fault = errstr
}
} else {
// it's a new opcode
if ot.saveOpcodes {
newOpcode := opcode{pc16, op, errstr}
currentEntry.Opcodes = append(currentEntry.Opcodes, newOpcode)
}
}
// detect and store bblocks
if ot.saveBblocks {
// PC discontinuities can only happen because of a PUSH (which is followed by the data to be pushed) or a JUMP (which lands into a JUMPDEST)
// Therefore, after a PC discontinuity we either have op==JUMPDEST or lastOp==PUSH
// Only the JUMPDEST case is a real control flow discontinuity and therefore starts a new bblock
lseg := len(currentEntry.Bblocks)
isFirstBblock := lseg == 0
isContinuous := pc16 == currentEntry.lastPc16+1 || currentEntry.lastOp.IsPush()
if isFirstBblock || !isContinuous {
// Record the end of the past bblock, if there is one
if !isFirstBblock {
//fmt.Fprintf(ot.fsumWriter,"Bblock %d ends\n", lseg)
currentEntry.Bblocks[lseg-1].End = currentEntry.lastPc16
//fmt.Printf("End\t%x\t%s\n", lastPc, lastOp.String())
}
// Start a new bblock
// Note that it can happen that a new bblock starts with an opcode that triggers an Out Of Gas fault, so it'd be a bblock with only 1 opcode (JUMPDEST)
// The only case where we want to avoid creating a new bblock is if the opcode is repeated, because then it was already in the previous bblock
if !faultAndRepeated {
//fmt.Fprintf(ot.fsumWriter,"Bblock %d begins\n", lseg+1)
currentEntry.Bblocks = append(currentEntry.Bblocks, bblock{Start: pc16})
//fmt.Printf("Start\t%x\t%s\n", o.Pc.uint64, o.Op.String())
//sanity check
// we're starting a bblock, so either we're in PC=0 or we have OP=JUMPDEST
if pc16 != 0 && op.String() != "JUMPDEST" {
panic(fmt.Sprintf("Bad bblock? lastpc=%x, lastOp=%s; pc=%x, op=%s; bn=%d txaddr=%s tx=%d-%s",
currentEntry.lastPc16, currentEntry.lastOp.String(), pc, op.String(), ot.blockNumber, currentEntry.TxAddr, currentEntry.Depth, currentEntry.TxHash.String()))
}
}
}
}
currentEntry.lastPc16 = pc16
currentEntry.lastOp = op
}
func (ot *opcodeTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, opDepth int, err error) {
// CaptureFault sees the system as it is after the fault happens
// CaptureState might have already recorded the opcode before it failed. Let's centralize the processing there.
ot.CaptureState(pc, op, gas, cost, scope, nil, opDepth, err)
}
type segPrefix struct {
BlockNum uint64
NumTxs uint
}
// OpcodeTracer re-executes historical transactions in read-only mode
// and traces them at the opcode level
func OpcodeTracer(genesis *types.Genesis, blockNum uint64, chaindata string, numBlocks uint64,
saveOpcodes bool, saveBblocks bool, logger log.Logger) error {
blockNumOrig := blockNum
startTime := time.Now()
sigs := make(chan os.Signal, 1)
interruptCh := make(chan bool, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigs
interruptCh <- true
}()
ot := NewOpcodeTracer(blockNum, saveOpcodes, saveBblocks)
chainDb := mdbx.MustOpen(chaindata)
defer chainDb.Close()
historyDb := chainDb
historyTx, err1 := historyDb.BeginRo(context.Background())
if err1 != nil {
return err1
}
defer historyTx.Rollback()
var historyV3 bool
chainDb.View(context.Background(), func(tx kv.Tx) (err error) {
historyV3, err = kvcfg.HistoryV3.Enabled(tx)
if err != nil {
return err
}
return nil
})
blockReader := freezeblocks.NewBlockReader(freezeblocks.NewRoSnapshots(ethconfig.BlocksFreezing{Enabled: false}, "", log.New()), nil /* BorSnapshots */)
chainConfig := genesis.Config
vmConfig := vm.Config{Tracer: ot, Debug: true}
noOpWriter := state.NewNoopWriter()
interrupt := false
var fsum *os.File
var chanOpcodes chan blockTxs
if saveOpcodes {
chanOpcodes = make(chan blockTxs, 10)
defer close(chanOpcodes)
go func() {
defer debug.LogPanic()
var fops *os.File
var fopsWriter *bufio.Writer
var fopsEnc *gob.Encoder
var e error
for blockTxs := range chanOpcodes {
bn := blockTxs.BlockNum
bnStr := strconv.Itoa(int(bn))
if fops != nil && bn%1000 == 0 {
fopsWriter.Flush()
fops.Close()
fops = nil
}
if fops == nil {
if fops, e = os.Create("./opcodes-" + bnStr); e != nil {
panic(e)
}
fopsWriter = bufio.NewWriter(fops)
fopsEnc = gob.NewEncoder(fopsWriter)
}
if e = fopsEnc.Encode(blockTxs); e != nil {
panic(e)
}
}
if fopsWriter != nil {
fopsWriter.Flush()
fops.Close()
fops = nil
}
lo := len(chanOpcodes)
if lo > 0 {
panic(fmt.Sprintf("Opcode channel not empty at the end: lo=%d", lo))
}
}()
}
var chanSegDump chan bblockDump
var chanSegPrefix chan segPrefix
if saveBblocks {
chanSegDump = make(chan bblockDump, 1024)
defer close(chanSegDump)
chanSegPrefix = make(chan segPrefix, 1024)
defer close(chanSegPrefix)
go func() {
var f *os.File
var fWriter *bufio.Writer
var fwEnc *json.Encoder
var e error
for sp := range chanSegPrefix {
bn := sp.BlockNum
bnStr := strconv.Itoa(int(bn))
if f != nil && bn%1000 == 0 {
if _, e = fWriter.WriteString("\n}"); e != nil {
panic(e)
}
fWriter.Flush()
f.Close()
f = nil
}
if f == nil {
if f, e = os.Create("./bblocks-" + bnStr + ".json"); e != nil {
panic(e)
}
fWriter = bufio.NewWriter(f)
fwEnc = json.NewEncoder(fWriter)
}
if _, e = fWriter.WriteString(",\n\"" + bnStr + "\":[\n"); e != nil {
panic(e)
}
for i := uint(0); i < sp.NumTxs; i++ {
if i != 0 {
if _, e = fWriter.WriteString(","); e != nil {
panic(e)
}
}
sd := <-chanSegDump
if e = fwEnc.Encode(sd); e != nil {
panic(e)
}
}
if _, e = fWriter.WriteString("]"); e != nil {
panic(e)
}
}
if fWriter != nil {
if _, e = fWriter.WriteString("\n}"); e != nil {
panic(e)
}
fWriter.Flush()
f.Close()
f = nil
}
lsp := len(chanSegPrefix)
lsd := len(chanSegDump)
if lsp > 0 || lsd > 0 {
panic(fmt.Sprintf("Bblock channels not empty at the end: sp=%d sd=%d", lsp, lsd))
}
}()
}
timeLastBlock := startTime
blockNumLastReport := blockNum
for !interrupt {
var block *types.Block
if err := chainDb.View(context.Background(), func(tx kv.Tx) (err error) {
block, err = blockReader.BlockByNumber(context.Background(), tx, blockNum)
return err
}); err != nil {
panic(err)
}
if block == nil {
break
}
bnStr := strconv.Itoa(int(blockNum))
if fsum == nil {
var err error
if fsum, err = os.Create("./summary-" + bnStr); err != nil {
return err
}
ot.fsumWriter = bufio.NewWriter(fsum)
}
dbstate, err := rpchelper.CreateHistoryStateReader(historyTx, block.NumberU64(), 0, historyV3, chainConfig.ChainName)
if err != nil {
return err
}
intraBlockState := state.New(dbstate)
getHeader := func(hash libcommon.Hash, number uint64) *types.Header {
return rawdb.ReadHeader(historyTx, hash, number)
}
receipts, err1 := runBlock(ethash.NewFullFaker(), intraBlockState, noOpWriter, noOpWriter, chainConfig, getHeader, block, vmConfig, false, logger)
if err1 != nil {
return err1
}
if chainConfig.IsByzantium(block.NumberU64()) {
receiptSha := types.DeriveSha(receipts)
if receiptSha != block.ReceiptHash() {
return fmt.Errorf("mismatched receipt headers for block %d", block.NumberU64())
}
}
// go through the traces and act on them
if saveBblocks {
sp := segPrefix{blockNum, uint(len(ot.Txs))}
chanSegPrefix <- sp
}
chanBblocksIsBlocking := false
for i := range ot.Txs {
t := ot.Txs[i]
if saveBblocks {
sd := bblockDump{t.TxHash, &t.TxAddr, t.CodeHash, &t.Bblocks, &t.OpcodeFault, &t.Fault, t.Create, t.CodeSize}
//fsegEnc.Encode(sd)
chanBblocksIsBlocking = len(chanSegDump) == cap(chanSegDump)-1
chanSegDump <- sd
}
for j := range t.Opcodes {
o := &t.Opcodes[j]
//only print to the summary the opcodes that are interesting
isOpFault := o.Fault != ""
if isOpFault {
fmt.Fprintf(ot.fsumWriter, "Opcode FAULT\tb=%d taddr=%s TxF=%s opF=%s tx=%s\n", blockNum, t.TxAddr, t.Fault, t.OpcodeFault, t.TxHash.String())
fmt.Fprint(ot.fsumWriter, "\n")
}
}
isTxFault := t.Fault != ""
if !isTxFault {
continue
}
if t.OpcodeFault == t.Fault {
continue
}
if t.Fault == "out of gas" {
// frequent and uninteresting
continue
}
ths := ""
if t.TxHash != nil {
ths = t.TxHash.String()
}
fmt.Fprintf(ot.fsumWriter, "Tx FAULT\tb=%d opF=%s\tTxF=%s\ttaddr=%s\ttx=%s\n", blockNum, t.OpcodeFault, t.Fault, t.TxAddr, ths)
}
if chanBblocksIsBlocking {
log.Debug("Channel for bblocks got full and caused some blocking", "block", blockNum)
}
if saveOpcodes {
// just save everything
bt := blockTxs{blockNum, ot.Txs}
chanOpcodesIsBlocking := len(chanOpcodes) == cap(chanOpcodes)-1
chanOpcodes <- bt
if chanOpcodesIsBlocking {
log.Debug("Channel for opcodes got full and caused some blocking", "block", blockNum)
}
}
blockNum++
resetOpcodeTracer(ot)
ot.blockNumber = blockNum
// Check for interrupts
select {
case interrupt = <-interruptCh:
fmt.Println("interrupted, please wait for cleanup...")
default:
}
if blockNum >= blockNumOrig+numBlocks {
interrupt = true
}
if interrupt || blockNum%1000 == 0 {
bps := float64(blockNum-blockNumLastReport) / time.Since(timeLastBlock).Seconds()
timeLastBlock = time.Now()
blockNumLastReport = blockNum
bpss := fmt.Sprintf("%.2f", bps)
log.Info("Checked", "blocks", blockNum, "blocks/s", bpss)
ot.fsumWriter.Flush()
fi, err := fsum.Stat()
if err != nil {
return err
}
// if the summary file for the just-finished range of blocks is empty, delete it
if fi.Size() == 0 {
os.Remove(fi.Name())
}
fsum.Close()
fsum = nil
}
}
bps := float64(blockNum-blockNumOrig) / time.Since(startTime).Seconds()
bpss := fmt.Sprintf("%.2f", bps)
log.Info("Checked", "blocks", blockNum, "next time specify --block", blockNum, "duration", time.Since(startTime), "blocks/s", bpss)
return nil
}
func runBlock(engine consensus.Engine, ibs *state.IntraBlockState, txnWriter state.StateWriter, blockWriter state.StateWriter,
chainConfig *chain2.Config, getHeader func(hash libcommon.Hash, number uint64) *types.Header, block *types.Block, vmConfig vm.Config, trace bool, logger log.Logger) (types.Receipts, error) {
header := block.Header()
vmConfig.TraceJumpDest = true
gp := new(core.GasPool).AddGas(block.GasLimit()).AddBlobGas(chainConfig.GetMaxBlobGasPerBlock())
usedGas := new(uint64)
usedBlobGas := new(uint64)
var receipts types.Receipts
core.InitializeBlockExecution(engine, nil, header, chainConfig, ibs, logger)
rules := chainConfig.Rules(block.NumberU64(), block.Time())
for i, tx := range block.Transactions() {
ibs.SetTxContext(tx.Hash(), block.Hash(), i)
receipt, _, err := core.ApplyTransaction(chainConfig, core.GetHashFn(header, getHeader), engine, nil, gp, ibs, txnWriter, header, tx, usedGas, usedBlobGas, vmConfig)
if err != nil {
return nil, fmt.Errorf("could not apply tx %d [%x] failed: %w", i, tx.Hash(), err)
}
if trace {
fmt.Printf("tx idx %d, gas used %d\n", i, receipt.GasUsed)
}
receipts = append(receipts, receipt)
}
if !vmConfig.ReadOnly {
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
tx := block.Transactions()
if _, _, _, err := engine.FinalizeAndAssemble(chainConfig, header, ibs, tx, block.Uncles(), receipts, block.Withdrawals(), nil, nil, nil, logger); err != nil {
return nil, fmt.Errorf("finalize of block %d failed: %w", block.NumberU64(), err)
}
if err := ibs.CommitBlock(rules, blockWriter); err != nil {
return nil, fmt.Errorf("committing block %d failed: %w", block.NumberU64(), err)
}
}
return receipts, nil
}