Skip to content

Commit 87e46b3

Browse files
committed
fixes arbitrum: add OnBlockUpdate, fix call order
1 parent 9e317c1 commit 87e46b3

File tree

3 files changed

+111
-17
lines changed

3 files changed

+111
-17
lines changed

core/blockchain.go

+2
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ type BlockchainLogger interface {
172172
// `td` is the total difficulty prior to `block`.
173173
OnBlockStart(block *types.Block, td *big.Int, finalized *types.Header, safe *types.Header)
174174
OnBlockEnd(err error)
175+
// OnBlockUpdate is called when a block header was modified after the OnBlockStart. This happens when mining, but also on some Layer2
176+
OnBlockUpdate(block *types.Block, td *big.Int)
175177
OnGenesisBlock(genesis *types.Block, alloc GenesisAlloc)
176178
}
177179

eth/tracers/firehose.go

+105-17
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"encoding/base64"
66
"encoding/hex"
7+
"encoding/json"
78
"errors"
89
"fmt"
910
"io"
@@ -110,7 +111,6 @@ func (f *Firehose) OnBlockStart(b *types.Block, td *big.Int, finalized *types.He
110111
firehoseDebug("block start number=%d hash=%s", b.NumberU64(), b.Hash())
111112

112113
f.ensureNotInBlock()
113-
114114
f.block = &pbeth.Block{
115115
Hash: b.Hash().Bytes(),
116116
Number: b.Number().Uint64(),
@@ -127,6 +127,14 @@ func (f *Firehose) OnBlockStart(b *types.Block, td *big.Int, finalized *types.He
127127
f.blockFinality.populateFromChain(finalized)
128128
}
129129

130+
func (f *Firehose) OnBlockUpdate(b *types.Block, td *big.Int) {
131+
f.ensureInBlock()
132+
f.block.Hash = b.Hash().Bytes()
133+
f.block.Number = b.Number().Uint64()
134+
f.block.Header = newBlockHeaderFromChainBlock(b, firehoseBigIntFromNative(new(big.Int).Add(td, b.Difficulty())))
135+
f.block.Size = b.Size()
136+
}
137+
130138
func (f *Firehose) OnBlockEnd(err error) {
131139
firehoseDebug("block ending err=%s", errorView(err))
132140

@@ -164,6 +172,14 @@ func (f *Firehose) CaptureTxStart(evm *vm.EVM, tx *types.Transaction) {
164172
}
165173

166174
f.captureTxStart(tx, tx.Hash(), from, to, evm.IsPrecompileAddr)
175+
176+
switch tx.Type() {
177+
case types.ArbitrumDepositTxType, types.ArbitrumSubmitRetryableTxType, types.ArbitrumInternalTxType:
178+
firehoseDebug("Adding simulated root call to arbitrum tx hash=%s type=%d gas=%d input=%s", tx.Hash(), tx.Type(), tx.Gas(), inputView(tx.Data()))
179+
f.callStart("root", pbeth.CallType_CALL, from, *tx.To(), tx.Data(), tx.Gas(), tx.Value())
180+
}
181+
return
182+
167183
}
168184

169185
// captureTxStart is used internally a two places, in the normal "tracer" and in the "OnGenesisBlock",
@@ -198,6 +214,12 @@ func (f *Firehose) CaptureTxEnd(receipt *types.Receipt, err error) {
198214
firehoseDebug("trx ending")
199215
f.ensureInBlockAndInTrx()
200216

217+
switch receipt.Type {
218+
case types.ArbitrumDepositTxType, types.ArbitrumSubmitRetryableTxType, types.ArbitrumInternalTxType:
219+
firehoseDebug("Closing simulated root call to arbitrum tx type=%d", receipt.Type)
220+
f.callEnd("root", nil, receipt.GasUsed, err)
221+
}
222+
201223
f.block.TransactionTraces = append(f.block.TransactionTraces, f.completeTransaction(receipt))
202224

203225
// The reset must be done as the very last thing as the CallStack needs to be
@@ -275,6 +297,7 @@ func (f *Firehose) removeLogBlockIndexOnStateRevertedCalls() {
275297
if call.StateReverted {
276298
for _, log := range call.Logs {
277299
log.BlockIndex = 0
300+
log.Index = 0
278301
}
279302
}
280303
}
@@ -299,26 +322,45 @@ func (f *Firehose) assignOrdinalToReceiptLogs() {
299322
})
300323

301324
if len(callLogs) != len(receiptsLogs) {
325+
j, err := json.Marshal(trx)
326+
if err != nil {
327+
firehoseDebug("error marshalling trx during panic handling: %s", err)
328+
}
329+
330+
firehoseDebug("got this transaction: %s", string(j))
302331
panic(fmt.Errorf(
303332
"mismatch between Firehose call logs and Ethereum transaction receipt logs, transaction receipt has %d logs but there is %d Firehose call logs",
304333
len(receiptsLogs),
305334
len(callLogs),
306335
))
307336
}
308337

338+
var txIndex uint32 = 0
339+
for _, log := range callLogs {
340+
log.Index = txIndex
341+
txIndex++
342+
}
343+
309344
for i := 0; i < len(callLogs); i++ {
310345
callLog := callLogs[i]
311346
receiptsLog := receiptsLogs[i]
312347

313348
result := &validationResult{}
314349
// Ordinal must **not** be checked as we are assigning it here below after the validations
315350
validateBytesField(result, "Address", callLog.Address, receiptsLog.Address)
316-
validateUint32Field(result, "Index", callLog.Index, receiptsLog.Index)
351+
//validateUint32Field(result, "Index", callLog.Index, receiptsLog.Index)
317352
validateUint32Field(result, "BlockIndex", callLog.BlockIndex, receiptsLog.BlockIndex)
318353
validateBytesField(result, "Data", callLog.Data, receiptsLog.Data)
319354
validateArrayOfBytesField(result, "Topics", callLog.Topics, receiptsLog.Topics)
320355

321356
if len(result.failures) > 0 {
357+
for i, ll := range callLogs {
358+
result.failures = append(result.failures, fmt.Sprintf("log %d, idx %d", i, ll.Index))
359+
}
360+
for i, ll := range receiptsLogs {
361+
result.failures = append(result.failures, fmt.Sprintf("theirs: log %d, idx %d", i, ll.Index))
362+
}
363+
322364
result.panicOnAnyFailures("mismatch between Firehose call log and Ethereum transaction receipt log at index %d", i)
323365
}
324366

@@ -653,7 +695,15 @@ func (f *Firehose) newBalanceChange(tag string, address common.Address, oldValue
653695
}
654696
}
655697

698+
// maybe useful later
699+
// var arbosAddress = common.HexToAddress("A4B05FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
700+
656701
func (f *Firehose) OnNonceChange(a common.Address, prev, new uint64) {
702+
if new == prev {
703+
firehoseDebug("skipping NonceChange new==prev (%d)", prev)
704+
return
705+
}
706+
657707
f.ensureInBlockAndInTrx()
658708

659709
activeCall := f.callStack.Peek()
@@ -698,51 +748,79 @@ func (f *Firehose) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev
698748
}
699749

700750
func (f *Firehose) OnStorageChange(a common.Address, k, prev, new common.Hash) {
701-
f.ensureInBlockAndInTrxAndInCall()
751+
firehoseTrace("on storage change addr=%s", a)
752+
753+
f.ensureInBlockAndInTrx()
702754

703755
activeCall := f.callStack.Peek()
704-
activeCall.StorageChanges = append(activeCall.StorageChanges, &pbeth.StorageChange{
756+
change := &pbeth.StorageChange{
705757
Address: a.Bytes(),
706758
Key: k.Bytes(),
707759
OldValue: prev.Bytes(),
708760
NewValue: new.Bytes(),
709761
Ordinal: f.blockOrdinal.Next(),
710-
})
762+
}
763+
764+
// There is an initial gas consumption happening will the call is not yet started, we track it manually
765+
if activeCall == nil {
766+
f.deferredCallState.storageChanges = append(f.deferredCallState.storageChanges, change)
767+
return
768+
}
769+
770+
activeCall.StorageChanges = append(activeCall.StorageChanges, change)
771+
711772
}
712773

713774
func (f *Firehose) OnLog(l *types.Log) {
714-
f.ensureInBlockAndInTrxAndInCall()
775+
776+
firehoseTrace("on log addr=%s topics=%d, txindex=%d", l.Address, len(l.Topics), f.transactionLogIndex)
777+
778+
f.ensureInBlockAndInTrx()
715779

716780
topics := make([][]byte, len(l.Topics))
717781
for i, topic := range l.Topics {
718782
topics[i] = topic.Bytes()
719783
}
720784

721-
activeCall := f.callStack.Peek()
722-
activeCall.Logs = append(activeCall.Logs, &pbeth.Log{
785+
log := &pbeth.Log{
723786
Address: l.Address.Bytes(),
724787
Topics: topics,
725788
Data: l.Data,
726789
Index: f.transactionLogIndex,
727790
BlockIndex: uint32(l.Index),
728791
Ordinal: f.blockOrdinal.Next(),
729-
})
792+
}
730793

731794
f.transactionLogIndex++
795+
796+
activeCall := f.callStack.Peek()
797+
if activeCall == nil {
798+
f.deferredCallState.logs = append(f.deferredCallState.logs, log)
799+
return
800+
}
801+
802+
activeCall.Logs = append(activeCall.Logs, log)
732803
}
733804

734805
func (f *Firehose) OnNewAccount(a common.Address) {
735-
f.ensureInBlockAndInTrxAndInCall()
806+
f.ensureInBlockAndInTrx()
736807

737808
if f.isPrecompiledAddr(a) {
738809
return
739810
}
740811

741-
activeCall := f.callStack.Peek()
742-
activeCall.AccountCreations = append(activeCall.AccountCreations, &pbeth.AccountCreation{
812+
acc := &pbeth.AccountCreation{
743813
Account: a.Bytes(),
744814
Ordinal: f.blockOrdinal.Next(),
745-
})
815+
}
816+
817+
activeCall := f.callStack.Peek()
818+
if activeCall == nil {
819+
f.deferredCallState.accountCreations = append(f.deferredCallState.accountCreations, acc)
820+
return
821+
}
822+
823+
activeCall.AccountCreations = append(activeCall.AccountCreations)
746824
}
747825

748826
func (f *Firehose) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {
@@ -869,6 +947,7 @@ func (f *Firehose) ensureInCall() {
869947
}
870948

871949
func (f *Firehose) panicNotInState(msg string) string {
950+
firehoseDebugPrintStack()
872951
panic(fmt.Errorf("%s (inBlock=%t, inTransaction=%t, inCall=%t)", msg, f.block != nil, f.transaction != nil, f.callStack.HasActiveCall()))
873952
}
874953

@@ -1307,9 +1386,12 @@ func (s *CallStack) Peek() *pbeth.Call {
13071386
// that is recorded before the Call has been started. This happens on the "starting"
13081387
// portion of the call/created.
13091388
type DeferredCallState struct {
1310-
balanceChanges []*pbeth.BalanceChange
1311-
gasChanges []*pbeth.GasChange
1312-
nonceChanges []*pbeth.NonceChange
1389+
balanceChanges []*pbeth.BalanceChange
1390+
gasChanges []*pbeth.GasChange
1391+
storageChanges []*pbeth.StorageChange
1392+
logs []*pbeth.Log
1393+
accountCreations []*pbeth.AccountCreation
1394+
nonceChanges []*pbeth.NonceChange
13131395
}
13141396

13151397
func NewDeferredCallState() *DeferredCallState {
@@ -1328,6 +1410,9 @@ func (d *DeferredCallState) MaybePopulateCallAndReset(source string, call *pbeth
13281410
// We must happen because it's populated at beginning of the call as well as at the very end
13291411
call.BalanceChanges = append(call.BalanceChanges, d.balanceChanges...)
13301412
call.GasChanges = append(call.GasChanges, d.gasChanges...)
1413+
call.StorageChanges = append(call.StorageChanges, d.storageChanges...)
1414+
call.Logs = append(call.Logs, d.logs...)
1415+
call.AccountCreations = append(call.AccountCreations, d.accountCreations...)
13311416
call.NonceChanges = append(call.NonceChanges, d.nonceChanges...)
13321417

13331418
d.Reset()
@@ -1336,12 +1421,15 @@ func (d *DeferredCallState) MaybePopulateCallAndReset(source string, call *pbeth
13361421
}
13371422

13381423
func (d *DeferredCallState) IsEmpty() bool {
1339-
return len(d.balanceChanges) == 0 && len(d.gasChanges) == 0 && len(d.nonceChanges) == 0
1424+
return len(d.balanceChanges) == 0 && len(d.gasChanges) == 0 && len(d.nonceChanges) == 0 && len(d.storageChanges) == 0 && len(d.logs) == 0
13401425
}
13411426

13421427
func (d *DeferredCallState) Reset() {
13431428
d.balanceChanges = nil
13441429
d.gasChanges = nil
1430+
d.storageChanges = nil
1431+
d.logs = nil
1432+
d.accountCreations = nil
13451433
d.nonceChanges = nil
13461434
}
13471435

eth/tracers/printer.go

+4
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ func (p *Printer) OnBlockEnd(err error) {
8787
fmt.Printf("OnBlockEnd: err=%v\n", err)
8888
}
8989

90+
func (f *Printer) OnBlockUpdate(b *types.Block, td *big.Int) {
91+
fmt.Printf("OnBlockUpdate: b=%v, td=%v\n", b.NumberU64(), td)
92+
}
93+
9094
func (p *Printer) OnGenesisBlock(b *types.Block, alloc core.GenesisAlloc) {
9195
fmt.Printf("OnGenesisBlock: b=%v, allocLength=%d\n", b.NumberU64(), len(alloc))
9296
}

0 commit comments

Comments
 (0)