Added greater support for SetCode transactions

This commit is contained in:
Matthieu Vachon 2025-02-19 15:42:07 -05:00
parent dbe1dac352
commit b20db144a2
12 changed files with 1067 additions and 3647 deletions

View file

@ -1,6 +0,0 @@
version: v1
plugins:
- plugin: buf.build/protocolbuffers/go:v1.31.0
out: pb
opt: paths=source_relative

View file

@ -29,9 +29,9 @@ import (
"github.com/ethereum/go-ethereum/internal/version"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
pbeth "github.com/ethereum/go-ethereum/pb/sf/ethereum/type/v2"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
pbeth "github.com/streamingfast/firehose-ethereum/types/pb/sf/ethereum/type/v2"
"golang.org/x/exp/maps"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"
@ -158,6 +158,7 @@ type Firehose struct {
blockReorderOrdinal bool
blockReorderOrdinalSnapshot uint64
blockReorderOrdinalOnce sync.Once
blockIsGenesis bool
// Transaction state
evm *tracing.VMContext
@ -279,6 +280,7 @@ func (f *Firehose) resetBlock() {
f.blockReorderOrdinal = false
f.blockReorderOrdinalSnapshot = 0
f.blockReorderOrdinalOnce = sync.Once{}
f.blockIsGenesis = false
}
// resetTransaction resets the transaction state and the call state in one shot
@ -536,7 +538,7 @@ func (f *Firehose) OnSystemCallEnd() {
}
func (f *Firehose) OnTxStart(evm *tracing.VMContext, tx *types.Transaction, from common.Address) {
firehoseInfo("trx start (tracer=%s hash=%s type=%d gas=%d isolated=%t input=%s)", f.tracerID, tx.Hash(), tx.Type(), tx.Gas(), f.transactionIsolated, inputView(tx.Data()))
firehoseInfo("trx start (tracer=%s hash=%s %s type=%d gas=%d isolated=%t input=%s)", f.tracerID, tx.Hash(), fromToTxView(&from, tx), tx.Type(), tx.Gas(), f.transactionIsolated, inputView(tx.Data()))
f.ensureInBlockAndNotInTrxAndNotInCall()
@ -557,12 +559,7 @@ func (f *Firehose) OnTxStart(evm *tracing.VMContext, tx *types.Transaction, from
func (f *Firehose) onTxStart(tx *types.Transaction, hash common.Hash, from, to common.Address) {
v, r, s := tx.RawSignatureValues()
var blobGas *uint64
if tx.Type() == types.BlobTxType {
blobGas = ptr(tx.BlobGas())
}
f.transaction = &pbeth.TransactionTrace{
trx := &pbeth.TransactionTrace{
BeginOrdinal: f.blockOrdinal.Next(),
Hash: hash.Bytes(),
From: from.Bytes(),
@ -579,14 +576,23 @@ func (f *Firehose) onTxStart(tx *types.Transaction, hash common.Hash, from, to c
AccessList: newAccessListFromChain(tx.AccessList()),
MaxFeePerGas: maxFeePerGas(tx),
MaxPriorityFeePerGas: maxPriorityFeePerGas(tx),
BlobGas: blobGas,
BlobGasFeeCap: firehoseBigIntFromNative(tx.BlobGasFeeCap()),
BlobHashes: newBlobHashesFromChain(tx.BlobHashes()),
}
switch tx.Type() {
case types.BlobTxType:
trx.BlobGas = ptr(tx.BlobGas())
trx.BlobGasFeeCap = firehoseBigIntFromNative(tx.BlobGasFeeCap())
trx.BlobHashes = newBlobHashesFromChain(tx.BlobHashes())
case types.SetCodeTxType:
trx.SetCodeAuthorizations = newSetCodeAuthorizationsFromChain(tx.SetCodeAuthorizations())
}
f.transaction = trx
}
func (f *Firehose) OnTxEnd(receipt *types.Receipt, err error) {
firehoseInfo("trx ending (tracer=%s, isolated=%t, error=%s)", f.tracerID, f.transactionIsolated, errorView(err))
firehoseInfo("trx ending (tracer=%s, isolated=%t, err=%s)", f.tracerID, f.transactionIsolated, errorView(err))
f.ensureInBlockAndInTrx()
trxTrace := f.completeTransaction(receipt)
@ -613,7 +619,7 @@ func (f *Firehose) OnTxEnd(receipt *types.Receipt, err error) {
}
func (f *Firehose) completeTransaction(receipt *types.Receipt) *pbeth.TransactionTrace {
firehoseInfo("completing transaction (call_count=%d receipt=%s)", len(f.transaction.Calls), (*receiptView)(receipt))
firehoseInfo("completing transaction (call_count=%d receipt=%s)", len(f.transaction.Calls), receiptView(receipt))
// Sorting needs to happen first, before we populate the state reverted
slices.SortFunc(f.transaction.Calls, func(i, j *pbeth.Call) int {
@ -622,6 +628,10 @@ func (f *Firehose) completeTransaction(receipt *types.Receipt) *pbeth.Transactio
rootCall := f.transaction.Calls[0]
// Can be done prior moving last deferred call state to the root call as we are only interested from the initial
// deferred state that already been transferred into the root call (in `onCallStart(...)`).
f.discardUncommittedSetCodeAuthorization(rootCall)
if !f.deferredCallState.IsEmpty() {
f.deferredCallState.MaybePopulateCallAndReset("root", rootCall)
}
@ -686,6 +696,39 @@ func (f *Firehose) populateStateReverted() {
}
}
// discardUncommittedSetCodeAuthorization set `discarded = true` for all the SetCodeAuthorization element
// that don't have a corresponding NonceChange coming from the root call of the transaction, which
// means they weren't committed to the state.
//
// Indeed, EIP-7702 states that are invalid SetCodeAuthorization is simply discard and it's not recorded
// to chain's state.
func (f *Firehose) discardUncommittedSetCodeAuthorization(rootCall *pbeth.Call) {
usedNonceChange := map[int]bool{}
findNonceChange := func(forAddress []byte, nonce uint64) *pbeth.NonceChange {
for i, change := range rootCall.NonceChanges {
if change.OldValue == nonce && change.NewValue == nonce+1 && bytes.Equal(change.Address, forAddress) && usedNonceChange[i] == false {
usedNonceChange[i] = true
return change
}
}
return nil
}
for _, auth := range f.transaction.SetCodeAuthorizations {
if len(auth.Authority) == 0 {
// Nothing to check, authority is empty, it's not a valid authorization
auth.Discarded = true
continue
}
if findNonceChange(auth.Authority, auth.Nonce) == nil {
firehoseDebug("discarded set code authorization, no corresponding nonce change found (address=%s nonce=%d)", hex.EncodeToString(auth.Authority), auth.Nonce)
auth.Discarded = true
}
}
}
func (f *Firehose) removeLogBlockIndexOnStateRevertedCalls() {
for _, call := range f.transaction.Calls {
if call.StateReverted {
@ -995,7 +1038,7 @@ func (f *Firehose) captureInterpreterStep(activeCall *pbeth.Call, pc uint64, op
}
func (f *Firehose) callStart(source string, callType pbeth.CallType, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
firehoseDebug("call start (source=%s index=%d type=%s input=%s)", source, f.callStack.NextIndex(), callType, inputView(input))
firehoseDebug("call start (source=%s index=%d type=%s ref=%s input=%s)", source, f.callStack.NextIndex(), callType, fromToView(&from, &to), inputView(input))
f.ensureInBlockAndInTrx()
if *f.applyBackwardCompatibility {
@ -1018,6 +1061,18 @@ func (f *Firehose) callStart(source string, callType pbeth.CallType, from common
GasLimit: gas,
}
if f.blockRules.IsPrague && !f.inSystemCall && !f.blockIsGenesis && callType != pbeth.CallType_CREATE {
firehoseTrace("call resolving delegation (from=%s)", from)
code := f.evm.StateDB.GetCode(to)
if len(code) != 0 {
if target, ok := types.ParseDelegation(code); ok {
firehoseDebug("call resolved delegation (from=%s, delegates_to=%s)", from, target)
call.AddressDelegatesTo = target.Bytes()
}
}
}
if *f.applyBackwardCompatibility {
// Known Firehose issue: The BeginOrdinal of the genesis block root call is never actually
// incremented and it's always 0.
@ -1174,6 +1229,9 @@ func (f *Firehose) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
f.ensureBlockChainInit()
// Going to be reset in OnBlockEnd
f.blockIsGenesis = true
f.OnBlockStart(tracing.BlockEvent{Block: b, Finalized: nil, Safe: nil})
f.onTxStart(types.NewTx(&types.LegacyTx{}), emptyCommonHash, emptyCommonAddress, emptyCommonAddress)
f.OnCallEnter(0, byte(vm.CALL), emptyCommonAddress, emptyCommonAddress, nil, 0, nil)
@ -1260,7 +1318,7 @@ func (f *Firehose) OnBalanceChange(a common.Address, prev, new *big.Int, reason
}
func (f *Firehose) newBalanceChange(tag string, address common.Address, oldValue, newValue *big.Int, reason pbeth.BalanceChange_Reason) *pbeth.BalanceChange {
firehoseTrace("balance changed (tag=%s before=%d after=%d reason=%s)", tag, oldValue, newValue, reason)
firehoseTrace("balance changed (tag=%s address=%s before=%d after=%d reason=%s)", tag, shortAddressView(&address), oldValue, newValue, reason)
if reason == pbeth.BalanceChange_REASON_UNKNOWN {
panic(fmt.Errorf("received unknown balance change reason %s", reason))
@ -1302,8 +1360,14 @@ func (f *Firehose) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev
if f.transaction != nil {
activeCall := f.callStack.Peek()
// Since EIP-7702 and the introduction of the `SetCode` transaction, a traced `StateDB.SetCode(...)` call
// is now happening within the "bootstrap" transaction phase which happens before any call is made. So
// in the event there is no active call, we push the code change to the deferred state and will be applied
// on the root call when it's finally created.
if activeCall == nil {
f.panicInvalidState("caller expected to be in call state but we were not, this is a bug", 0)
f.deferredCallState.codeChanges = append(f.deferredCallState.codeChanges, f.newCodeChange(a, prevCodeHash, prev, codeHash, code))
return
}
// Geth 1.14.12 introduced a new behavior where a code change is emitted when a contract
@ -1325,28 +1389,25 @@ func (f *Firehose) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev
return
}
activeCall.CodeChanges = append(activeCall.CodeChanges, &pbeth.CodeChange{
Address: a.Bytes(),
OldHash: prevCodeHash.Bytes(),
OldCode: prev,
NewHash: codeHash.Bytes(),
NewCode: code,
Ordinal: f.blockOrdinal.Next(),
})
activeCall.CodeChanges = append(activeCall.CodeChanges, f.newCodeChange(a, prevCodeHash, prev, codeHash, code))
} else {
f.block.CodeChanges = append(f.block.CodeChanges, &pbeth.CodeChange{
Address: a.Bytes(),
f.block.CodeChanges = append(f.block.CodeChanges, f.newCodeChange(a, prevCodeHash, prev, codeHash, code))
}
}
func (f *Firehose) newCodeChange(addr common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) *pbeth.CodeChange {
return &pbeth.CodeChange{
Address: addr.Bytes(),
OldHash: prevCodeHash.Bytes(),
OldCode: prev,
NewHash: codeHash.Bytes(),
NewCode: code,
Ordinal: f.blockOrdinal.Next(),
})
}
}
func (f *Firehose) OnStorageChange(a common.Address, k, prev, new common.Hash) {
firehoseTrace("storage changed (key=%s, before=%s after=%s)", k, prev, new)
firehoseTrace("storage changed (address=%s key=%s, before=%s after=%s)", shortAddressView(&a), k, prev, new)
f.ensureInBlockAndInTrxAndInCall()
@ -1609,7 +1670,7 @@ func (f *Firehose) printBlockToFirehose(block *pbeth.Block, finalityStatus *Fina
}
// **Important* The final space in the Sprintf template is mandatory!
f.outputBuffer.WriteString(fmt.Sprintf("FIRE BLOCK %d %s %d %s %d %d ", block.Number, hex.EncodeToString(block.Hash), previousNum, previousHash, libNum, block.Time().UnixNano()))
f.outputBuffer.WriteString(fmt.Sprintf("FIRE BLOCK %d %s %d %s %d %d ", block.Number, hex.EncodeToString(block.Hash), previousNum, previousHash, libNum, block.MustTime().UnixNano()))
encoder := base64.NewEncoder(base64.StdEncoding, f.outputBuffer)
if _, err = encoder.Write(marshalled); err != nil {
@ -1860,6 +1921,37 @@ func newBlobHashesFromChain(blobHashes []common.Hash) (out [][]byte) {
return
}
func newSetCodeAuthorizationsFromChain(authorizations []types.SetCodeAuthorization) (out []*pbeth.SetCodeAuthorization) {
if len(authorizations) == 0 {
return nil
}
out = make([]*pbeth.SetCodeAuthorization, len(authorizations))
for i, authorization := range authorizations {
pbAuthorization := &pbeth.SetCodeAuthorization{
ChainId: authorization.ChainID.Bytes(),
Nonce: authorization.Nonce,
V: uint32(authorization.V),
R: normalizeSignaturePoint(authorization.R.Bytes()),
S: normalizeSignaturePoint(authorization.S.Bytes()),
}
authority, err := authorization.Authority()
if err != nil {
// The node skips invalid authorizations, we do the same, at transaction's end, we will
// also remove authorizations that didn't result into a code change.
firehoseDebug("failed to compute authority for authorization at index %d (err=%s)", i, errorView(err))
pbAuthorization.Discarded = true
} else {
pbAuthorization.Authority = authority.Bytes()
}
out[i] = pbAuthorization
}
return
}
var balanceChangeReasonToPb = map[tracing.BalanceChangeReason]pbeth.BalanceChange_Reason{
tracing.BalanceIncreaseRewardMineUncle: pbeth.BalanceChange_REASON_REWARD_MINE_UNCLE,
tracing.BalanceIncreaseRewardMineBlock: pbeth.BalanceChange_REASON_REWARD_MINE_BLOCK,
@ -2113,6 +2205,7 @@ type DeferredCallState struct {
balanceChanges []*pbeth.BalanceChange
gasChanges []*pbeth.GasChange
nonceChanges []*pbeth.NonceChange
codeChanges []*pbeth.CodeChange
}
func NewDeferredCallState() *DeferredCallState {
@ -2133,6 +2226,7 @@ func (d *DeferredCallState) MaybePopulateCallAndReset(source string, call *pbeth
call.BalanceChanges = append(call.BalanceChanges, d.balanceChanges...)
call.GasChanges = append(call.GasChanges, d.gasChanges...)
call.NonceChanges = append(call.NonceChanges, d.nonceChanges...)
call.CodeChanges = append(call.CodeChanges, d.codeChanges...)
d.Reset()
@ -2140,7 +2234,7 @@ func (d *DeferredCallState) MaybePopulateCallAndReset(source string, call *pbeth
}
func (d *DeferredCallState) IsEmpty() bool {
return len(d.accountCreations) == 0 && len(d.balanceChanges) == 0 && len(d.gasChanges) == 0 && len(d.nonceChanges) == 0
return len(d.accountCreations) == 0 && len(d.balanceChanges) == 0 && len(d.gasChanges) == 0 && len(d.nonceChanges) == 0 && len(d.codeChanges) == 0
}
func (d *DeferredCallState) Reset() {
@ -2148,8 +2242,10 @@ func (d *DeferredCallState) Reset() {
d.balanceChanges = nil
d.gasChanges = nil
d.nonceChanges = nil
d.codeChanges = nil
}
//go:inline
func errorView(err error) _errorView {
return _errorView{err}
}
@ -2163,7 +2259,57 @@ func (e _errorView) String() string {
return "<no error>"
}
return e.err.Error()
return `"` + e.err.Error() + `"`
}
//go:inline
func fromToTxView(from *common.Address, tx *types.Transaction) _fromToView {
return _fromToView{from, tx.To()}
}
//go:inline
func fromToView(from *common.Address, to *common.Address) _fromToView {
return _fromToView{from, to}
}
type _fromToView struct {
// from is actually always set
from *common.Address
to *common.Address
}
func (b _fromToView) String() string {
if b.from == nil && b.to == nil {
return ""
}
to := "<contract>"
if b.to != nil {
to = shortenAddress(b.to)
}
return fmt.Sprintf("(%s -> %s)", shortenAddress(b.from), to)
}
//go:inline
func shortAddressView(addr *common.Address) *_shortAddressView {
return (*_shortAddressView)(addr)
}
type _shortAddressView common.Address
func (a *_shortAddressView) String() string {
if a == nil {
return "<nil>"
}
return shortenAddress((*common.Address)(a))
}
func shortenAddress(addr *common.Address) string {
full := addr.String()
return full[:6] + ".." + full[len(full)-4:]
}
type inputView []byte
@ -2199,9 +2345,14 @@ func (b outputView) String() string {
return fmt.Sprintf("%d bytes", len(b))
}
type receiptView types.Receipt
//go:inline
func receiptView(receipt *types.Receipt) *_receiptView {
return (*_receiptView)(receipt)
}
func (r *receiptView) String() string {
type _receiptView types.Receipt
func (r *_receiptView) String() string {
if r == nil {
return "<failed>"
}

View file

@ -14,7 +14,7 @@ import (
"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"
pbeth "github.com/streamingfast/firehose-ethereum/types/pb/sf/ethereum/type/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/exp/maps"

View file

@ -1,18 +1,23 @@
package firehose_test
import (
"encoding/json"
"math/big"
"path/filepath"
"strings"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/core/vm/program"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
"github.com/stretchr/testify/require"
)
@ -28,18 +33,10 @@ func TestFirehoseChain(t *testing.T) {
BaseFee: big.NewInt(8),
}
tracer, err := tracers.NewFirehoseFromRawJSON(json.RawMessage(`{
"applyBackwardsCompatibility": true,
"_private": {
"flushToTestBuffer": true,
"ignoreGenesisBlock": true
}
}`))
require.NoError(t, err)
tracer, tracingHooks, onClose := newFirehoseTestTracer(t)
defer onClose()
hooks := tracers.NewTracingHooksFromFirehose(tracer)
genesis, blockchain := newBlockchain(t, types.GenesisAlloc{}, context, hooks)
genesis, blockchain := newBlockchain(t, types.GenesisAlloc{}, context, tracingHooks)
block := types.NewBlock(&types.Header{
ParentHash: genesis.ToBlock().Hash(),
@ -81,15 +78,10 @@ func TestFirehosePrestate(t *testing.T) {
name := filepath.Base(folder)
t.Run(name, func(t *testing.T) {
tracer, err := tracers.NewFirehoseFromRawJSON(json.RawMessage(`{
"applyBackwardsCompatibility": true,
"_private": {
"flushToTestBuffer": true
}
}`))
require.NoError(t, err)
tracer, tracingHooks, onClose := newFirehoseTestTracer(t)
defer onClose()
runPrestateBlock(t, filepath.Join(folder, "prestate.json"), tracers.NewTracingHooksFromFirehose(tracer))
runPrestateBlock(t, filepath.Join(folder, "prestate.json"), tracingHooks)
genesisLine, blockLines, unknownLines := readTracerFirehoseLines(t, tracer)
require.Len(t, unknownLines, 0, "Lines:\n%s", strings.Join(slicesMap(unknownLines, func(l unknownLine) string { return "- '" + string(l) + "'" }), "\n"))
@ -99,3 +91,127 @@ func TestFirehosePrestate(t *testing.T) {
}
}
func TestFirehose_EIP7702(t *testing.T) {
// Copied from ./core/blockchain_test.go#L4180 (TestEIP7702)
var (
config = *params.MergedTestChainConfig
signer = types.LatestSigner(&config)
engine = beacon.New(ethash.NewFaker())
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
cc = common.HexToAddress("0x000000000000000000000000000000000000cccc")
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
)
gspec := &core.Genesis{
Config: &config,
Alloc: types.GenesisAlloc{
addr1: {Balance: funds},
addr2: {Balance: funds},
aa: { // The address 0xAAAA calls into addr2
Code: program.New().Call(nil, addr2, 1, 0, 0, 0, 0).Bytes(),
Nonce: 0,
Balance: big.NewInt(0),
},
bb: { // The address 0xBBBB sstores 42 into slot 42.
Code: program.New().Sstore(0x42, 0x42).Bytes(),
Nonce: 0,
Balance: big.NewInt(0),
},
cc: { // The address 0xCCCC sstores 42 into slot 42.
Code: program.New().Sstore(0x42, 0x42).Bytes(),
Nonce: 0,
Balance: big.NewInt(0),
},
},
}
// Sign authorization tuples.
// The way the auths are combined, it becomes
// 1. tx -> addr1 which is delegated to 0xaaaa
// 2. addr1:0xaaaa calls into addr2:0xbbbb
// 3. addr2:0xbbbb writes to storage
auth1, _ := types.SignSetCode(key1, types.SetCodeAuthorization{
ChainID: *uint256.MustFromBig(gspec.Config.ChainID),
Address: aa,
Nonce: 1,
})
auth2OverwrittenLaterInList, _ := types.SignSetCode(key2, types.SetCodeAuthorization{
Address: cc,
Nonce: 0,
})
auth3InvalidAuthority := auth2OverwrittenLaterInList
auth3InvalidAuthority.V = 4
auth4, _ := types.SignSetCode(key2, types.SetCodeAuthorization{
Address: bb,
Nonce: 1,
})
auth5InvalidNonce, _ := types.SignSetCode(key2, types.SetCodeAuthorization{
Address: bb,
Nonce: 1,
})
auth1Reset, _ := types.SignSetCode(key1, types.SetCodeAuthorization{
ChainID: *uint256.MustFromBig(gspec.Config.ChainID),
Address: common.Address{},
Nonce: 4,
})
_, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, 3, func(i int, b *core.BlockGen) {
b.SetCoinbase(aa)
if i == 0 {
txdata := &types.SetCodeTx{
ChainID: uint256.MustFromBig(gspec.Config.ChainID),
Nonce: 0,
To: addr1,
Gas: 500000,
GasFeeCap: uint256.MustFromBig(newGwei(5)),
GasTipCap: uint256.NewInt(2),
AuthList: []types.SetCodeAuthorization{auth1, auth2OverwrittenLaterInList, auth3InvalidAuthority, auth4, auth5InvalidNonce},
}
tx := types.MustSignNewTx(key1, signer, txdata)
b.AddTx(tx)
} else if i == 1 {
txdata := &types.LegacyTx{
Nonce: 2,
To: &addr1,
Value: big.NewInt(0),
Gas: 500000,
GasPrice: newGwei(500),
}
tx := types.MustSignNewTx(key1, signer, txdata)
b.AddTx(tx)
} else if i == 2 {
txdata := &types.SetCodeTx{
ChainID: uint256.MustFromBig(gspec.Config.ChainID),
Nonce: 3,
To: addr1,
Gas: 500000,
GasFeeCap: uint256.MustFromBig(newGwei(5)),
GasTipCap: uint256.NewInt(2),
AuthList: []types.SetCodeAuthorization{auth1Reset},
}
tx := types.MustSignNewTx(key1, signer, txdata)
b.AddTx(tx)
}
})
tracer, tracingHooks, onClose := newFirehoseTestTracer(t)
defer onClose()
chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: tracingHooks}, nil)
require.NoError(t, err, "failed to create tester chain")
defer chain.Stop()
n, err := chain.InsertChain(blocks)
require.NoError(t, err, "failed to insert chain block %d", n)
assertBlockEquals(t, tracer, filepath.Join("testdata", "TestEIP7702"), len(blocks))
}

View file

@ -4,12 +4,16 @@ import (
"bytes"
"encoding/base64"
"fmt"
"math/big"
"os"
"path/filepath"
"strings"
"testing"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/eth/tracers"
pbeth "github.com/ethereum/go-ethereum/pb/sf/ethereum/type/v2"
"github.com/ethereum/go-ethereum/params"
pbeth "github.com/streamingfast/firehose-ethereum/types/pb/sf/ethereum/type/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/encoding/protojson"
@ -24,6 +28,27 @@ type firehoseInitLine struct {
type firehoseBlockLines []firehoseBlockLine
func newFirehoseTestTracer(t *testing.T) (*tracers.Firehose, *tracing.Hooks, func()) {
t.Helper()
tracer, err := tracers.NewFirehoseFromRawJSON([]byte(`{
"applyBackwardsCompatibility": true,
"_private": {
"flushToTestBuffer": true,
"ignoreGenesisBlock": true
}
}`))
require.NoError(t, err)
hooks := tracers.NewTracingHooksFromFirehose(tracer)
return tracer, hooks, func() {
if hooks.OnClose != nil {
hooks.OnClose()
}
}
}
func (lines firehoseBlockLines) assertEquals(t *testing.T, goldenDir string, expected ...firehoseBlockLineParams) {
actualParams := slicesMap(lines, func(l firehoseBlockLine) firehoseBlockLineParams { return l.Params })
require.Equal(t, expected, actualParams, "Actual lines block params do not match expected lines block params")
@ -102,6 +127,16 @@ type firehoseBlockLineParams struct {
type unknownLine string
// assertBlockEquals reads the tracer output and compares it to the golden files in the given directory.
func assertBlockEquals(t *testing.T, tracer *tracers.Firehose, goldenDir string, expectedBlockCount int) {
t.Helper()
genesisLine, blockLines, unknownLines := readTracerFirehoseLines(t, tracer)
require.Len(t, unknownLines, 0, "Lines:\n%s", strings.Join(slicesMap(unknownLines, func(l unknownLine) string { return "- '" + string(l) + "'" }), "\n"))
require.NotNil(t, genesisLine)
blockLines.assertOnlyBlockEquals(t, goldenDir, expectedBlockCount)
}
func readTracerFirehoseLines(t *testing.T, tracer *tracers.Firehose) (genesisLine *firehoseInitLine, blockLines firehoseBlockLines, unknownLines []unknownLine) {
t.Helper()
@ -157,3 +192,7 @@ func readTracerFirehoseLines(t *testing.T, tracer *tracers.Firehose) (genesisLin
func ptr[T any](v T) *T {
return &v
}
func newGwei(n int64) *big.Int {
return new(big.Int).Mul(big.NewInt(n), big.NewInt(params.GWei))
}

View file

@ -0,0 +1,297 @@
{
"hash": "tNKSM8SZPvaEgBA4yUj9YayXYYuXnLCuHx5oWJwVOhw=",
"number": "1",
"size": "1190",
"header": {
"parentHash": "pWIUj7qpHz/G8tLNdaLNz8Jr1TmoyDxTXP9TdFcxi1w=",
"uncleHash": "HcxN6N7HXXqrhbVntszUGtMSRRuUinQT8KFC/UDUk0c=",
"coinbase": "AAAAAAAAAAAAAAAAAAAAAAAAqqo=",
"stateRoot": "wFieFJlWOhntuAXVWTxqg5Uur7kaXrFRERwFB8+qscc=",
"transactionsRoot": "L6HG4v+Zoguv352C8ZdQGQ4tE5BJdzCJVbfWBHMquN0=",
"receiptRoot": "6LQ+xgZZXKAlBStw088D6A2kWiz7g+TtQlAALnPMSi4=",
"logsBloom": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"difficulty": {
"bytes": "AA=="
},
"number": "1",
"gasLimit": "4712388",
"gasUsed": "142021",
"timestamp": "1970-01-01T00:00:10Z",
"mixHash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"hash": "tNKSM8SZPvaEgBA4yUj9YayXYYuXnLCuHx5oWJwVOhw=",
"baseFeePerGas": {
"bytes": "NCdwwA=="
},
"withdrawalsRoot": "VugfFxvMVab/g0XmksD4bltI4BuZbK3AAWIvteNjtCE=",
"blobGasUsed": "0",
"excessBlobGas": "0",
"parentBeaconRoot": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"requestsHash": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="
},
"transactionTraces": [
{
"to": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"gasPrice": {
"bytes": "NCdwwg=="
},
"gasLimit": "500000",
"r": "E2l+Qpz16QzQhLJM+8gu8vd/lpMS1JAC0/hIlSNzFeg=",
"s": "N9pmx9wBwi0vXsT283fImtj6D4zPzYK3ZFXwBhYmbBA=",
"gasUsed": "142021",
"type": "TRX_TYPE_SET_CODE",
"maxFeePerGas": {
"bytes": "ASoF8gA="
},
"maxPriorityFeePerGas": {
"bytes": "Ag=="
},
"hash": "alLg5Yk4t0M/5q97S3ye+D/U/jP60tAc1kNI8q4G+G0=",
"from": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"beginOrdinal": "5",
"endOrdinal": "27",
"status": "SUCCEEDED",
"receipt": {
"cumulativeGasUsed": "142021",
"logsBloom": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"calls": [
{
"index": 1,
"callType": "CALL",
"caller": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"addressDelegatesTo": "AAAAAAAAAAAAAAAAAAAAAAAAqqo=",
"gasLimit": "354000",
"gasConsumed": "31526",
"balanceChanges": [
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"oldValue": {
"bytes": "DeC2s6dkAAA="
},
"newValue": {
"bytes": "Dd8ozD895cA="
},
"reason": "REASON_GAS_BUY",
"ordinal": "6"
},
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"oldValue": {
"bytes": "Dd8ozD895b8="
},
"newValue": {
"bytes": "DeBFrisGZrU="
},
"reason": "REASON_GAS_REFUND",
"ordinal": "25"
},
{
"address": "AAAAAAAAAAAAAAAAAAAAAAAAqqo=",
"newValue": {
"bytes": "BFWK"
},
"reason": "REASON_REWARD_TRANSACTION_FEE",
"ordinal": "26"
}
],
"nonceChanges": [
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"newValue": "1",
"ordinal": "8"
},
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"oldValue": "1",
"newValue": "2",
"ordinal": "9"
},
{
"address": "cDxLK9cMFp9XFxAcruVDKZ/JRsc=",
"newValue": "1",
"ordinal": "11"
},
{
"address": "cDxLK9cMFp9XFxAcruVDKZ/JRsc=",
"oldValue": "1",
"newValue": "2",
"ordinal": "13"
}
],
"codeChanges": [
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"oldHash": "xdJGAYb3IzySfn2y3McDwOUAtlPKgic7e/rYBF2FpHA=",
"newHash": "6BOVhfT5ksAf8bXRTf8FXcJyKhFA0Cb18MtIsAn1Z9o=",
"newCode": "7wEAAAAAAAAAAAAAAAAAAAAAAAAAqqo=",
"ordinal": "10"
},
{
"address": "cDxLK9cMFp9XFxAcruVDKZ/JRsc=",
"oldHash": "xdJGAYb3IzySfn2y3McDwOUAtlPKgic7e/rYBF2FpHA=",
"newHash": "2nRj6FdgztYMKwAqS2x5e7pQdCMd4bTSeffx8pbIFmU=",
"newCode": "7wEAAAAAAAAAAAAAAAAAAAAAAAAAzMw=",
"ordinal": "12"
},
{
"address": "cDxLK9cMFp9XFxAcruVDKZ/JRsc=",
"oldHash": "2nRj6FdgztYMKwAqS2x5e7pQdCMd4bTSeffx8pbIFmU=",
"oldCode": "7wEAAAAAAAAAAAAAAAAAAAAAAAAAzMw=",
"newHash": "0mhxQ0BYDv3haWHrT7NIuLWUSlfUHjFwuLM/8Jt1SXA=",
"newCode": "7wEAAAAAAAAAAAAAAAAAAAAAAAAAu7s=",
"ordinal": "14"
}
],
"gasChanges": [
{
"oldValue": "500000",
"newValue": "354000",
"reason": "REASON_INTRINSIC_GAS",
"ordinal": "7"
},
{
"oldValue": "353880",
"newValue": "351280",
"reason": "REASON_STATE_COLD_ACCESS",
"ordinal": "16"
},
{
"oldValue": "353980",
"newValue": "5348",
"reason": "REASON_CALL",
"ordinal": "17"
},
{
"oldValue": "5348",
"newValue": "322474",
"reason": "REASON_REFUND_AFTER_EXECUTION",
"ordinal": "23"
}
],
"endOrdinal": "24"
},
{
"index": 2,
"parentIndex": 1,
"depth": 1,
"callType": "CALL",
"caller": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"address": "cDxLK9cMFp9XFxAcruVDKZ/JRsc=",
"addressDelegatesTo": "AAAAAAAAAAAAAAAAAAAAAAAAu7s=",
"value": {
"bytes": "AQ=="
},
"gasLimit": "339232",
"gasConsumed": "22106",
"storageChanges": [
{
"address": "cDxLK9cMFp9XFxAcruVDKZ/JRsc=",
"key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEI=",
"oldValue": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"newValue": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEI=",
"ordinal": "21"
}
],
"balanceChanges": [
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"oldValue": {
"bytes": "Dd8ozD895cA="
},
"newValue": {
"bytes": "Dd8ozD895b8="
},
"reason": "REASON_TRANSFER",
"ordinal": "19"
},
{
"address": "cDxLK9cMFp9XFxAcruVDKZ/JRsc=",
"oldValue": {
"bytes": "DeC2s6dkAAA="
},
"newValue": {
"bytes": "DeC2s6dkAAE="
},
"reason": "REASON_TRANSFER",
"ordinal": "20"
}
],
"beginOrdinal": "18",
"endOrdinal": "22"
}
],
"setCodeAuthorizations": [
{
"chainId": "AQ==",
"nonce": "1",
"v": 1,
"r": "9+Pll/wJfnHtbCaxSyXlOVvIUQ1YuRNq9DnhJxXy1yE=",
"s": "bPfD15Ob/beENz7/wOuwvXVJaRpRPzlePNq/hgJySYc=",
"authority": "cVYrcZmYc9tbKG35V68ZnslGF/c="
},
{
"r": "2uP1vwtGC/qSZin0z+PZ2kBZ/xY3445BRUpUUwr4UOc=",
"s": "HhA+a0fj/u82ZZyD9gMoHso5vGditId+wkZrJcpo4UM=",
"authority": "cDxLK9cMFp9XFxAcruVDKZ/JRsc="
},
{
"discarded": true,
"v": 4,
"r": "2uP1vwtGC/qSZin0z+PZ2kBZ/xY3445BRUpUUwr4UOc=",
"s": "HhA+a0fj/u82ZZyD9gMoHso5vGditId+wkZrJcpo4UM="
},
{
"nonce": "1",
"r": "aZRYP4l48gpWJSypjCxSptjK/kDFXmphIXOrnH6LlKs=",
"s": "HLb0WRaaPuzez2ZbtK39Ch9vZGXNjm5AYg2i8X5MMwg=",
"authority": "cDxLK9cMFp9XFxAcruVDKZ/JRsc="
},
{
"discarded": true,
"nonce": "1",
"r": "aZRYP4l48gpWJSypjCxSptjK/kDFXmphIXOrnH6LlKs=",
"s": "HLb0WRaaPuzez2ZbtK39Ch9vZGXNjm5AYg2i8X5MMwg=",
"authority": "cDxLK9cMFp9XFxAcruVDKZ/JRsc="
}
]
}
],
"systemCalls": [
{
"index": 1,
"callType": "CALL",
"caller": "//////////////////////////4=",
"address": "AA899tcygH7xMZ+3uLuFItC+rAI=",
"gasLimit": "30000000",
"input": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"endOrdinal": "2"
},
{
"index": 1,
"callType": "CALL",
"caller": "//////////////////////////4=",
"address": "AAD5CCfxxToQy3oCM1sXUyAAKTU=",
"gasLimit": "30000000",
"input": "pWIUj7qpHz/G8tLNdaLNz8Jr1TmoyDxTXP9TdFcxi1w=",
"endOrdinal": "4"
},
{
"index": 1,
"callType": "CALL",
"caller": "//////////////////////////4=",
"address": "AAAJYe9IDrVegNGa2DV5pkwAcAI=",
"gasLimit": "30000000",
"endOrdinal": "29"
},
{
"index": 1,
"callType": "CALL",
"caller": "//////////////////////////4=",
"address": "AAC73cfOSIZC+1efiwDzpZAAclE=",
"gasLimit": "30000000",
"endOrdinal": "31"
}
],
"ver": 3
}

View file

@ -0,0 +1,218 @@
{
"hash": "Hlau1KLprrOkaDe4TNZdPEoXeJ4UGEL1PwUY/qq+/rk=",
"number": "2",
"size": "717",
"header": {
"parentHash": "tNKSM8SZPvaEgBA4yUj9YayXYYuXnLCuHx5oWJwVOhw=",
"uncleHash": "HcxN6N7HXXqrhbVntszUGtMSRRuUinQT8KFC/UDUk0c=",
"coinbase": "AAAAAAAAAAAAAAAAAAAAAAAAqqo=",
"stateRoot": "C/zDETYPAXEFuq3RDENkXppoaNjfcWKHhIQqZGC0D5c=",
"transactionsRoot": "5SXnkgZs4gKgt8t4GTnSeoV1i17zHn3xbZXSln9fd2E=",
"receiptRoot": "TaaZwIwiwzd65OYkLzazOIYrFN0/DnyXzNqaA/dY3pQ=",
"logsBloom": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"difficulty": {
"bytes": "AA=="
},
"number": "2",
"gasLimit": "4712388",
"gasUsed": "35126",
"timestamp": "1970-01-01T00:00:20Z",
"mixHash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"hash": "Hlau1KLprrOkaDe4TNZdPEoXeJ4UGEL1PwUY/qq+/rk=",
"baseFeePerGas": {
"bytes": "LgcbLA=="
},
"withdrawalsRoot": "VugfFxvMVab/g0XmksD4bltI4BuZbK3AAWIvteNjtCE=",
"blobGasUsed": "0",
"excessBlobGas": "0",
"parentBeaconRoot": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"requestsHash": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="
},
"transactionTraces": [
{
"to": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"nonce": "2",
"gasPrice": {
"bytes": "dGpSiAA="
},
"gasLimit": "500000",
"v": "JQ==",
"r": "llnySTXnLA5URm+RriiJTQ4gvkdUifLtVOIyBCpDFTw=",
"s": "SsqOfVDTp1cpwvz0g4nLhIzrWRGwh0Jgz4l9GK/UHbE=",
"gasUsed": "35126",
"hash": "TNmQOfjPE0jeas0eba+xGMaitDgyCOKmvNIBapciCiE=",
"from": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"beginOrdinal": "5",
"endOrdinal": "21",
"status": "SUCCEEDED",
"receipt": {
"cumulativeGasUsed": "35126",
"logsBloom": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"calls": [
{
"index": 1,
"callType": "CALL",
"caller": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"addressDelegatesTo": "AAAAAAAAAAAAAAAAAAAAAAAAqqo=",
"gasLimit": "479000",
"gasConsumed": "14126",
"balanceChanges": [
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"oldValue": {
"bytes": "DeBFrisGZrU="
},
"newValue": {
"bytes": "CmgYAUEtZrU="
},
"reason": "REASON_GAS_BUY",
"ordinal": "6"
},
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"oldValue": {
"bytes": "CmgYAUEtZrQ="
},
"newValue": {
"bytes": "DaHgOZLVtrQ="
},
"reason": "REASON_GAS_REFUND",
"ordinal": "19"
},
{
"address": "AAAAAAAAAAAAAAAAAAAAAAAAqqo=",
"oldValue": {
"bytes": "BFWK"
},
"newValue": {
"bytes": "PkzJFSq+Qg=="
},
"reason": "REASON_REWARD_TRANSACTION_FEE",
"ordinal": "20"
}
],
"nonceChanges": [
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"oldValue": "2",
"newValue": "3",
"ordinal": "8"
}
],
"gasChanges": [
{
"oldValue": "500000",
"newValue": "479000",
"reason": "REASON_INTRINSIC_GAS",
"ordinal": "7"
},
{
"oldValue": "478880",
"newValue": "476380",
"reason": "REASON_STATE_COLD_ACCESS",
"ordinal": "10"
},
{
"oldValue": "476380",
"newValue": "473780",
"reason": "REASON_STATE_COLD_ACCESS",
"ordinal": "11"
},
{
"oldValue": "478980",
"newValue": "7262",
"reason": "REASON_CALL",
"ordinal": "12"
},
{
"oldValue": "7262",
"newValue": "464874",
"reason": "REASON_REFUND_AFTER_EXECUTION",
"ordinal": "17"
}
],
"endOrdinal": "18"
},
{
"index": 2,
"parentIndex": 1,
"depth": 1,
"callType": "CALL",
"caller": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"address": "cDxLK9cMFp9XFxAcruVDKZ/JRsc=",
"addressDelegatesTo": "AAAAAAAAAAAAAAAAAAAAAAAAu7s=",
"value": {
"bytes": "AQ=="
},
"gasLimit": "459818",
"gasConsumed": "2206",
"balanceChanges": [
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"oldValue": {
"bytes": "CmgYAUEtZrU="
},
"newValue": {
"bytes": "CmgYAUEtZrQ="
},
"reason": "REASON_TRANSFER",
"ordinal": "14"
},
{
"address": "cDxLK9cMFp9XFxAcruVDKZ/JRsc=",
"oldValue": {
"bytes": "DeC2s6dkAAE="
},
"newValue": {
"bytes": "DeC2s6dkAAI="
},
"reason": "REASON_TRANSFER",
"ordinal": "15"
}
],
"beginOrdinal": "13",
"endOrdinal": "16"
}
]
}
],
"systemCalls": [
{
"index": 1,
"callType": "CALL",
"caller": "//////////////////////////4=",
"address": "AA899tcygH7xMZ+3uLuFItC+rAI=",
"gasLimit": "30000000",
"input": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"endOrdinal": "2"
},
{
"index": 1,
"callType": "CALL",
"caller": "//////////////////////////4=",
"address": "AAD5CCfxxToQy3oCM1sXUyAAKTU=",
"gasLimit": "30000000",
"input": "tNKSM8SZPvaEgBA4yUj9YayXYYuXnLCuHx5oWJwVOhw=",
"endOrdinal": "4"
},
{
"index": 1,
"callType": "CALL",
"caller": "//////////////////////////4=",
"address": "AAAJYe9IDrVegNGa2DV5pkwAcAI=",
"gasLimit": "30000000",
"endOrdinal": "23"
},
{
"index": 1,
"callType": "CALL",
"caller": "//////////////////////////4=",
"address": "AAC73cfOSIZC+1efiwDzpZAAclE=",
"gasLimit": "30000000",
"endOrdinal": "25"
}
],
"ver": 3
}

View file

@ -0,0 +1,182 @@
{
"hash": "nM0MjYrd54p5L0zQNGpHV7fxetFL1VCejCvGELDLop8=",
"number": "3",
"size": "817",
"header": {
"parentHash": "Hlau1KLprrOkaDe4TNZdPEoXeJ4UGEL1PwUY/qq+/rk=",
"uncleHash": "HcxN6N7HXXqrhbVntszUGtMSRRuUinQT8KFC/UDUk0c=",
"coinbase": "AAAAAAAAAAAAAAAAAAAAAAAAqqo=",
"stateRoot": "vWvMYC8Hck2r4d77C3bO7nRWmEtpytSHv092DGpMjeI=",
"transactionsRoot": "Bpo3ARUhIUuu9hH3yrkUNF2s22VFnHRDwdWxzM0pb5U=",
"receiptRoot": "A2x9IEIO284ktQYhSLzoBWO0j9RTL7HAaLLJalMRcBk=",
"logsBloom": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"difficulty": {
"bytes": "AA=="
},
"number": "3",
"gasLimit": "4712388",
"gasUsed": "36800",
"timestamp": "1970-01-01T00:00:30Z",
"mixHash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"hash": "nM0MjYrd54p5L0zQNGpHV7fxetFL1VCejCvGELDLop8=",
"baseFeePerGas": {
"bytes": "KFws9Q=="
},
"withdrawalsRoot": "VugfFxvMVab/g0XmksD4bltI4BuZbK3AAWIvteNjtCE=",
"blobGasUsed": "0",
"excessBlobGas": "0",
"parentBeaconRoot": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"requestsHash": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="
},
"transactionTraces": [
{
"to": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"nonce": "3",
"gasPrice": {
"bytes": "KFws9w=="
},
"gasLimit": "500000",
"r": "3+bIOst9jVy0xdYLs7dP1KfP1XrtsXepmpY0W3XBKII=",
"s": "UGXb+KJ8dparEBn0Wb2lX5fh1gB2+AiHfowzzmJWOkE=",
"gasUsed": "36800",
"type": "TRX_TYPE_SET_CODE",
"maxFeePerGas": {
"bytes": "ASoF8gA="
},
"maxPriorityFeePerGas": {
"bytes": "Ag=="
},
"hash": "vj4lUeVR72WjVf2OYUmz3Um0SH4d6qCao7cZRbBiaCI=",
"from": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"beginOrdinal": "5",
"endOrdinal": "15",
"status": "SUCCEEDED",
"receipt": {
"cumulativeGasUsed": "36800",
"logsBloom": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"calls": [
{
"index": 1,
"callType": "CALL",
"caller": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"gasLimit": "454000",
"balanceChanges": [
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"oldValue": {
"bytes": "DaHgOZLVtrQ="
},
"newValue": {
"bytes": "DaCsTVRHwNQ="
},
"reason": "REASON_GAS_BUY",
"ordinal": "6"
},
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"oldValue": {
"bytes": "DaCsTVRHwNQ="
},
"newValue": {
"bytes": "DaHJj9CWBHQ="
},
"reason": "REASON_GAS_REFUND",
"ordinal": "13"
},
{
"address": "AAAAAAAAAAAAAAAAAAAAAAAAqqo=",
"oldValue": {
"bytes": "PkzJFSq+Qg=="
},
"newValue": {
"bytes": "PkzJFSvdwg=="
},
"reason": "REASON_REWARD_TRANSACTION_FEE",
"ordinal": "14"
}
],
"nonceChanges": [
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"oldValue": "3",
"newValue": "4",
"ordinal": "8"
},
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"oldValue": "4",
"newValue": "5",
"ordinal": "9"
}
],
"codeChanges": [
{
"address": "cVYrcZmYc9tbKG35V68ZnslGF/c=",
"oldHash": "6BOVhfT5ksAf8bXRTf8FXcJyKhFA0Cb18MtIsAn1Z9o=",
"oldCode": "7wEAAAAAAAAAAAAAAAAAAAAAAAAAqqo=",
"newHash": "xdJGAYb3IzySfn2y3McDwOUAtlPKgic7e/rYBF2FpHA=",
"ordinal": "10"
}
],
"gasChanges": [
{
"oldValue": "500000",
"newValue": "454000",
"reason": "REASON_INTRINSIC_GAS",
"ordinal": "7"
}
],
"endOrdinal": "12"
}
],
"setCodeAuthorizations": [
{
"chainId": "AQ==",
"nonce": "4",
"r": "8RpcvObxuhW9b2SyJDTKMBtrqfyaAJlzS37bOLPTKIo=",
"s": "NMkyzSai5xkwNV4ttrTli9jZHXeB7hUwej28frcnTDw=",
"authority": "cVYrcZmYc9tbKG35V68ZnslGF/c="
}
]
}
],
"systemCalls": [
{
"index": 1,
"callType": "CALL",
"caller": "//////////////////////////4=",
"address": "AA899tcygH7xMZ+3uLuFItC+rAI=",
"gasLimit": "30000000",
"input": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"endOrdinal": "2"
},
{
"index": 1,
"callType": "CALL",
"caller": "//////////////////////////4=",
"address": "AAD5CCfxxToQy3oCM1sXUyAAKTU=",
"gasLimit": "30000000",
"input": "Hlau1KLprrOkaDe4TNZdPEoXeJ4UGEL1PwUY/qq+/rk=",
"endOrdinal": "4"
},
{
"index": 1,
"callType": "CALL",
"caller": "//////////////////////////4=",
"address": "AAAJYe9IDrVegNGa2DV5pkwAcAI=",
"gasLimit": "30000000",
"endOrdinal": "17"
},
{
"index": 1,
"callType": "CALL",
"caller": "//////////////////////////4=",
"address": "AAC73cfOSIZC+1efiwDzpZAAclE=",
"gasLimit": "30000000",
"endOrdinal": "19"
}
],
"ver": 3
}

1
go.mod
View file

@ -58,6 +58,7 @@ require (
github.com/rs/cors v1.7.0
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
github.com/status-im/keycard-go v0.2.0
github.com/streamingfast/firehose-ethereum/types v0.0.0-20250219193809-31f4c76a8a5d
github.com/stretchr/testify v1.9.0
github.com/supranational/blst v0.3.14
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7

2
go.sum
View file

@ -493,6 +493,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
github.com/streamingfast/firehose-ethereum/types v0.0.0-20250219193809-31f4c76a8a5d h1:8Ki6y+B7HB3Wo8DBUqoIpNxT6mGZ/5joPHRZSvLIFdE=
github.com/streamingfast/firehose-ethereum/types v0.0.0-20250219193809-31f4c76a8a5d/go.mod h1:CG22ObinxSbKIP19bAj0uro0a290kzZTiBbjL8VR0SE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=

View file

@ -1,27 +0,0 @@
package pbeth
import (
"encoding/hex"
"math/big"
"time"
)
var b0 = big.NewInt(0)
func (b *Block) PreviousID() string {
return hex.EncodeToString(b.Header.ParentHash)
}
func (b *Block) Time() time.Time {
return b.Header.Timestamp.AsTime()
}
func (m *BigInt) Native() *big.Int {
if m == nil {
return b0
}
z := new(big.Int)
z.SetBytes(m.Bytes)
return z
}

File diff suppressed because it is too large Load diff