Ported various improvements done in the Firehose tracer in other supported chains

This commit is contained in:
Matthieu Vachon 2024-04-17 15:58:46 -04:00
parent 4105834d36
commit ad7fdb83cc
3 changed files with 724 additions and 194 deletions

File diff suppressed because it is too large Load diff

View file

@ -4,15 +4,22 @@ import (
"encoding/json"
"fmt"
"math/big"
"os"
"reflect"
"slices"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
pbeth "github.com/ethereum/go-ethereum/pb/sf/ethereum/type/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/exp/maps"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"
)
@ -286,3 +293,178 @@ func filter[S ~[]T, T any](s S, f func(T) bool) (out S) {
return out
}
func TestFirehose_reorderIsolatedTransactionsAndOrdinals(t *testing.T) {
tests := []struct {
name string
populate func(t *Firehose)
expectedBlockFile string
}{
{
name: "empty",
populate: func(t *Firehose) {
t.OnBlockStart(blockEvent(1))
// Simulated GetTxTracer being called
t.blockReorderOrdinalOnce.Do(func() {
t.blockReorderOrdinal = true
t.blockReorderOrdinalSnapshot = t.blockOrdinal.value
})
t.blockOrdinal.Reset()
t.onTxStart(txEvent(), hex2Hash("CC"), from, to)
t.OnCallEnter(0, byte(vm.CALL), from, to, nil, 0, nil)
t.OnBalanceChange(empty, b(1), b(2), 0)
t.OnCallExit(0, nil, 0, nil, false)
t.OnTxEnd(txReceiptEvent(2), nil)
t.blockOrdinal.Reset()
t.onTxStart(txEvent(), hex2Hash("AA"), from, to)
t.OnCallEnter(0, byte(vm.CALL), from, to, nil, 0, nil)
t.OnBalanceChange(empty, b(1), b(2), 0)
t.OnCallExit(0, nil, 0, nil, false)
t.OnTxEnd(txReceiptEvent(0), nil)
t.blockOrdinal.Reset()
t.onTxStart(txEvent(), hex2Hash("BB"), from, to)
t.OnCallEnter(0, byte(vm.CALL), from, to, nil, 0, nil)
t.OnBalanceChange(empty, b(1), b(2), 0)
t.OnCallExit(0, nil, 0, nil, false)
t.OnTxEnd(txReceiptEvent(1), nil)
},
expectedBlockFile: "testdata/firehose/reorder-ordinals-empty.golden.json",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := NewFirehose(&FirehoseConfig{
ApplyBackwardCompatibility: ptr(false),
})
f.OnBlockchainInit(params.AllEthashProtocolChanges)
tt.populate(f)
f.reorderIsolatedTransactionsAndOrdinals()
goldenUpdate := os.Getenv("GOLDEN_UPDATE") == "true"
goldenPath := tt.expectedBlockFile
if !goldenUpdate && !fileExits(t, goldenPath) {
t.Fatalf("the golden file %q does not exist, re-run with 'GOLDEN_UPDATE=true go test ./... -run %q' to generate the intial version", goldenPath, t.Name())
}
content, err := protojson.MarshalOptions{Indent: " "}.Marshal(f.block)
require.NoError(t, err)
if goldenUpdate {
require.NoError(t, os.WriteFile(goldenPath, content, os.ModePerm))
}
expected, err := os.ReadFile(goldenPath)
require.NoError(t, err)
expectedBlock := &pbeth.Block{}
protojson.Unmarshal(expected, expectedBlock)
if !proto.Equal(expectedBlock, f.block) {
assert.Equal(t, expectedBlock, f.block, "Run 'GOLDEN_UPDATE=true go test ./... -run %q' to update golden file", t.Name())
}
seenOrdinals := make(map[uint64]int)
walkChanges(f.block.BalanceChanges, seenOrdinals)
walkChanges(f.block.CodeChanges, seenOrdinals)
walkCalls(f.block.SystemCalls, seenOrdinals)
for _, trx := range f.block.TransactionTraces {
seenOrdinals[trx.BeginOrdinal] = seenOrdinals[trx.BeginOrdinal] + 1
seenOrdinals[trx.EndOrdinal] = seenOrdinals[trx.EndOrdinal] + 1
walkCalls(trx.Calls, seenOrdinals)
}
// No ordinal should be seen more than once
for ordinal, count := range seenOrdinals {
assert.Equal(t, 1, count, "Ordinal %d seen %d times", ordinal, count)
}
ordinals := maps.Keys(seenOrdinals)
slices.Sort(ordinals)
// All ordinals should be in stricly increasing order
prev := -1
for _, ordinal := range ordinals {
if prev != -1 {
assert.Equal(t, prev+1, int(ordinal), "Ordinal %d is not in sequence", ordinal)
}
}
})
}
}
func walkCalls(calls []*pbeth.Call, ordinals map[uint64]int) {
for _, call := range calls {
walkCall(call, ordinals)
}
}
func walkCall(call *pbeth.Call, ordinals map[uint64]int) {
ordinals[call.BeginOrdinal] = ordinals[call.BeginOrdinal] + 1
ordinals[call.EndOrdinal] = ordinals[call.EndOrdinal] + 1
walkChanges(call.BalanceChanges, ordinals)
walkChanges(call.CodeChanges, ordinals)
walkChanges(call.Logs, ordinals)
walkChanges(call.StorageChanges, ordinals)
walkChanges(call.NonceChanges, ordinals)
walkChanges(call.GasChanges, ordinals)
}
func walkChanges[T any](changes []T, ordinals map[uint64]int) {
for _, change := range changes {
var x any = change
if v, ok := x.(interface{ GetOrdinal() uint64 }); ok {
ordinals[v.GetOrdinal()] = ordinals[v.GetOrdinal()] + 1
}
}
}
var b = big.NewInt
var empty, from, to = common.HexToAddress("00"), common.HexToAddress("01"), common.HexToAddress("02")
var hex2Hash = common.HexToHash
func fileExits(t *testing.T, path string) bool {
t.Helper()
stat, err := os.Stat(path)
return err == nil && !stat.IsDir()
}
func txEvent() *types.Transaction {
return types.NewTx(&types.LegacyTx{
Nonce: 0,
GasPrice: big.NewInt(1),
Gas: 1,
To: &to,
Value: big.NewInt(1),
Data: nil,
V: big.NewInt(1),
R: big.NewInt(1),
S: big.NewInt(1),
})
}
func txReceiptEvent(txIndex uint) *types.Receipt {
return &types.Receipt{
Status: 1,
TransactionIndex: txIndex,
}
}
func blockEvent(height uint64) tracing.BlockEvent {
return tracing.BlockEvent{
Block: types.NewBlock(&types.Header{
Number: big.NewInt(int64(height)),
}, nil, nil, nil, nil),
TD: b(1),
}
}

View file

@ -0,0 +1,122 @@
{
"hash": "sr0uqeYWyrLV1vijvIG6fe+0mu8LRG6FLMHv5UmUxK8=",
"number": "1",
"size": "501",
"header": {
"parentHash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"uncleHash": "HcxN6N7HXXqrhbVntszUGtMSRRuUinQT8KFC/UDUk0c=",
"coinbase": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"stateRoot": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"transactionsRoot": "VugfFxvMVab/g0XmksD4bltI4BuZbK3AAWIvteNjtCE=",
"receiptRoot": "VugfFxvMVab/g0XmksD4bltI4BuZbK3AAWIvteNjtCE=",
"logsBloom": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"difficulty": {
"bytes": "AA=="
},
"totalDifficulty": {
"bytes": "AQ=="
},
"number": "1",
"timestamp": "1970-01-01T00:00:00Z",
"mixHash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"hash": "sr0uqeYWyrLV1vijvIG6fe+0mu8LRG6FLMHv5UmUxK8="
},
"transactionTraces": [
{
"to": "AAAAAAAAAAAAAAAAAAAAAAAAAAI=",
"gasPrice": {
"bytes": "AQ=="
},
"gasLimit": "1",
"value": {
"bytes": "AQ=="
},
"v": "AQ==",
"r": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE=",
"s": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE=",
"hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKo=",
"from": "AAAAAAAAAAAAAAAAAAAAAAAAAAE=",
"beginOrdinal": "1",
"endOrdinal": "4",
"status": "SUCCEEDED",
"receipt": {
"logsBloom": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"calls": [
{
"index": 1,
"callType": "CALL",
"caller": "AAAAAAAAAAAAAAAAAAAAAAAAAAE=",
"address": "AAAAAAAAAAAAAAAAAAAAAAAAAAI=",
"beginOrdinal": "2",
"endOrdinal": "3"
}
]
},
{
"to": "AAAAAAAAAAAAAAAAAAAAAAAAAAI=",
"gasPrice": {
"bytes": "AQ=="
},
"gasLimit": "1",
"value": {
"bytes": "AQ=="
},
"v": "AQ==",
"r": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE=",
"s": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE=",
"index": 1,
"hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALs=",
"from": "AAAAAAAAAAAAAAAAAAAAAAAAAAE=",
"beginOrdinal": "5",
"endOrdinal": "8",
"status": "SUCCEEDED",
"receipt": {
"logsBloom": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"calls": [
{
"index": 1,
"callType": "CALL",
"caller": "AAAAAAAAAAAAAAAAAAAAAAAAAAE=",
"address": "AAAAAAAAAAAAAAAAAAAAAAAAAAI=",
"beginOrdinal": "6",
"endOrdinal": "7"
}
]
},
{
"to": "AAAAAAAAAAAAAAAAAAAAAAAAAAI=",
"gasPrice": {
"bytes": "AQ=="
},
"gasLimit": "1",
"value": {
"bytes": "AQ=="
},
"v": "AQ==",
"r": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE=",
"s": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE=",
"index": 2,
"hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMw=",
"from": "AAAAAAAAAAAAAAAAAAAAAAAAAAE=",
"beginOrdinal": "9",
"endOrdinal": "12",
"status": "SUCCEEDED",
"receipt": {
"logsBloom": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"calls": [
{
"index": 1,
"callType": "CALL",
"caller": "AAAAAAAAAAAAAAAAAAAAAAAAAAE=",
"address": "AAAAAAAAAAAAAAAAAAAAAAAAAAI=",
"beginOrdinal": "10",
"endOrdinal": "11"
}
]
}
],
"ver": 4
}