From edd0dc427ed42bf933de2dbcdf4f1cff20be9c97 Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Sun, 22 Sep 2024 13:33:50 +0200 Subject: [PATCH 01/45] feat: add firehose tracer --- firehose/firehose.go | 2255 ++++++++++ firehose/firehose_test.go | 493 +++ firehose/pb/sf/ethereum/type/v2/type.go | 27 + firehose/pb/sf/ethereum/type/v2/type.pb.go | 3691 +++++++++++++++++ .../reorder-ordinals-empty.golden.json | 122 + go.mod | 2 +- server/config/config.go | 2 +- server/config/toml.go | 2 +- x/evm/types/tracer.go | 6 + 9 files changed, 6597 insertions(+), 3 deletions(-) create mode 100644 firehose/firehose.go create mode 100644 firehose/firehose_test.go create mode 100644 firehose/pb/sf/ethereum/type/v2/type.go create mode 100644 firehose/pb/sf/ethereum/type/v2/type.pb.go create mode 100755 firehose/testdata/firehose/reorder-ordinals-empty.golden.json diff --git a/firehose/firehose.go b/firehose/firehose.go new file mode 100644 index 0000000000..b0a7ffe5bd --- /dev/null +++ b/firehose/firehose.go @@ -0,0 +1,2255 @@ +package tracers + +import ( + "bytes" + "cmp" + "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "math/big" + "os" + "regexp" + "runtime" + "runtime/debug" + "slices" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "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/crypto" + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" + "golang.org/x/exp/maps" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" + + pbeth "github.com/evmos/ethermint/firehose/pb/sf/ethereum/type/v2" +) + +// Here what you can expect from the debugging levels: +// - Info == block start/end + trx start/end +// - Debug == Info + call start/end + error +// - Trace == Debug + state db changes, log, balance, nonce, code, storage, gas +var firehoseTracerLogLevel = strings.ToLower(os.Getenv("FIREHOSE_ETHEREUM_TRACER_LOG_LEVEL")) +var isFirehoseInfoEnabled = firehoseTracerLogLevel == "info" || firehoseTracerLogLevel == "debug" || firehoseTracerLogLevel == "trace" +var isFirehoseDebugEnabled = firehoseTracerLogLevel == "debug" || firehoseTracerLogLevel == "trace" +var isFirehoseTracerEnabled = firehoseTracerLogLevel == "trace" + +var emptyCommonAddress = common.Address{} +var emptyCommonHash = common.Hash{} + +func init() { + staticFirehoseChainValidationOnInit() + + tracers.LiveDirectory.Register("firehose", newFirehoseTracer) + + // Those 2 are defined but not used in this branch, they are kept because used in other branches + // so it's easier to keep them here and suppress the warning by faking a usage. + _ = new(Firehose).newIsolatedTransactionTracer + _ = new(FinalityStatus).populate +} + +func newFirehoseTracer(cfg json.RawMessage) (*tracing.Hooks, error) { + firehoseTracer, err := NewFirehoseFromRawJSON(cfg) + if err != nil { + return nil, err + } + + return NewTracingHooksFromFirehose(firehoseTracer), nil +} + +func NewTracingHooksFromFirehose(tracer *Firehose) *tracing.Hooks { + return &tracing.Hooks{ + OnBlockchainInit: tracer.OnBlockchainInit, + OnGenesisBlock: tracer.OnGenesisBlock, + OnBlockStart: tracer.OnBlockStart, + OnBlockEnd: tracer.OnBlockEnd, + OnSkippedBlock: tracer.OnSkippedBlock, + + OnTxStart: tracer.OnTxStart, + OnTxEnd: tracer.OnTxEnd, + OnEnter: tracer.OnCallEnter, + OnExit: tracer.OnCallExit, + OnOpcode: tracer.OnOpcode, + OnFault: tracer.OnOpcodeFault, + + OnBalanceChange: tracer.OnBalanceChange, + OnNonceChange: tracer.OnNonceChange, + OnCodeChange: tracer.OnCodeChange, + OnStorageChange: tracer.OnStorageChange, + OnGasChange: tracer.OnGasChange, + OnLog: tracer.OnLog, + + // This is being discussed in PR https://github.com/ethereum/go-ethereum/pull/29355 + // but Firehose needs them so we add handling for them in our patch. + OnSystemCallStart: tracer.OnSystemCallStart, + OnSystemCallEnd: tracer.OnSystemCallEnd, + } +} + +type FirehoseConfig struct { + ApplyBackwardCompatibility *bool `json:"applyBackwardCompatibility"` + + // Only used for testing, only possible through JSON configuration + private *privateFirehoseConfig +} + +type privateFirehoseConfig struct { + FlushToTestBuffer bool `json:"flushToTestBuffer"` + IgnoreGenesisBlock bool `json:"ignoreGenesisBlock"` +} + +// LogKeValues returns a list of key-values to be logged when the config is printed. +func (c *FirehoseConfig) LogKeyValues() []any { + applyBackwardCompatibility := "" + if c.ApplyBackwardCompatibility != nil { + applyBackwardCompatibility = strconv.FormatBool(*c.ApplyBackwardCompatibility) + } + + return []any{ + "config.applyBackwardCompatibility", applyBackwardCompatibility, + } +} + +type Firehose struct { + // Global state + outputBuffer *bytes.Buffer + initSent *atomic.Bool + chainConfig *params.ChainConfig + hasher crypto.KeccakState // Keccak256 hasher instance shared across tracer needs (non-concurrent safe) + hasherBuf common.Hash // Keccak256 hasher result array shared across tracer needs (non-concurrent safe) + tracerID string + // The FirehoseTracer is used in multiple chains, some for which were produced using a legacy version + // of the whole tracing infrastructure. This legacy version had many small bugs here and there that + // we must "reproduce" on some chain to ensure that the FirehoseTracer produces the same output + // as the legacy version. + // + // This value is fed from the tracer configuration. If explicitly set, the value set will be used + // here. If not set in the config, then we inspect `OnBlockchainInit` the chain config to determine + // if it's a network for which we must reproduce the legacy bugs. + applyBackwardCompatibility *bool + + // Block state + block *pbeth.Block + blockBaseFee *big.Int + blockOrdinal *Ordinal + blockFinality *FinalityStatus + blockRules params.Rules + blockReorderOrdinal bool + blockReorderOrdinalSnapshot uint64 + blockReorderOrdinalOnce sync.Once + + // Transaction state + evm *tracing.VMContext + transaction *pbeth.TransactionTrace + transactionLogIndex uint32 + inSystemCall bool + blockIsPrecompiledAddr func(addr common.Address) bool + transactionIsolated bool + transactionTransient *pbeth.TransactionTrace + + // Call state + callStack *CallStack + deferredCallState *DeferredCallState + latestCallEnterSuicided bool + + // Testing state, only used in tests and private configs + testingBuffer *bytes.Buffer + testingIgnoreGenesisBlock bool +} + +const FirehoseProtocolVersion = "3.0" + +func NewFirehoseFromRawJSON(cfg json.RawMessage) (*Firehose, error) { + var config FirehoseConfig + if len([]byte(cfg)) > 0 { + if err := json.Unmarshal(cfg, &config); err != nil { + return nil, fmt.Errorf("failed to parse Firehose config: %w", err) + } + + // Special handling of some "private" fields + type privateConfigRoot struct { + Private *privateFirehoseConfig `json:"_private"` + } + + var privateConfig privateConfigRoot + if err := json.Unmarshal(cfg, &privateConfig); err != nil { + log.Info("Firehose failed to parse private config, ignoring", "error", err) + } else { + config.private = privateConfig.Private + } + } + + return NewFirehose(&config), nil +} + +func NewFirehose(config *FirehoseConfig) *Firehose { + log.Info("Firehose tracer created", config.LogKeyValues()...) + + firehose := &Firehose{ + // Global state + outputBuffer: bytes.NewBuffer(make([]byte, 0, 100*1024*1024)), + initSent: new(atomic.Bool), + chainConfig: nil, + hasher: crypto.NewKeccakState(), + tracerID: "global", + applyBackwardCompatibility: config.ApplyBackwardCompatibility, + + // Block state + blockOrdinal: &Ordinal{}, + blockFinality: &FinalityStatus{}, + blockReorderOrdinal: false, + + // Transaction state + transactionLogIndex: 0, + + // Call state + callStack: NewCallStack(), + deferredCallState: NewDeferredCallState(), + latestCallEnterSuicided: false, + } + + if config.private != nil { + firehose.testingIgnoreGenesisBlock = config.private.IgnoreGenesisBlock + if config.private.FlushToTestBuffer { + firehose.testingBuffer = bytes.NewBuffer(nil) + } + } + + return firehose +} + +func (f *Firehose) newIsolatedTransactionTracer(tracerID string) *Firehose { + f.ensureInBlock(0) + + return &Firehose{ + // Global state + initSent: f.initSent, + chainConfig: f.chainConfig, + hasher: crypto.NewKeccakState(), + hasherBuf: common.Hash{}, + tracerID: tracerID, + + // Block state + block: f.block, + blockBaseFee: f.blockBaseFee, + blockOrdinal: &Ordinal{}, + blockFinality: f.blockFinality, + blockIsPrecompiledAddr: f.blockIsPrecompiledAddr, + blockRules: f.blockRules, + + // Transaction state + transactionLogIndex: 0, + transactionIsolated: true, + + // Call state + callStack: NewCallStack(), + deferredCallState: NewDeferredCallState(), + latestCallEnterSuicided: false, + } +} + +// resetBlock resets the block state only, do not reset transaction or call state +func (f *Firehose) resetBlock() { + f.block = nil + f.blockBaseFee = nil + f.blockOrdinal.Reset() + f.blockFinality.Reset() + f.blockIsPrecompiledAddr = nil + f.blockRules = params.Rules{} + f.blockReorderOrdinal = false + f.blockReorderOrdinalSnapshot = 0 + f.blockReorderOrdinalOnce = sync.Once{} +} + +// resetTransaction resets the transaction state and the call state in one shot +func (f *Firehose) resetTransaction() { + firehoseDebug("resetting transaction state") + + f.transaction = nil + f.evm = nil + f.transactionLogIndex = 0 + f.inSystemCall = false + f.transactionTransient = nil + + f.callStack.Reset() + f.latestCallEnterSuicided = false + f.deferredCallState.Reset() +} + +func (f *Firehose) OnBlockchainInit(chainConfig *params.ChainConfig) { + f.chainConfig = chainConfig + + if wasNeverSent := f.initSent.CompareAndSwap(false, true); wasNeverSent { + f.printToFirehose("INIT", FirehoseProtocolVersion, "geth", params.Version) + } else { + f.panicInvalidState("The OnBlockchainInit callback was called more than once", 0) + } + + if f.applyBackwardCompatibility == nil { + f.applyBackwardCompatibility = ptr(chainNeedsLegacyBackwardCompatibility(chainConfig.ChainID)) + } + + log.Info("Firehose tracer initialized", "chain_id", chainConfig.ChainID, "apply_backward_compatibility", *f.applyBackwardCompatibility, "protocol_version", FirehoseProtocolVersion) +} + +var mainnetChainID = big.NewInt(1) +var goerliChainID = big.NewInt(5) +var sepoliaChainID = big.NewInt(11155111) +var holeskyChainID = big.NewInt(17000) +var polygonMainnetChainID = big.NewInt(137) +var polygonMumbaiChainID = big.NewInt(80001) +var polygonAmoyChainID = big.NewInt(80002) +var bscMainnetChainID = big.NewInt(56) +var bscTestnetChainID = big.NewInt(97) + +func chainNeedsLegacyBackwardCompatibility(id *big.Int) bool { + return id.Cmp(mainnetChainID) == 0 || + id.Cmp(goerliChainID) == 0 || + id.Cmp(sepoliaChainID) == 0 || + id.Cmp(holeskyChainID) == 0 || + id.Cmp(polygonMainnetChainID) == 0 || + id.Cmp(polygonMumbaiChainID) == 0 || + id.Cmp(polygonAmoyChainID) == 0 || + id.Cmp(bscMainnetChainID) == 0 || + id.Cmp(bscTestnetChainID) == 0 +} + +func (f *Firehose) OnBlockStart(event tracing.BlockEvent) { + b := event.Block + firehoseInfo("block start (number=%d hash=%s)", b.NumberU64(), b.Hash()) + + f.ensureBlockChainInit() + + f.blockRules = f.chainConfig.Rules(b.Number(), f.chainConfig.TerminalTotalDifficultyPassed, b.Time()) + f.blockIsPrecompiledAddr = getActivePrecompilesChecker(f.blockRules) + + f.block = &pbeth.Block{ + Hash: b.Hash().Bytes(), + Number: b.Number().Uint64(), + Header: newBlockHeaderFromChainHeader(b.Header(), firehoseBigIntFromNative(new(big.Int).Add(event.TD, b.Difficulty()))), + Size: b.Size(), + Ver: 4, + } + + if *f.applyBackwardCompatibility { + f.block.Ver = 3 + } + + for _, uncle := range b.Uncles() { + // TODO: check if td should be part of uncles + f.block.Uncles = append(f.block.Uncles, newBlockHeaderFromChainHeader(uncle, nil)) + } + + if f.block.Header.BaseFeePerGas != nil { + f.blockBaseFee = f.block.Header.BaseFeePerGas.Native() + } + + f.blockFinality.populateFromChain(event.Finalized) +} + +func (f *Firehose) OnSkippedBlock(event tracing.BlockEvent) { + // Blocks that are skipped from blockchain that were known and should contain 0 transactions. + // It happened in the past, on Polygon if I recall right, that we missed block because some block + // went in this code path. + // + // See https: //github.com/streamingfast/go-ethereum/blob/a46903cf0cad829479ded66b369017914bf82314/core/blockchain.go#L1797-L1814 + if event.Block.Transactions().Len() > 0 { + panic(fmt.Sprintf("The tracer received an `OnSkippedBlock` block #%d (%s) with %d transactions, this according to core/blockchain.go should never happen and is an error", + event.Block.NumberU64(), + event.Block.Hash().Hex(), + event.Block.Transactions().Len(), + )) + } + + // Trace the block as normal, worst case the Firehose system will simply discard it at some point + f.OnBlockStart(event) + f.OnBlockEnd(nil) +} + +func getActivePrecompilesChecker(rules params.Rules) func(addr common.Address) bool { + activePrecompiles := vm.DefaultActivePrecompiles(rules) + + activePrecompilesMap := make(map[common.Address]bool, len(activePrecompiles)) + for _, addr := range activePrecompiles { + activePrecompilesMap[addr] = true + } + + return func(addr common.Address) bool { + _, found := activePrecompilesMap[addr] + return found + } +} + +func (f *Firehose) OnBlockEnd(err error) { + firehoseInfo("block ending (err=%s)", errorView(err)) + + if err == nil { + if f.blockReorderOrdinal { + f.reorderIsolatedTransactionsAndOrdinals() + } + + f.ensureInBlockAndNotInTrx() + f.printBlockToFirehose(f.block, f.blockFinality) + } else { + // An error occurred, could have happen in transaction/call context, we must not check if in trx/call, only check in block + f.ensureInBlock(0) + } + + f.resetBlock() + f.resetTransaction() + + firehoseInfo("block end") +} + +// reorderIsolatedTransactionsAndOrdinals is called right after all transactions have completed execution. It will sort transactions +// according to their index. +// +// But most importantly, will re-assign all the ordinals of each transaction recursively. When the parallel execution happened, +// all ordinal were made relative to the transaction they were contained in. But now, we are going to re-assign them to the +// global block ordinal by getting the current ordinal and ad it to the transaction ordinal and so forth. +func (f *Firehose) reorderIsolatedTransactionsAndOrdinals() { + if !f.blockReorderOrdinal { + firehoseInfo("post process isolated transactions skipped (block_reorder_ordinals=false)") + return + } + + ordinalBase := f.blockReorderOrdinalSnapshot + firehoseInfo("post processing isolated transactions sorting & re-assigning ordinals (ordinal_base=%d)", ordinalBase) + + slices.SortStableFunc(f.block.TransactionTraces, func(i, j *pbeth.TransactionTrace) int { + return int(i.Index) - int(j.Index) + }) + + baseline := ordinalBase + for _, trx := range f.block.TransactionTraces { + trx.BeginOrdinal += baseline + for _, call := range trx.Calls { + f.reorderCallOrdinals(call, baseline) + } + + for _, log := range trx.Receipt.Logs { + log.Ordinal += baseline + } + + trx.EndOrdinal += baseline + baseline = trx.EndOrdinal + } + + for _, ch := range f.block.BalanceChanges { + if ch.Ordinal >= ordinalBase { + ch.Ordinal += baseline + } + } + for _, ch := range f.block.CodeChanges { + if ch.Ordinal >= ordinalBase { + ch.Ordinal += baseline + } + } + for _, call := range f.block.SystemCalls { + if call.BeginOrdinal >= ordinalBase { + f.reorderCallOrdinals(call, baseline) + } + } +} + +func (f *Firehose) reorderCallOrdinals(call *pbeth.Call, ordinalBase uint64) (ordinalEnd uint64) { + if *f.applyBackwardCompatibility { + if call.BeginOrdinal != 0 { + call.BeginOrdinal += ordinalBase // consistent with a known small bug: root call has beginOrdinal set to 0 + } + } else { + call.BeginOrdinal += ordinalBase + } + + for _, log := range call.Logs { + log.Ordinal += ordinalBase + } + for _, act := range call.AccountCreations { + act.Ordinal += ordinalBase + } + for _, ch := range call.BalanceChanges { + ch.Ordinal += ordinalBase + } + for _, ch := range call.GasChanges { + ch.Ordinal += ordinalBase + } + for _, ch := range call.NonceChanges { + ch.Ordinal += ordinalBase + } + for _, ch := range call.StorageChanges { + ch.Ordinal += ordinalBase + } + for _, ch := range call.CodeChanges { + ch.Ordinal += ordinalBase + } + + call.EndOrdinal += ordinalBase + + return call.EndOrdinal +} + +func (f *Firehose) OnSystemCallStart() { + firehoseInfo("system call start") + f.ensureInBlockAndNotInTrx() + + f.inSystemCall = true + f.transaction = &pbeth.TransactionTrace{} +} + +func (f *Firehose) OnSystemCallEnd() { + f.ensureInBlockAndInTrx() + f.ensureInSystemCall() + + f.block.SystemCalls = append(f.block.SystemCalls, f.transaction.Calls...) + + f.resetTransaction() +} + +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())) + + f.ensureInBlockAndNotInTrxAndNotInCall() + + f.evm = evm + var to common.Address + if tx.To() == nil { + to = crypto.CreateAddress(from, evm.StateDB.GetNonce(from)) + } else { + to = *tx.To() + } + + f.onTxStart(tx, tx.Hash(), from, to) +} + +// onTxStart is used internally a two places, in the normal "tracer" and in the "OnGenesisBlock", +// we manually pass some override to the `tx` because genesis block has a different way of creating +// the transaction that wraps the genesis block. +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{ + BeginOrdinal: f.blockOrdinal.Next(), + Hash: hash.Bytes(), + From: from.Bytes(), + To: to.Bytes(), + Nonce: tx.Nonce(), + GasLimit: tx.Gas(), + GasPrice: gasPrice(tx, f.blockBaseFee), + Value: firehoseBigIntFromNative(tx.Value()), + Input: tx.Data(), + V: emptyBytesToNil(v.Bytes()), + R: normalizeSignaturePoint(r.Bytes()), + S: normalizeSignaturePoint(s.Bytes()), + Type: transactionTypeFromChainTxType(tx.Type()), + AccessList: newAccessListFromChain(tx.AccessList()), + MaxFeePerGas: maxFeePerGas(tx), + MaxPriorityFeePerGas: maxPriorityFeePerGas(tx), + BlobGas: blobGas, + BlobGasFeeCap: firehoseBigIntFromNative(tx.BlobGasFeeCap()), + BlobHashes: newBlobHashesFromChain(tx.BlobHashes()), + } +} + +func (f *Firehose) OnTxEnd(receipt *types.Receipt, err error) { + firehoseInfo("trx ending (tracer=%s, isolated=%t, error=%s)", f.tracerID, f.transactionIsolated, errorView(err)) + f.ensureInBlockAndInTrx() + + trxTrace := f.completeTransaction(receipt) + + // In this case, we are in some kind of parallel processing and we must simply add the transaction + // to a transient storage (and not in the block directly). Adding it to the block will be done by the + // `OnTxCommit` callback. + if f.transactionIsolated { + f.transactionTransient = trxTrace + + // We must not reset transaction here. In the isolated transaction tracer, the transaction is reset + // by the `OnTxReset` callback which comes from outside the tracer. Second, resetting the transaction + // also resets the [f.transactionTransient] field which is the one we want to keep on completion + // of an isolated transaction. + } else { + f.block.TransactionTraces = append(f.block.TransactionTraces, trxTrace) + + // The reset must be done as the very last thing as the CallStack needs to be + // properly populated for the `completeTransaction` call above to complete correctly. + f.resetTransaction() + } + + firehoseInfo("trx end (tracer=%s)", f.tracerID) +} + +func (f *Firehose) completeTransaction(receipt *types.Receipt) *pbeth.TransactionTrace { + 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 { + return cmp.Compare(i.Index, j.Index) + }) + + rootCall := f.transaction.Calls[0] + + if !f.deferredCallState.IsEmpty() { + f.deferredCallState.MaybePopulateCallAndReset("root", rootCall) + } + + // Receipt can be nil if an error occurred during the transaction execution, right now we don't have it + if receipt != nil { + f.transaction.Index = uint32(receipt.TransactionIndex) + f.transaction.GasUsed = receipt.GasUsed + f.transaction.Receipt = newTxReceiptFromChain(receipt, f.transaction.Type) + f.transaction.Status = transactionStatusFromChainTxReceipt(receipt.Status) + } + + // It's possible that the transaction was reverted, but we still have a receipt, in that case, we must + // check the root call + if rootCall.StatusReverted { + f.transaction.Status = pbeth.TransactionTraceStatus_REVERTED + } + + // Order is important, we must populate the state reverted before we remove the log block index and re-assign ordinals + f.populateStateReverted() + f.removeLogBlockIndexOnStateRevertedCalls() + f.assignOrdinalAndIndexToReceiptLogs() + + if *f.applyBackwardCompatibility { + // Known Firehose issue: This field has never been populated in the old Firehose instrumentation + } else { + f.transaction.ReturnData = rootCall.ReturnData + } + + f.transaction.EndOrdinal = f.blockOrdinal.Next() + + return f.transaction +} + +func (f *Firehose) populateStateReverted() { + // Calls are ordered by execution index. So the algo is quite simple. + // We loop through the flat calls, at each call, if the parent is present + // and reverted, the current call is reverted. Otherwise, if the current call + // is failed, the state is reverted. In all other cases, we simply continue + // our iteration loop. + // + // This works because we see the parent before its children, and since we + // trickle down the state reverted value down the children, checking the parent + // of a call will always tell us if the whole chain of parent/child should + // be reverted + // + calls := f.transaction.Calls + for _, call := range f.transaction.Calls { + var parent *pbeth.Call + if call.ParentIndex > 0 { + parent = calls[call.ParentIndex-1] + } + + call.StateReverted = (parent != nil && parent.StateReverted) || call.StatusFailed + } +} + +func (f *Firehose) removeLogBlockIndexOnStateRevertedCalls() { + for _, call := range f.transaction.Calls { + if call.StateReverted { + for _, log := range call.Logs { + log.BlockIndex = 0 + } + } + } +} + +func (f *Firehose) assignOrdinalAndIndexToReceiptLogs() { + firehoseTrace("assigning ordinal and index to logs") + defer func() { + firehoseTrace("assigning ordinal and index to logs terminated") + }() + + trx := f.transaction + + callLogs := []*pbeth.Log{} + for _, call := range trx.Calls { + firehoseTrace("checking call (reverted=%t logs=%d)", call.StateReverted, len(call.Logs)) + if call.StateReverted { + continue + } + + callLogs = append(callLogs, call.Logs...) + } + + slices.SortFunc(callLogs, func(i, j *pbeth.Log) int { + return cmp.Compare(i.Ordinal, j.Ordinal) + }) + + // When a transaction failed the receipt can be nil, so we need to deal with this + var receiptsLogs []*pbeth.Log + if trx.Receipt == nil { + if len(callLogs) == 0 { + // No logs in the transaction (nor in calls), nothing to do + return + } + + panic(fmt.Errorf( + "mismatch between Firehose call logs and Ethereum transaction %s receipt logs at block #%d, the transaction has no receipt (failed) so there is no logs but it exists %d Firehose call logs", + hex.EncodeToString(trx.Hash), + f.block.Number, + len(callLogs), + )) + } else { + receiptsLogs = trx.Receipt.Logs + } + + if len(callLogs) != len(receiptsLogs) { + panic(fmt.Errorf( + "mismatch between Firehose call logs and Ethereum transaction %s receipt logs at block #%d, transaction receipt has %d logs but there is %d Firehose call logs", + hex.EncodeToString(trx.Hash), + f.block.Number, + len(receiptsLogs), + len(callLogs), + )) + } + + for i := 0; i < len(callLogs); i++ { + callLog := callLogs[i] + receiptsLog := receiptsLogs[i] + + result := &validationResult{} + // Ordinal must **not** be checked as we are assigning it here below after the validations + validateBytesField(result, "Address", callLog.Address, receiptsLog.Address) + validateUint32Field(result, "BlockIndex", callLog.BlockIndex, receiptsLog.BlockIndex) + validateBytesField(result, "Data", callLog.Data, receiptsLog.Data) + validateArrayOfBytesField(result, "Topics", callLog.Topics, receiptsLog.Topics) + + if len(result.failures) > 0 { + result.panicOnAnyFailures("mismatch between Firehose call log and Ethereum transaction receipt log at index %d", i) + } + + receiptsLog.Index = callLog.Index + receiptsLog.Ordinal = callLog.Ordinal + } +} + +// OnCallEnter implements the EVMLogger interface to initialize the tracing operation. +func (f *Firehose) OnCallEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + opCode := vm.OpCode(typ) + + var callType pbeth.CallType + if isRootCall := depth == 0; isRootCall { + callType = rootCallType(opCode == vm.CREATE) + } else { + // The invokation for vm.SELFDESTRUCT is called while already in another call and is recorded specially + // in the Geth tracer and generates `OnEnter/OnExit` callbacks. However in Firehose, self destruction + // simply sets the call as having called suicided so there is no extra call. + // + // So we ignore `OnEnter/OnExit` callbacks for `SELFDESTRUCT` opcode, we ignore it here and set + // a special sentinel variable that will tell `OnExit` to ignore itself. + if opCode == vm.SELFDESTRUCT { + f.ensureInCall() + f.callStack.Peek().Suicide = true + + // The next OnCallExit must be ignored, this variable will make the next OnCallExit to be ignored + f.latestCallEnterSuicided = true + return + } + + callType = callTypeFromOpCode(opCode) + if callType == pbeth.CallType_UNSPECIFIED { + panic(fmt.Errorf("unexpected call type, received OpCode %s but only call related opcode (CALL, CREATE, CREATE2, STATIC, DELEGATECALL and CALLCODE) or SELFDESTRUCT is accepted", opCode)) + } + } + + f.callStart(computeCallSource(depth), callType, from, to, input, gas, value) +} + +// OnCallExit is called after the call finishes to finalize the tracing. +func (f *Firehose) OnCallExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + f.callEnd(computeCallSource(depth), output, gasUsed, err, reverted) +} + +// OnOpcode implements the EVMLogger interface to trace a single step of VM execution. +func (f *Firehose) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { + firehoseTrace("on opcode (op=%s gas=%d cost=%d, err=%s)", vm.OpCode(op), gas, cost, errorView(err)) + + if activeCall := f.callStack.Peek(); activeCall != nil { + opCode := vm.OpCode(op) + f.captureInterpreterStep(activeCall, pc, opCode, gas, cost, scope, rData, depth, err) + + // The rest of the logic expects that a call succeeded, nothing to do more here if the interpreter failed on this OpCode + if err != nil { + return + } + + // The gas change must come first to retain Firehose backward compatibility. Indeed, before Firehose 3.0, + // we had a specific method `OnKeccakPreimage` that was called during the KECCAK256 opcode. However, in + // the new model, we do it through `OnOpcode`. + // + // The gas change recording in the previous Firehose patch was done before calling `OnKeccakPreimage` so + // we must do the same here. + // + // No need to wrap in apply backward compatibility, the old behavior is fine in all cases. + if cost > 0 { + if reason, found := opCodeToGasChangeReasonMap[opCode]; found { + activeCall.GasChanges = append(activeCall.GasChanges, f.newGasChange("state", gas, gas-cost, reason)) + } + } + + if opCode == vm.KECCAK256 { + f.onOpcodeKeccak256(activeCall, scope.StackData(), Memory(scope.MemoryData())) + } + } +} + +// onOpcodeKeccak256 is called during the SHA3 (a.k.a KECCAK256) opcode it's known +// in Firehose tracer as Keccak preimages. The preimage is the input data that +// was used to produce the given keccak hash. +func (f *Firehose) onOpcodeKeccak256(call *pbeth.Call, stack []uint256.Int, memory Memory) { + if call.KeccakPreimages == nil { + call.KeccakPreimages = make(map[string]string) + } + + offset, size := stack[len(stack)-1], stack[len(stack)-2] + preImage := memory.GetPtrUint256(&offset, &size) + + // We should have exclusive access to the hasher, we can safely reset it. + f.hasher.Reset() + f.hasher.Write(preImage) + f.hasher.Read(f.hasherBuf[:]) + + encodedData := hex.EncodeToString(preImage) + + if *f.applyBackwardCompatibility { + // Known Firehose issue: It appears the old Firehose instrumentation have a bug + // where when the keccak256 preimage is empty, it is written as "." which is + // completely wrong. + // + // To keep the same behavior, we will write the preimage as a "." when the encoded + // data is an empty string. + if encodedData == "" { + encodedData = "." + } + } + + call.KeccakPreimages[hex.EncodeToString(f.hasherBuf[:])] = encodedData +} + +var opCodeToGasChangeReasonMap = map[vm.OpCode]pbeth.GasChange_Reason{ + vm.CREATE: pbeth.GasChange_REASON_CONTRACT_CREATION, + vm.CREATE2: pbeth.GasChange_REASON_CONTRACT_CREATION2, + vm.CALL: pbeth.GasChange_REASON_CALL, + vm.STATICCALL: pbeth.GasChange_REASON_STATIC_CALL, + vm.CALLCODE: pbeth.GasChange_REASON_CALL_CODE, + vm.DELEGATECALL: pbeth.GasChange_REASON_DELEGATE_CALL, + vm.RETURN: pbeth.GasChange_REASON_RETURN, + vm.REVERT: pbeth.GasChange_REASON_REVERT, + vm.LOG0: pbeth.GasChange_REASON_EVENT_LOG, + vm.LOG1: pbeth.GasChange_REASON_EVENT_LOG, + vm.LOG2: pbeth.GasChange_REASON_EVENT_LOG, + vm.LOG3: pbeth.GasChange_REASON_EVENT_LOG, + vm.LOG4: pbeth.GasChange_REASON_EVENT_LOG, + vm.SELFDESTRUCT: pbeth.GasChange_REASON_SELF_DESTRUCT, + vm.CALLDATACOPY: pbeth.GasChange_REASON_CALL_DATA_COPY, + vm.CODECOPY: pbeth.GasChange_REASON_CODE_COPY, + vm.EXTCODECOPY: pbeth.GasChange_REASON_EXT_CODE_COPY, + vm.RETURNDATACOPY: pbeth.GasChange_REASON_RETURN_DATA_COPY, +} + +// OnOpcodeFault implements the EVMLogger interface to trace an execution fault. +func (f *Firehose) OnOpcodeFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) { + if activeCall := f.callStack.Peek(); activeCall != nil { + f.captureInterpreterStep(activeCall, pc, vm.OpCode(op), gas, cost, scope, nil, depth, err) + } +} + +func (f *Firehose) captureInterpreterStep(activeCall *pbeth.Call, pc uint64, op vm.OpCode, gas, cost uint64, _ tracing.OpContext, rData []byte, depth int, err error) { + _, _, _, _, _, _, _ = pc, op, gas, cost, rData, depth, err + + if *f.applyBackwardCompatibility { + // for call, we need to process the executed code here + // since in old firehose executed code calculation depends if the code exist + if activeCall.CallType == pbeth.CallType_CALL && !activeCall.ExecutedCode { + firehoseTrace("Intepreter step for callType_CALL") + activeCall.ExecutedCode = len(activeCall.Input) > 0 + } + } else { + activeCall.ExecutedCode = true + } +} + +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)) + f.ensureInBlockAndInTrx() + + if *f.applyBackwardCompatibility { + // Known Firehose issue: Contract creation call's input is always `nil` in old Firehose patch + // due to an oversight that having it in `CodeChange` would be sufficient but this is wrong + // as constructor's input are not part of the code change but part of the call input. + if callType == pbeth.CallType_CREATE { + input = nil + } + } + + call := &pbeth.Call{ + CallType: callType, + Depth: 0, + Caller: from.Bytes(), + Address: to.Bytes(), + // We need to clone `input` received by the tracer as it's re-used within Geth! + Input: bytes.Clone(input), + Value: firehoseBigIntFromNative(value), + GasLimit: gas, + } + + if *f.applyBackwardCompatibility { + // Known Firehose issue: The BeginOrdinal of the genesis block root call is never actually + // incremented and it's always 0. + // + // Ref 042a2ff03fd623f151d7726314b8aad6 + + call.BeginOrdinal = 0 + call.ExecutedCode = f.getExecutedCode(f.evm, call) + + if f.block.Number != 0 { + call.BeginOrdinal = f.blockOrdinal.Next() + } + } else { + call.BeginOrdinal = f.blockOrdinal.Next() + } + + if err := f.deferredCallState.MaybePopulateCallAndReset(source, call); err != nil { + panic(err) + } + + if *f.applyBackwardCompatibility { + // Known Firehose issue: The `BeginOrdinal` of the root call is incremented but must + // be assigned back to 0 because of a bug in the console reader. remove on new chain. + // + // New chain integration should remove this `if` statement + if source == "root" { + call.BeginOrdinal = 0 + } + } + + f.callStack.Push(call) +} + +// Known Firehose issue: How we computed `executed_code` before was not working for contract's that only +// deal with ETH transfer through Solidity `receive()` built-in since those call have `len(input) == 0` +// +// Older comment keeping for future review: +// +// For precompiled address however, interpreter does not run so determine there was a bug in Firehose instrumentation where we would +// +// if call.ExecutedCode || (f.isPrecompiledAddr != nil && f.isPrecompiledAddr(common.BytesToAddress(call.Address))) { +// // In this case, we are sure that some code executed. This translates in the old Firehose instrumentation +// // that it would have **never** emitted an `account_without_code`. +// // +// // When no `account_without_code` was executed in the previous Firehose instrumentation, +// // the `call.ExecutedCode` defaulted to the condition below +// call.ExecutedCode = call.CallType != pbeth.CallType_CREATE && len(call.Input) > 0 +// } else { +// +// // In all other cases, we are sure that no code executed. This translates in the old Firehose instrumentation +// // that it would have emitted an `account_without_code` and it would have then forced set the `call.ExecutedCode` +// // to `false`. +// call.ExecutedCode = false +// } +func (f *Firehose) getExecutedCode(evm *tracing.VMContext, call *pbeth.Call) bool { + precompile := f.blockIsPrecompiledAddr(common.BytesToAddress(call.Address)) + + if evm != nil && call.CallType == pbeth.CallType_CALL { + if !evm.StateDB.Exist(common.BytesToAddress(call.Address)) && + !precompile && f.blockRules.IsEIP158 && + (call.Value == nil || call.Value.Native().Sign() == 0) { + firehoseTrace("executed code IsSpuriousDragon (callType=%s inputLength=%d)", call.CallType.String(), len(call.Input)) + return call.CallType != pbeth.CallType_CREATE && len(call.Input) > 0 + } + } + + if precompile { + firehoseTrace("executed code isprecompile (callType=%s inputLength=%d)", call.CallType.String(), len(call.Input)) + return call.CallType != pbeth.CallType_CREATE && len(call.Input) > 0 + } + + if call.CallType == pbeth.CallType_CALL { + firehoseTrace("executed code callType_CALL") + // calculation for executed code will happen in captureInterpreterStep + return false + } + + firehoseTrace("executed code default (callType=%s inputLength=%d)", call.CallType.String(), len(call.Input)) + return call.CallType != pbeth.CallType_CREATE && len(call.Input) > 0 +} + +func (f *Firehose) callEnd(source string, output []byte, gasUsed uint64, err error, reverted bool) { + firehoseDebug("call end (source=%s index=%d output=%s gasUsed=%d err=%s reverted=%t)", source, f.callStack.ActiveIndex(), outputView(output), gasUsed, errorView(err), reverted) + + if f.latestCallEnterSuicided { + if source != "child" { + panic(fmt.Errorf("unexpected source for suicided call end, expected child but got %s, suicide are always produced on a 'child' source", source)) + } + + // Geth native tracer does a `OnEnter(SELFDESTRUCT, ...)/OnExit(...)`, we must skip the `OnExit` call + // in that case because we did not push it on our CallStack. + f.latestCallEnterSuicided = false + return + } + + f.ensureInBlockAndInTrxAndInCall() + + call := f.callStack.Pop() + call.GasConsumed = gasUsed + + // For create call, we do not save the returned value which is the actual contract's code + if call.CallType != pbeth.CallType_CREATE { + call.ReturnData = bytes.Clone(output) + } + + if reverted { + failureReason := "" + if err != nil { + failureReason = err.Error() + } + + call.FailureReason = failureReason + call.StatusFailed = true + + // We also treat ErrInsufficientBalance and ErrDepth as reverted in Firehose model + // because they do not cost any gas. + call.StatusReverted = errors.Is(err, vm.ErrExecutionReverted) || errors.Is(err, vm.ErrInsufficientBalance) || errors.Is(err, vm.ErrDepth) + + if *f.applyBackwardCompatibility { + // Known Firehose issue: FIXME Document! + if !call.ExecutedCode && (errors.Is(err, vm.ErrInsufficientBalance) || errors.Is(err, vm.ErrDepth)) { + call.ExecutedCode = call.CallType != pbeth.CallType_CREATE && len(call.Input) > 0 + } + } + } + + if *f.applyBackwardCompatibility { + // Known Firehose issue: The EndOrdinal of the genesis block root call is never actually + // incremented and it's always 0. + if f.block.Number != 0 { + call.EndOrdinal = f.blockOrdinal.Next() + } + } else { + call.EndOrdinal = f.blockOrdinal.Next() + } + + f.transaction.Calls = append(f.transaction.Calls, call) +} + +func computeCallSource(depth int) string { + if depth == 0 { + return "root" + } + + return "child" +} + +func (f *Firehose) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { + firehoseInfo("genesis block (number=%d hash=%s)", b.NumberU64(), b.Hash()) + if f.testingIgnoreGenesisBlock { + firehoseInfo("genesis block ignored due to testing config") + return + } + + f.ensureBlockChainInit() + + f.OnBlockStart(tracing.BlockEvent{Block: b, TD: big.NewInt(0), Finalized: nil, Safe: nil}) + f.onTxStart(types.NewTx(&types.LegacyTx{}), emptyCommonHash, emptyCommonAddress, emptyCommonAddress) + f.OnCallEnter(0, byte(vm.CALL), emptyCommonAddress, emptyCommonAddress, nil, 0, nil) + + for _, addr := range sortedKeys(alloc) { + account := alloc[addr] + + if account.Balance != nil && account.Balance.Sign() != 0 { + activeCall := f.callStack.Peek() + activeCall.BalanceChanges = append(activeCall.BalanceChanges, f.newBalanceChange("genesis", addr, common.Big0, account.Balance, pbeth.BalanceChange_REASON_GENESIS_BALANCE)) + } + + if len(account.Code) > 0 { + f.OnCodeChange(addr, emptyCommonHash, nil, common.BytesToHash(crypto.Keccak256(account.Code)), account.Code) + } + + if account.Nonce > 0 { + f.OnNonceChange(addr, 0, account.Nonce) + } + + for _, key := range sortedKeys(account.Storage) { + f.OnStorageChange(addr, key, emptyCommonHash, account.Storage[key]) + } + } + + f.OnCallExit(0, nil, 0, nil, false) + f.OnTxEnd(&types.Receipt{ + PostState: b.Root().Bytes(), + Status: types.ReceiptStatusSuccessful, + }, nil) + f.OnBlockEnd(nil) +} + +type bytesGetter interface { + comparable + Bytes() []byte +} + +func sortedKeys[K bytesGetter, V any](m map[K]V) []K { + keys := maps.Keys(m) + slices.SortFunc(keys, func(i, j K) int { + return bytes.Compare(i.Bytes(), j.Bytes()) + }) + + return keys +} + +func (f *Firehose) OnBalanceChange(a common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { + if reason == tracing.BalanceChangeUnspecified { + // We ignore those, if they are mislabelled, too bad so particular attention needs to be ported to this + return + } + + if *f.applyBackwardCompatibility { + // Known Firehose issue: It's possible to burn Ether by sending some ether to a suicided account. In those case, + // at theend of block producing, StateDB finalize the block by burning ether from the account. This is something + // we were not tracking in the old Firehose instrumentation. + if reason == tracing.BalanceDecreaseSelfdestructBurn { + return + } + } + + f.ensureInBlockOrTrx() + + change := f.newBalanceChange("tracer", a, prev, new, balanceChangeReasonFromChain(reason)) + + if f.transaction != nil { + activeCall := f.callStack.Peek() + + // There is an initial transfer happening will the call is not yet started, we track it manually + if activeCall == nil { + f.deferredCallState.balanceChanges = append(f.deferredCallState.balanceChanges, change) + return + } + + activeCall.BalanceChanges = append(activeCall.BalanceChanges, change) + } else { + f.block.BalanceChanges = append(f.block.BalanceChanges, change) + } +} + +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) + + if reason == pbeth.BalanceChange_REASON_UNKNOWN { + panic(fmt.Errorf("received unknown balance change reason %s", reason)) + } + + return &pbeth.BalanceChange{ + Ordinal: f.blockOrdinal.Next(), + Address: address.Bytes(), + OldValue: firehoseBigIntFromNative(oldValue), + NewValue: firehoseBigIntFromNative(newValue), + Reason: reason, + } +} + +func (f *Firehose) OnNonceChange(a common.Address, prev, new uint64) { + f.ensureInBlockAndInTrx() + + activeCall := f.callStack.Peek() + change := &pbeth.NonceChange{ + Address: a.Bytes(), + OldValue: prev, + NewValue: new, + Ordinal: f.blockOrdinal.Next(), + } + + // There is an initial nonce change happening when the call is not yet started, we track it manually + if activeCall == nil { + f.deferredCallState.nonceChanges = append(f.deferredCallState.nonceChanges, change) + return + } + + activeCall.NonceChanges = append(activeCall.NonceChanges, change) +} + +func (f *Firehose) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) { + f.ensureInBlockOrTrx() + + change := &pbeth.CodeChange{ + Address: a.Bytes(), + OldHash: prevCodeHash.Bytes(), + OldCode: prev, + NewHash: codeHash.Bytes(), + NewCode: code, + Ordinal: f.blockOrdinal.Next(), + } + + if f.transaction != nil { + activeCall := f.callStack.Peek() + if activeCall == nil { + f.panicInvalidState("caller expected to be in call state but we were not, this is a bug", 0) + } + + activeCall.CodeChanges = append(activeCall.CodeChanges, change) + } else { + f.block.CodeChanges = append(f.block.CodeChanges, change) + } +} + +func (f *Firehose) OnStorageChange(a common.Address, k, prev, new common.Hash) { + f.ensureInBlockAndInTrxAndInCall() + + activeCall := f.callStack.Peek() + activeCall.StorageChanges = append(activeCall.StorageChanges, &pbeth.StorageChange{ + Address: a.Bytes(), + Key: k.Bytes(), + OldValue: prev.Bytes(), + NewValue: new.Bytes(), + Ordinal: f.blockOrdinal.Next(), + }) +} + +func (f *Firehose) OnLog(l *types.Log) { + f.ensureInBlockAndInTrxAndInCall() + + topics := make([][]byte, len(l.Topics)) + for i, topic := range l.Topics { + topics[i] = topic.Bytes() + } + + activeCall := f.callStack.Peek() + firehoseTrace("adding log to call (address=%s call=%d [has already %d logs])", l.Address, activeCall.Index, len(activeCall.Logs)) + + activeCall.Logs = append(activeCall.Logs, &pbeth.Log{ + Address: l.Address.Bytes(), + Topics: topics, + Data: l.Data, + Index: f.transactionLogIndex, + BlockIndex: uint32(l.Index), + Ordinal: f.blockOrdinal.Next(), + }) + + f.transactionLogIndex++ +} + +func (f *Firehose) OnGasChange(old, new uint64, reason tracing.GasChangeReason) { + f.ensureInBlockAndInTrx() + + if old == new { + return + } + + if reason == tracing.GasChangeCallOpCode { + // We ignore those because we track OpCode gas consumption manually by tracking the gas value at `OnOpcode` call + return + } + + if *f.applyBackwardCompatibility { + // Known Firehose issue: New geth native tracer added more gas change, some that we were indeed missing and + // should have included in our previous patch. + // + // Ref eb1916a67d9bea03df16a7a3e2cfac72 + if reason == tracing.GasChangeTxInitialBalance || + reason == tracing.GasChangeTxRefunds || + reason == tracing.GasChangeTxLeftOverReturned || + reason == tracing.GasChangeCallInitialBalance || + reason == tracing.GasChangeCallLeftOverReturned { + return + } + } + + activeCall := f.callStack.Peek() + change := f.newGasChange("tracer", old, new, gasChangeReasonFromChain(reason)) + + // There is an initial gas consumption happening will the call is not yet started, we track it manually + if activeCall == nil { + f.deferredCallState.gasChanges = append(f.deferredCallState.gasChanges, change) + return + } + + activeCall.GasChanges = append(activeCall.GasChanges, change) +} + +func (f *Firehose) newGasChange(tag string, oldValue, newValue uint64, reason pbeth.GasChange_Reason) *pbeth.GasChange { + firehoseTrace("gas consumed (tag=%s before=%d after=%d reason=%s)", tag, oldValue, newValue, reason) + + // Should already be checked by the caller, but we keep it here for safety if the code ever change + if reason == pbeth.GasChange_REASON_UNKNOWN { + panic(fmt.Errorf("received unknown gas change reason %s", reason)) + } + + return &pbeth.GasChange{ + OldValue: oldValue, + NewValue: newValue, + Ordinal: f.blockOrdinal.Next(), + Reason: reason, + } +} + +func (f *Firehose) ensureBlockChainInit() { + if f.chainConfig == nil { + f.panicInvalidState("the OnBlockchainInit hook should have been called at this point", 2) + } +} + +func (f *Firehose) ensureInBlock(callerSkip int) { + if f.block == nil { + f.panicInvalidState("caller expected to be in block state but we were not, this is a bug", callerSkip+1) + } + + if f.chainConfig == nil { + f.panicInvalidState("the OnBlockchainInit hook should have been called at this point", callerSkip+1) + } +} + +func (f *Firehose) ensureNotInBlock(callerSkip int) { + if f.block != nil { + f.panicInvalidState("caller expected to not be in block state but we were, this is a bug", callerSkip+1) + } +} + +// Suppress lint warning about unusued method, we keep it in the patch because it's used in other +// network which pulls this branch. +var _ = new(Firehose).ensureNotInBlock + +func (f *Firehose) ensureInBlockAndInTrx() { + f.ensureInBlock(2) + + if f.transaction == nil { + f.panicInvalidState("caller expected to be in transaction state but we were not, this is a bug", 2) + } +} + +func (f *Firehose) ensureInBlockAndNotInTrx() { + f.ensureInBlock(2) + + if f.transaction != nil { + f.panicInvalidState("caller expected to not be in transaction state but we were, this is a bug", 2) + } +} + +func (f *Firehose) ensureInBlockAndNotInTrxAndNotInCall() { + f.ensureInBlock(2) + + if f.transaction != nil { + f.panicInvalidState("caller expected to not be in transaction state but we were, this is a bug", 2) + } + + if f.callStack.HasActiveCall() { + f.panicInvalidState("caller expected to not be in call state but we were, this is a bug", 2) + } +} + +func (f *Firehose) ensureInBlockOrTrx() { + if f.transaction == nil && f.block == nil { + f.panicInvalidState("caller expected to be in either block or transaction state but we were not, this is a bug", 2) + } +} + +func (f *Firehose) ensureInBlockAndInTrxAndInCall() { + if f.transaction == nil || f.block == nil { + f.panicInvalidState("caller expected to be in block and in transaction but we were not, this is a bug", 2) + } + + if !f.callStack.HasActiveCall() { + f.panicInvalidState("caller expected to be in call state but we were not, this is a bug", 2) + } +} + +func (f *Firehose) ensureInCall() { + if f.block == nil { + f.panicInvalidState("caller expected to be in call state but we were not, this is a bug", 2) + } +} + +func (f *Firehose) ensureInSystemCall() { + if !f.inSystemCall { + f.panicInvalidState("call expected to be in system call state but we were not, this is a bug", 2) + } +} + +func (f *Firehose) panicInvalidState(msg string, callerSkip int) string { + caller := "N/A" + if _, file, line, ok := runtime.Caller(callerSkip); ok { + caller = fmt.Sprintf("%s:%d", file, line) + } + + if f.block != nil { + msg += fmt.Sprintf(" at block #%d (%s)", f.block.Number, hex.EncodeToString(f.block.Hash)) + } + + if f.transaction != nil { + msg += fmt.Sprintf(" in transaction %s", hex.EncodeToString(f.transaction.Hash)) + } + + panic(fmt.Errorf("%s (caller=%s, init=%t, inBlock=%t, inTransaction=%t, inCall=%t)", msg, caller, f.chainConfig != nil, f.block != nil, f.transaction != nil, f.callStack.HasActiveCall())) +} + +// printBlockToFirehose is a helper function to print a block to Firehose protocl format. +func (f *Firehose) printBlockToFirehose(block *pbeth.Block, finalityStatus *FinalityStatus) { + marshalled, err := proto.Marshal(block) + if err != nil { + panic(fmt.Errorf("failed to marshal block: %w", err)) + } + + f.outputBuffer.Reset() + + previousHash := block.PreviousID() + previousNum := 0 + if block.Number > 0 { + previousNum = int(block.Number) - 1 + } + + libNum := finalityStatus.LastIrreversibleBlockNumber + if finalityStatus.IsEmpty() { + // FIXME: We should have access to the genesis block to perform this operation to ensure we never go below the + // the genesis block + if block.Number >= 200 { + libNum = block.Number - 200 + } else { + libNum = 0 + } + } + + // **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())) + + encoder := base64.NewEncoder(base64.StdEncoding, f.outputBuffer) + if _, err = encoder.Write(marshalled); err != nil { + panic(fmt.Errorf("write to encoder should have been infaillible: %w", err)) + } + + if err := encoder.Close(); err != nil { + panic(fmt.Errorf("closing encoder should have been infaillible: %w", err)) + } + + f.outputBuffer.WriteString("\n") + + f.flushToFirehose(f.outputBuffer.Bytes()) +} + +// printToFirehose is an easy way to print to Firehose format, it essentially +// adds the "FIRE" prefix to the input and joins the input with spaces as well +// as adding a newline at the end. +// +// It flushes this through [flushToFirehose] to the `os.Stdout` writer. +func (f *Firehose) printToFirehose(input ...string) { + f.flushToFirehose([]byte("FIRE " + strings.Join(input, " ") + "\n")) +} + +// flushToFirehose sends data to Firehose via `io.Writter` checking for errors +// and retrying if necessary. +// +// If error is still present after 10 retries, prints an error message to `writer` +// as well as writing file `/tmp/firehose_writer_failed_print.log` with the same +// error message. +func (f *Firehose) flushToFirehose(in []byte) { + var writer io.Writer = os.Stdout + if f.testingBuffer != nil { + writer = f.testingBuffer + } + + var written int + var err error + loops := 10 + for i := 0; i < loops; i++ { + written, err = writer.Write(in) + + if len(in) == written { + return + } + + in = in[written:] + if i == loops-1 { + break + } + } + + errstr := fmt.Sprintf("\nFIREHOSE FAILED WRITING %dx: %s\n", loops, err) + os.WriteFile("/tmp/firehose_writer_failed_print.log", []byte(errstr), 0644) + fmt.Fprint(writer, errstr) +} + +// TestingBuffer is an internal method only used for testing purposes +// that should never be used in production code. +// +// There is no public api guaranteed for this method. +func (f *Firehose) InternalTestingBuffer() *bytes.Buffer { + return f.testingBuffer +} + +// FIXME: Create a unit test that is going to fail as soon as any header is added in +func newBlockHeaderFromChainHeader(h *types.Header, td *pbeth.BigInt) *pbeth.BlockHeader { + var withdrawalsHashBytes []byte + if hash := h.WithdrawalsHash; hash != nil { + withdrawalsHashBytes = hash.Bytes() + } + + var parentBeaconRootBytes []byte + if root := h.ParentBeaconRoot; root != nil { + parentBeaconRootBytes = root.Bytes() + } + + pbHead := &pbeth.BlockHeader{ + Hash: h.Hash().Bytes(), + Number: h.Number.Uint64(), + ParentHash: h.ParentHash.Bytes(), + UncleHash: h.UncleHash.Bytes(), + Coinbase: h.Coinbase.Bytes(), + StateRoot: h.Root.Bytes(), + TransactionsRoot: h.TxHash.Bytes(), + ReceiptRoot: h.ReceiptHash.Bytes(), + LogsBloom: h.Bloom.Bytes(), + Difficulty: firehoseBigIntFromNative(h.Difficulty), + TotalDifficulty: td, + GasLimit: h.GasLimit, + GasUsed: h.GasUsed, + Timestamp: timestamppb.New(time.Unix(int64(h.Time), 0)), + ExtraData: h.Extra, + MixHash: h.MixDigest.Bytes(), + Nonce: h.Nonce.Uint64(), + BaseFeePerGas: firehoseBigIntFromNative(h.BaseFee), + WithdrawalsRoot: withdrawalsHashBytes, + BlobGasUsed: h.BlobGasUsed, + ExcessBlobGas: h.ExcessBlobGas, + ParentBeaconRoot: parentBeaconRootBytes, + + // Only set on Polygon fork(s) + TxDependency: nil, + } + + if pbHead.Difficulty == nil { + pbHead.Difficulty = &pbeth.BigInt{Bytes: []byte{0}} + } + + return pbHead +} + +// FIXME: Bring back Firehose test that ensures no new tx type are missed +func transactionTypeFromChainTxType(txType uint8) pbeth.TransactionTrace_Type { + switch txType { + case types.AccessListTxType: + return pbeth.TransactionTrace_TRX_TYPE_ACCESS_LIST + case types.DynamicFeeTxType: + return pbeth.TransactionTrace_TRX_TYPE_DYNAMIC_FEE + case types.LegacyTxType: + return pbeth.TransactionTrace_TRX_TYPE_LEGACY + case types.BlobTxType: + return pbeth.TransactionTrace_TRX_TYPE_BLOB + default: + panic(fmt.Errorf("unknown transaction type %d", txType)) + } +} + +func transactionStatusFromChainTxReceipt(txStatus uint64) pbeth.TransactionTraceStatus { + switch txStatus { + case types.ReceiptStatusSuccessful: + return pbeth.TransactionTraceStatus_SUCCEEDED + case types.ReceiptStatusFailed: + return pbeth.TransactionTraceStatus_FAILED + default: + panic(fmt.Errorf("unknown transaction status %d", txStatus)) + } +} + +func rootCallType(create bool) pbeth.CallType { + if create { + return pbeth.CallType_CREATE + } + + return pbeth.CallType_CALL +} + +func callTypeFromOpCode(typ vm.OpCode) pbeth.CallType { + switch typ { + case vm.CALL: + return pbeth.CallType_CALL + case vm.STATICCALL: + return pbeth.CallType_STATIC + case vm.DELEGATECALL: + return pbeth.CallType_DELEGATE + case vm.CREATE, vm.CREATE2: + return pbeth.CallType_CREATE + case vm.CALLCODE: + return pbeth.CallType_CALLCODE + } + + return pbeth.CallType_UNSPECIFIED +} + +func newTxReceiptFromChain(receipt *types.Receipt, txType pbeth.TransactionTrace_Type) (out *pbeth.TransactionReceipt) { + out = &pbeth.TransactionReceipt{ + StateRoot: receipt.PostState, + CumulativeGasUsed: receipt.CumulativeGasUsed, + LogsBloom: receipt.Bloom[:], + } + + if txType == pbeth.TransactionTrace_TRX_TYPE_BLOB { + out.BlobGasUsed = &receipt.BlobGasUsed + out.BlobGasPrice = firehoseBigIntFromNative(receipt.BlobGasPrice) + } + + if len(receipt.Logs) > 0 { + out.Logs = make([]*pbeth.Log, len(receipt.Logs)) + for i, log := range receipt.Logs { + out.Logs[i] = &pbeth.Log{ + Address: log.Address.Bytes(), + Topics: func() [][]byte { + if len(log.Topics) == 0 { + return nil + } + + out := make([][]byte, len(log.Topics)) + for i, topic := range log.Topics { + out[i] = topic.Bytes() + } + return out + }(), + Data: log.Data, + Index: uint32(i), + BlockIndex: uint32(log.Index), + + // Ordinal on transaction receipt logs is populated at the very end, so pairing + // between call logs and receipt logs is made + } + } + } + + return out +} + +func newAccessListFromChain(accessList types.AccessList) (out []*pbeth.AccessTuple) { + if len(accessList) == 0 { + return nil + } + + out = make([]*pbeth.AccessTuple, len(accessList)) + for i, tuple := range accessList { + out[i] = &pbeth.AccessTuple{ + Address: tuple.Address.Bytes(), + StorageKeys: func() [][]byte { + out := make([][]byte, len(tuple.StorageKeys)) + for i, key := range tuple.StorageKeys { + out[i] = key.Bytes() + } + return out + }(), + } + } + + return +} + +func newBlobHashesFromChain(blobHashes []common.Hash) (out [][]byte) { + if len(blobHashes) == 0 { + return nil + } + + out = make([][]byte, len(blobHashes)) + for i, blobHash := range blobHashes { + out[i] = blobHash.Bytes() + } + + 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, + tracing.BalanceIncreaseDaoContract: pbeth.BalanceChange_REASON_DAO_REFUND_CONTRACT, + tracing.BalanceDecreaseDaoAccount: pbeth.BalanceChange_REASON_DAO_ADJUST_BALANCE, + tracing.BalanceChangeTransfer: pbeth.BalanceChange_REASON_TRANSFER, + tracing.BalanceIncreaseGenesisBalance: pbeth.BalanceChange_REASON_GENESIS_BALANCE, + tracing.BalanceDecreaseGasBuy: pbeth.BalanceChange_REASON_GAS_BUY, + tracing.BalanceIncreaseRewardTransactionFee: pbeth.BalanceChange_REASON_REWARD_TRANSACTION_FEE, + tracing.BalanceIncreaseGasReturn: pbeth.BalanceChange_REASON_GAS_REFUND, + tracing.BalanceChangeTouchAccount: pbeth.BalanceChange_REASON_TOUCH_ACCOUNT, + tracing.BalanceIncreaseSelfdestruct: pbeth.BalanceChange_REASON_SUICIDE_REFUND, + tracing.BalanceDecreaseSelfdestruct: pbeth.BalanceChange_REASON_SUICIDE_WITHDRAW, + tracing.BalanceDecreaseSelfdestructBurn: pbeth.BalanceChange_REASON_BURN, + tracing.BalanceIncreaseWithdrawal: pbeth.BalanceChange_REASON_WITHDRAWAL, + + tracing.BalanceChangeUnspecified: pbeth.BalanceChange_REASON_UNKNOWN, +} + +func balanceChangeReasonFromChain(reason tracing.BalanceChangeReason) pbeth.BalanceChange_Reason { + if r, ok := balanceChangeReasonToPb[reason]; ok { + return r + } + + panic(fmt.Errorf("unknown tracer balance change reason value '%d', check state.BalanceChangeReason so see to which constant it refers to", reason)) +} + +var gasChangeReasonToPb = map[tracing.GasChangeReason]pbeth.GasChange_Reason{ + tracing.GasChangeTxInitialBalance: pbeth.GasChange_REASON_TX_INITIAL_BALANCE, + tracing.GasChangeTxRefunds: pbeth.GasChange_REASON_TX_REFUNDS, + tracing.GasChangeTxLeftOverReturned: pbeth.GasChange_REASON_TX_LEFT_OVER_RETURNED, + tracing.GasChangeCallInitialBalance: pbeth.GasChange_REASON_CALL_INITIAL_BALANCE, + tracing.GasChangeCallLeftOverReturned: pbeth.GasChange_REASON_CALL_LEFT_OVER_RETURNED, + tracing.GasChangeTxIntrinsicGas: pbeth.GasChange_REASON_INTRINSIC_GAS, + tracing.GasChangeCallContractCreation: pbeth.GasChange_REASON_CONTRACT_CREATION, + tracing.GasChangeCallContractCreation2: pbeth.GasChange_REASON_CONTRACT_CREATION2, + tracing.GasChangeCallCodeStorage: pbeth.GasChange_REASON_CODE_STORAGE, + tracing.GasChangeCallPrecompiledContract: pbeth.GasChange_REASON_PRECOMPILED_CONTRACT, + tracing.GasChangeCallStorageColdAccess: pbeth.GasChange_REASON_STATE_COLD_ACCESS, + tracing.GasChangeCallLeftOverRefunded: pbeth.GasChange_REASON_REFUND_AFTER_EXECUTION, + tracing.GasChangeCallFailedExecution: pbeth.GasChange_REASON_FAILED_EXECUTION, + + // Ignored, we track them manually, newGasChange ensure that we panic if we see Unknown + tracing.GasChangeCallOpCode: pbeth.GasChange_REASON_UNKNOWN, +} + +func gasChangeReasonFromChain(reason tracing.GasChangeReason) pbeth.GasChange_Reason { + if r, ok := gasChangeReasonToPb[reason]; ok { + if r == pbeth.GasChange_REASON_UNKNOWN { + panic(fmt.Errorf("tracer gas change reason value '%d' mapped to %s which is not accepted", reason, r)) + } + + return r + } + + panic(fmt.Errorf("unknown tracer gas change reason value '%d', check vm.GasChangeReason so see to which constant it refers to", reason)) +} + +func maxFeePerGas(tx *types.Transaction) *pbeth.BigInt { + switch tx.Type() { + case types.LegacyTxType, types.AccessListTxType: + return nil + + case types.DynamicFeeTxType, types.BlobTxType: + return firehoseBigIntFromNative(tx.GasFeeCap()) + + } + + panic(errUnhandledTransactionType("maxFeePerGas", tx.Type())) +} + +func maxPriorityFeePerGas(tx *types.Transaction) *pbeth.BigInt { + switch tx.Type() { + case types.LegacyTxType, types.AccessListTxType: + return nil + + case types.DynamicFeeTxType, types.BlobTxType: + return firehoseBigIntFromNative(tx.GasTipCap()) + } + + panic(errUnhandledTransactionType("maxPriorityFeePerGas", tx.Type())) +} + +func gasPrice(tx *types.Transaction, baseFee *big.Int) *pbeth.BigInt { + switch tx.Type() { + case types.LegacyTxType, types.AccessListTxType: + return firehoseBigIntFromNative(tx.GasPrice()) + + case types.DynamicFeeTxType, types.BlobTxType: + if baseFee == nil { + return firehoseBigIntFromNative(tx.GasPrice()) + } + + return firehoseBigIntFromNative(math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())) + } + + panic(errUnhandledTransactionType("gasPrice", tx.Type())) +} + +func FirehoseDebug(msg string, args ...interface{}) { + firehoseDebug(msg, args...) +} + +func firehoseInfo(msg string, args ...interface{}) { + if isFirehoseInfoEnabled { + fmt.Fprintf(os.Stderr, "[Firehose] "+msg+"\n", args...) + } +} + +func firehoseDebug(msg string, args ...interface{}) { + if isFirehoseDebugEnabled { + fmt.Fprintf(os.Stderr, "[Firehose] "+msg+"\n", args...) + } +} + +func firehoseTrace(msg string, args ...interface{}) { + if isFirehoseTracerEnabled { + fmt.Fprintf(os.Stderr, "[Firehose] "+msg+"\n", args...) + } +} + +// Ignore unused, we keep it around for debugging purposes +var _ = firehoseDebugPrintStack + +func firehoseDebugPrintStack() { + if isFirehoseDebugEnabled { + fmt.Fprintf(os.Stderr, "[Firehose] Stacktrace\n") + + // PrintStack prints to Stderr + debug.PrintStack() + } +} + +func errUnhandledTransactionType(tag string, value uint8) error { + return fmt.Errorf("unhandled transaction type's %d for firehose.%s(), carefully review the patch, if this new transaction type add new fields, think about adding them to Firehose Block format, when you see this message, it means something changed in the chain model and great care and thinking most be put here to properly understand the changes and the consequences they bring for the instrumentation", value, tag) +} + +type Ordinal struct { + value uint64 +} + +// Reset resets the ordinal to zero. +func (o *Ordinal) Reset() { + o.value = 0 +} + +// Next gives you the next sequential ordinal value that you should +// use to assign to your exeuction trace (block, transaction, call, etc). +func (o *Ordinal) Next() (out uint64) { + o.value++ + + return o.value +} + +type CallStack struct { + index uint32 + stack []*pbeth.Call + depth int +} + +func NewCallStack() *CallStack { + return &CallStack{} +} + +func (s *CallStack) Reset() { + s.index = 0 + s.stack = s.stack[:0] + s.depth = 0 +} + +func (s *CallStack) HasActiveCall() bool { + return len(s.stack) > 0 +} + +// Push a call onto the stack. The `Index` and `ParentIndex` of this call are +// assigned by this method which knowns how to find the parent call and deal with +// it. +func (s *CallStack) Push(call *pbeth.Call) { + s.index++ + call.Index = s.index + + call.Depth = uint32(s.depth) + s.depth++ + + // If a current call is active, it's the parent of this call + if parent := s.Peek(); parent != nil { + call.ParentIndex = parent.Index + } + + s.stack = append(s.stack, call) +} + +func (s *CallStack) ActiveIndex() uint32 { + if len(s.stack) == 0 { + return 0 + } + + return s.stack[len(s.stack)-1].Index +} + +func (s *CallStack) NextIndex() uint32 { + return s.index + 1 +} + +func (s *CallStack) Pop() (out *pbeth.Call) { + if len(s.stack) == 0 { + panic(fmt.Errorf("pop from empty call stack")) + } + + out = s.stack[len(s.stack)-1] + s.stack = s.stack[:len(s.stack)-1] + s.depth-- + + return +} + +// Peek returns the top of the stack without removing it, it's the +// activate call. +func (s *CallStack) Peek() *pbeth.Call { + if len(s.stack) == 0 { + return nil + } + + return s.stack[len(s.stack)-1] +} + +// DeferredCallState is a helper struct that can be used to accumulate call's state +// that is recorded before the Call has been started. This happens on the "starting" +// portion of the call/created. +type DeferredCallState struct { + accountCreations []*pbeth.AccountCreation + balanceChanges []*pbeth.BalanceChange + gasChanges []*pbeth.GasChange + nonceChanges []*pbeth.NonceChange +} + +func NewDeferredCallState() *DeferredCallState { + return &DeferredCallState{} +} + +func (d *DeferredCallState) MaybePopulateCallAndReset(source string, call *pbeth.Call) error { + if d.IsEmpty() { + return nil + } + + if source != "root" { + return fmt.Errorf("unexpected source for deferred call state, expected root but got %s, deferred call's state are always produced on the 'root' call", source) + } + + // We must happen because it's populated at beginning of the call as well as at the very end + call.AccountCreations = append(call.AccountCreations, d.accountCreations...) + call.BalanceChanges = append(call.BalanceChanges, d.balanceChanges...) + call.GasChanges = append(call.GasChanges, d.gasChanges...) + call.NonceChanges = append(call.NonceChanges, d.nonceChanges...) + + d.Reset() + + return nil +} + +func (d *DeferredCallState) IsEmpty() bool { + return len(d.accountCreations) == 0 && len(d.balanceChanges) == 0 && len(d.gasChanges) == 0 && len(d.nonceChanges) == 0 +} + +func (d *DeferredCallState) Reset() { + d.accountCreations = nil + d.balanceChanges = nil + d.gasChanges = nil + d.nonceChanges = nil +} + +func errorView(err error) _errorView { + return _errorView{err} +} + +type _errorView struct { + err error +} + +func (e _errorView) String() string { + if e.err == nil { + return "" + } + + return e.err.Error() +} + +type inputView []byte + +func (b inputView) String() string { + if len(b) == 0 { + return "" + } + + if len(b) < 4 { + return common.Bytes2Hex(b) + } + + method := b[:4] + rest := b[4:] + + if len(rest)%32 == 0 { + return fmt.Sprintf("%s (%d params)", common.Bytes2Hex(method), len(rest)/32) + } + + // Contract input starts with pre-defined chracters AFAIK, we could show them more nicely + + return fmt.Sprintf("%d bytes", len(rest)) +} + +type outputView []byte + +func (b outputView) String() string { + if len(b) == 0 { + return "" + } + + return fmt.Sprintf("%d bytes", len(b)) +} + +type receiptView types.Receipt + +func (r *receiptView) String() string { + if r == nil { + return "" + } + + status := "unknown" + switch r.Status { + case types.ReceiptStatusSuccessful: + status = "success" + case types.ReceiptStatusFailed: + status = "failed" + } + + return fmt.Sprintf("[status=%s, gasUsed=%d, logs=%d]", status, r.GasUsed, len(r.Logs)) +} + +func emptyBytesToNil(in []byte) []byte { + if len(in) == 0 { + return nil + } + + return in +} + +func normalizeSignaturePoint(value []byte) []byte { + if len(value) == 0 { + return nil + } + + if len(value) < 32 { + offset := 32 - len(value) + + out := make([]byte, 32) + copy(out[offset:32], value) + + return out + } + + return value[0:32] +} + +func firehoseBigIntFromNative(in *big.Int) *pbeth.BigInt { + if in == nil || in.Sign() == 0 { + return nil + } + + return &pbeth.BigInt{Bytes: in.Bytes()} +} + +type FinalityStatus struct { + LastIrreversibleBlockNumber uint64 + LastIrreversibleBlockHash []byte +} + +func (s *FinalityStatus) populate(finalNumber uint64, finalHash []byte) { + s.LastIrreversibleBlockNumber = finalNumber + s.LastIrreversibleBlockHash = finalHash +} + +func (s *FinalityStatus) populateFromChain(finalHeader *types.Header) { + if finalHeader == nil { + s.Reset() + return + } + + s.LastIrreversibleBlockNumber = finalHeader.Number.Uint64() + s.LastIrreversibleBlockHash = finalHeader.Hash().Bytes() +} + +func (s *FinalityStatus) Reset() { + s.LastIrreversibleBlockNumber = 0 + s.LastIrreversibleBlockHash = nil +} + +func (s *FinalityStatus) IsEmpty() bool { + return s.LastIrreversibleBlockNumber == 0 && len(s.LastIrreversibleBlockHash) == 0 +} + +var errFirehoseUnknownType = errors.New("firehose unknown tx type") +var sanitizeRegexp = regexp.MustCompile(`[\t( ){2,}]+`) + +func staticFirehoseChainValidationOnInit() { + firehoseKnownTxTypes := map[byte]bool{ + types.LegacyTxType: true, + types.AccessListTxType: true, + types.DynamicFeeTxType: true, + types.BlobTxType: true, + } + + for txType := byte(0); txType < 255; txType++ { + err := validateFirehoseKnownTransactionType(txType, firehoseKnownTxTypes[txType]) + if err != nil { + panic(fmt.Errorf(sanitizeRegexp.ReplaceAllString(` + If you see this panic message, it comes from a sanity check of Firehose instrumentation + around Ethereum transaction types. + + Over time, Ethereum added new transaction types but there is no easy way for Firehose to + report a compile time check that a new transaction's type must be handled. As such, we + have a runtime check at initialization of the process that encode/decode each possible + transaction's receipt and check proper handling. + + This panic means that a transaction that Firehose don't know about has most probably + been added and you must take **great care** to instrument it. One of the most important place + to look is in 'firehose.StartTransaction' where it should be properly handled. Think + carefully, read the EIP and ensure that any new "semantic" the transactions type's is + bringing is handled and instrumented (it might affect Block and other execution units also). + + For example, when London fork appeared, semantic of 'GasPrice' changed and it required + a different computation for 'GasPrice' when 'DynamicFeeTx' transaction were added. If you determined + it was indeed a new transaction's type, fix 'firehoseKnownTxTypes' variable above to include it + as a known Firehose type (after proper instrumentation of course). + + It's also possible the test itself is now flaky, we do 'receipt := types.Receipt{Type: }' + then 'buffer := receipt.EncodeRLP(...)' and then 'receipt.DecodeRLP(buffer)'. This should catch + new transaction types but could be now generate false positive. + + Received error: %w + `, " "), err)) + } + } +} + +func validateFirehoseKnownTransactionType(txType byte, isKnownFirehoseTxType bool) error { + writerBuffer := bytes.NewBuffer(nil) + + receipt := types.Receipt{Type: txType} + err := receipt.EncodeRLP(writerBuffer) + if err != nil { + if err == types.ErrTxTypeNotSupported { + if isKnownFirehoseTxType { + return fmt.Errorf("firehose known type but encoding RLP of receipt led to 'types.ErrTxTypeNotSupported'") + } + + // It's not a known type and encoding reported the same, so validation is OK + return nil + } + + // All other cases results in an error as we should have been able to encode it to RLP + return fmt.Errorf("encoding RLP: %w", err) + } + + readerBuffer := bytes.NewBuffer(writerBuffer.Bytes()) + err = receipt.DecodeRLP(rlp.NewStream(readerBuffer, 0)) + if err != nil { + if err == types.ErrTxTypeNotSupported { + if isKnownFirehoseTxType { + return fmt.Errorf("firehose known type but decoding of RLP of receipt led to 'types.ErrTxTypeNotSupported'") + } + + // It's not a known type and decoding reported the same, so validation is OK + return nil + } + + // All other cases results in an error as we should have been able to decode it from RLP + return fmt.Errorf("decoding RLP: %w", err) + } + + // If we reach here, encoding/decoding accepted the transaction's type, so let's ensure we expected the same + if !isKnownFirehoseTxType { + return fmt.Errorf("unknown tx type value %d: %w", txType, errFirehoseUnknownType) + } + + return nil +} + +type validationResult struct { + failures []string +} + +func (r *validationResult) panicOnAnyFailures(msg string, args ...any) { + if len(r.failures) > 0 { + panic(fmt.Errorf(fmt.Sprintf(msg, args...)+": validation failed:\n %s", strings.Join(r.failures, "\n"))) + } +} + +// We keep them around, planning in the future to use them (they existed in the previous Firehose patch) +var _, _, _, _ = validateAddressField, validateBigIntField, validateHashField, validateUint64Field + +func validateAddressField(into *validationResult, field string, a, b common.Address) { + validateField(into, field, a, b, a == b, common.Address.String) +} + +func validateBigIntField(into *validationResult, field string, a, b *big.Int) { + equal := false + if a == nil && b == nil { + equal = true + } else if a == nil || b == nil { + equal = false + } else { + equal = a.Cmp(b) == 0 + } + + validateField(into, field, a, b, equal, func(x *big.Int) string { + if x == nil { + return "" + } else { + return x.String() + } + }) +} + +func validateBytesField(into *validationResult, field string, a, b []byte) { + validateField(into, field, a, b, bytes.Equal(a, b), common.Bytes2Hex) +} + +func validateArrayOfBytesField(into *validationResult, field string, a, b [][]byte) { + if len(a) != len(b) { + into.failures = append(into.failures, fmt.Sprintf("%s [(actual element) %d != %d (expected element)]", field, len(a), len(b))) + return + } + + for i := range a { + validateBytesField(into, fmt.Sprintf("%s[%d]", field, i), a[i], b[i]) + } +} + +func validateHashField(into *validationResult, field string, a, b common.Hash) { + validateField(into, field, a, b, a == b, common.Hash.String) +} + +func validateUint32Field(into *validationResult, field string, a, b uint32) { + validateField(into, field, a, b, a == b, func(x uint32) string { return strconv.FormatUint(uint64(x), 10) }) +} + +func validateUint64Field(into *validationResult, field string, a, b uint64) { + validateField(into, field, a, b, a == b, func(x uint64) string { return strconv.FormatUint(x, 10) }) +} + +// validateField, pays the price for failure message construction only when field are not equal +func validateField[T any](into *validationResult, field string, a, b T, equal bool, toString func(x T) string) { + if !equal { + into.failures = append(into.failures, fmt.Sprintf("%s [(actual) %s %s %s (expected)]", field, toString(a), "!=", toString(b))) + } +} + +func ptr[T any](t T) *T { + return &t +} + +type Memory []byte + +func (m Memory) GetPtrUint256(offset, size *uint256.Int) []byte { + return m.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) +} + +func (m Memory) GetPtr(offset, size int64) []byte { + if size == 0 { + return nil + } + + if len(m) >= (int(offset) + int(size)) { + return m[offset : offset+size] + } + + // The EVM does memory expansion **after** notifying us about OnOpcode which we use + // to compute Keccak256 pre-images now. This creates problem when we want to retrieve + // the preimage data because the memory is not expanded yet but in the EVM is going to + // work because the memory is going to be expanded before the operation is actually + // executed so the memory will be of the correct size. + // + // In this situtation, we must pad with zeroes when the memory is not big enough. + reminder := m[offset:] + return append(reminder, make([]byte, int(size)-len(reminder))...) +} diff --git a/firehose/firehose_test.go b/firehose/firehose_test.go new file mode 100644 index 0000000000..8a9cc54bf2 --- /dev/null +++ b/firehose/firehose_test.go @@ -0,0 +1,493 @@ +package tracers + +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" + "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" + + pbeth "github.com/evmos/ethermint/firehose/pb/sf/ethereum/type/v2" +) + +func TestFirehoseCallStack_Push(t *testing.T) { + type actionRunner func(t *testing.T, s *CallStack) + + push := func(call *pbeth.Call) actionRunner { return func(_ *testing.T, s *CallStack) { s.Push(call) } } + pop := func() actionRunner { return func(_ *testing.T, s *CallStack) { s.Pop() } } + check := func(r actionRunner) actionRunner { return func(t *testing.T, s *CallStack) { r(t, s) } } + + tests := []struct { + name string + actions []actionRunner + }{ + { + "push/pop emtpy", []actionRunner{ + push(&pbeth.Call{}), + pop(), + check(func(t *testing.T, s *CallStack) { + require.Len(t, s.stack, 0) + }), + }, + }, + { + "push/push/push", []actionRunner{ + push(&pbeth.Call{}), + push(&pbeth.Call{}), + push(&pbeth.Call{}), + check(func(t *testing.T, s *CallStack) { + require.Len(t, s.stack, 3) + + require.Equal(t, 1, int(s.stack[0].Index)) + require.Equal(t, 0, int(s.stack[0].ParentIndex)) + + require.Equal(t, 2, int(s.stack[1].Index)) + require.Equal(t, 1, int(s.stack[1].ParentIndex)) + + require.Equal(t, 3, int(s.stack[2].Index)) + require.Equal(t, 2, int(s.stack[2].ParentIndex)) + }), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewCallStack() + + for _, action := range tt.actions { + action(t, s) + } + }) + } +} + +func Test_validateKnownTransactionTypes(t *testing.T) { + tests := []struct { + name string + txType byte + knownType bool + want error + }{ + {"legacy", 0, true, nil}, + {"access_list", 1, true, nil}, + {"inexistant", 255, false, nil}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateFirehoseKnownTransactionType(tt.txType, tt.knownType) + if tt.want == nil && err != nil { + t.Fatalf("Transaction of type %d expected to validate properly but received error %q", tt.txType, err) + } else if tt.want != nil && err == nil { + t.Fatalf("Transaction of type %d expected to validate improperly but generated no error", tt.txType) + } else if tt.want != nil && err != nil && tt.want.Error() != err.Error() { + t.Fatalf("Transaction of type %d expected to validate improperly but generated error %q does not match expected error %q", tt.txType, err, tt.want) + } + }) + } +} + +var ignorePbFieldNames = map[string]bool{ + "Hash": true, + "TotalDifficulty": true, + "state": true, + "unknownFields": true, + "sizeCache": true, + + // This was a Polygon specific field that existed for a while and has since been + // removed. It can be safely ignored in all protocols now. + "TxDependency": true, +} + +var pbFieldNameToGethMapping = map[string]string{ + "WithdrawalsRoot": "WithdrawalsHash", + "MixHash": "MixDigest", + "BaseFeePerGas": "BaseFee", + "StateRoot": "Root", + "ExtraData": "Extra", + "Timestamp": "Time", + "ReceiptRoot": "ReceiptHash", + "TransactionsRoot": "TxHash", + "LogsBloom": "Bloom", +} + +var ( + pbHeaderType = reflect.TypeFor[pbeth.BlockHeader]() + gethHeaderType = reflect.TypeFor[types.Header]() +) + +func Test_TypesHeader_AllConsensusFieldsAreKnown(t *testing.T) { + // This exact hash varies from protocol to protocol and also sometimes from one version to the other. + // When adding support for a new hard-fork that adds new block header fields, it's normal that this value + // changes. If you are sure the two struct are the same, then you can update the expected hash below + // to the new value. + expectedHash := common.HexToHash("5341947c531e5c9cf38202784b16ac66484fe1838aa6e825436b22321b927296") + + gethHeaderValue := reflect.New(gethHeaderType) + fillAllFieldsWithNonEmptyValues(t, gethHeaderValue, reflect.VisibleFields(gethHeaderType)) + gethHeader := gethHeaderValue.Interface().(*types.Header) + + // If you hit this assertion, it means that the fields `types.Header` of go-ethereum differs now + // versus last time this test was edited. + // + // It's important to understand that in Ethereum Block Header (e.g. `*types.Header`), the `Hash` is + // actually a computed value based on the other fields in the struct, so if you change any field, + // the hash will change also. + // + // On hard-fork, it happens that new fields are added, this test serves as a way to "detect" in codde + // that the expected fields of `types.Header` changed + require.Equal(t, expectedHash, gethHeader.Hash(), + "Geth Header Hash mistmatch, got %q but expecting %q on *types.Header:\n\nGeth Header (from fillNonDefault(new(*types.Header)))\n%s", + gethHeader.Hash().Hex(), + expectedHash, + asIndentedJSON(t, gethHeader), + ) +} + +func Test_FirehoseAndGethHeaderFieldMatches(t *testing.T) { + pbFields := filter(reflect.VisibleFields(pbHeaderType), func(f reflect.StructField) bool { + return !ignorePbFieldNames[f.Name] + }) + + gethFields := reflect.VisibleFields(gethHeaderType) + + pbFieldCount := len(pbFields) + gethFieldCount := len(gethFields) + + pbFieldNames := extractStructFieldNames(pbFields) + gethFieldNames := extractStructFieldNames(gethFields) + + // If you reach this assertion, it means that the fields count in the protobuf and go-ethereum are different. + // It is super important that you properly update the mapping from pbeth.BlockHeader to go-ethereum/core/types.Header + // that is done in `codecHeaderToGethHeader` function in `executor/provider_statedb.go`. + require.Equal( + t, + pbFieldCount, + gethFieldCount, + fieldsCountMistmatchMessage(t, pbFieldNames, gethFieldNames)) + + for pbFieldName := range pbFieldNames { + pbFieldRenamedName, found := pbFieldNameToGethMapping[pbFieldName] + if !found { + pbFieldRenamedName = pbFieldName + } + + assert.Contains(t, gethFieldNames, pbFieldRenamedName, "pbField.Name=%q (original %q) not found in gethFieldNames", pbFieldRenamedName, pbFieldName) + } +} + +func fillAllFieldsWithNonEmptyValues(t *testing.T, structValue reflect.Value, fields []reflect.StructField) { + t.Helper() + + for _, field := range fields { + fieldValue := structValue.Elem().FieldByName(field.Name) + require.True(t, fieldValue.IsValid(), "field %q not found", field.Name) + + switch fieldValue.Interface().(type) { + case []byte: + fieldValue.Set(reflect.ValueOf([]byte{1})) + case uint64: + fieldValue.Set(reflect.ValueOf(uint64(1))) + case *uint64: + var mockValue uint64 = 1 + fieldValue.Set(reflect.ValueOf(&mockValue)) + case *common.Hash: + var mockValue common.Hash = common.HexToHash("0x01") + fieldValue.Set(reflect.ValueOf(&mockValue)) + case common.Hash: + fieldValue.Set(reflect.ValueOf(common.HexToHash("0x01"))) + case common.Address: + fieldValue.Set(reflect.ValueOf(common.HexToAddress("0x01"))) + case types.Bloom: + fieldValue.Set(reflect.ValueOf(types.BytesToBloom([]byte{1}))) + case types.BlockNonce: + fieldValue.Set(reflect.ValueOf(types.EncodeNonce(1))) + case *big.Int: + fieldValue.Set(reflect.ValueOf(big.NewInt(1))) + case *pbeth.BigInt: + fieldValue.Set(reflect.ValueOf(&pbeth.BigInt{Bytes: []byte{1}})) + case *timestamppb.Timestamp: + fieldValue.Set(reflect.ValueOf(×tamppb.Timestamp{Seconds: 1})) + default: + // If you reach this panic in test, simply add a case above with a sane non-default + // value for the type in question. + t.Fatalf("unsupported type %T", fieldValue.Interface()) + } + } +} + +func fieldsCountMistmatchMessage(t *testing.T, pbFieldNames map[string]bool, gethFieldNames map[string]bool) string { + t.Helper() + + pbRemappedFieldNames := make(map[string]bool, len(pbFieldNames)) + for pbFieldName := range pbFieldNames { + pbFieldRenamedName, found := pbFieldNameToGethMapping[pbFieldName] + if !found { + pbFieldRenamedName = pbFieldName + } + + pbRemappedFieldNames[pbFieldRenamedName] = true + } + + return fmt.Sprintf( + "Field count mistmatch between `pbeth.BlockHeader` (has %d fields) and `*types.Header` (has %d fields)\n\n"+ + "Fields in `pbeth.Blockheader`:\n%s\n\n"+ + "Fields in `*types.Header`:\n%s\n\n"+ + "Missing in `pbeth.BlockHeader`:\n%s\n\n"+ + "Missing in `*types.Header`:\n%s", + len(pbRemappedFieldNames), + len(gethFieldNames), + asIndentedJSON(t, maps.Keys(pbRemappedFieldNames)), + asIndentedJSON(t, maps.Keys(gethFieldNames)), + asIndentedJSON(t, missingInSet(gethFieldNames, pbRemappedFieldNames)), + asIndentedJSON(t, missingInSet(pbRemappedFieldNames, gethFieldNames)), + ) +} + +func asIndentedJSON(t *testing.T, v any) string { + t.Helper() + out, err := json.MarshalIndent(v, "", " ") + require.NoError(t, err) + + return string(out) +} + +func missingInSet(a, b map[string]bool) []string { + missing := make([]string, 0) + for name := range a { + if !b[name] { + missing = append(missing, name) + } + } + + return missing +} + +func extractStructFieldNames(fields []reflect.StructField) map[string]bool { + result := make(map[string]bool, len(fields)) + for _, field := range fields { + result[field.Name] = true + } + return result +} + +func filter[S ~[]T, T any](s S, f func(T) bool) (out S) { + out = make(S, 0, len(s)/4) + for i, v := range s { + if f(v) { + out = append(out, s[i]) + } + } + + 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), + TD: b(1), + } +} + +func TestMemory_GetPtr(t *testing.T) { + type args struct { + offset int64 + size int64 + } + tests := []struct { + name string + m Memory + args args + want []byte + }{ + {"memory is just a bit too small", Memory([]byte{1, 2, 3}), args{0, 4}, []byte{1, 2, 3, 0}}, + {"memory is flushed with request", Memory([]byte{1, 2, 3, 4}), args{0, 4}, []byte{1, 2, 3, 4}}, + {"memory is just a bit too big", Memory([]byte{1, 2, 3, 4, 5}), args{0, 4}, []byte{1, 2, 3, 4}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, tt.m.GetPtr(tt.args.offset, tt.args.size)) + }) + } +} diff --git a/firehose/pb/sf/ethereum/type/v2/type.go b/firehose/pb/sf/ethereum/type/v2/type.go new file mode 100644 index 0000000000..38b6881fc1 --- /dev/null +++ b/firehose/pb/sf/ethereum/type/v2/type.go @@ -0,0 +1,27 @@ +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 +} diff --git a/firehose/pb/sf/ethereum/type/v2/type.pb.go b/firehose/pb/sf/ethereum/type/v2/type.pb.go new file mode 100644 index 0000000000..8103b2f182 --- /dev/null +++ b/firehose/pb/sf/ethereum/type/v2/type.pb.go @@ -0,0 +1,3691 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc (unknown) +// source: sf/ethereum/type/v2/type.proto + +package pbeth + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type TransactionTraceStatus int32 + +const ( + TransactionTraceStatus_UNKNOWN TransactionTraceStatus = 0 + TransactionTraceStatus_SUCCEEDED TransactionTraceStatus = 1 + TransactionTraceStatus_FAILED TransactionTraceStatus = 2 + TransactionTraceStatus_REVERTED TransactionTraceStatus = 3 +) + +// Enum value maps for TransactionTraceStatus. +var ( + TransactionTraceStatus_name = map[int32]string{ + 0: "UNKNOWN", + 1: "SUCCEEDED", + 2: "FAILED", + 3: "REVERTED", + } + TransactionTraceStatus_value = map[string]int32{ + "UNKNOWN": 0, + "SUCCEEDED": 1, + "FAILED": 2, + "REVERTED": 3, + } +) + +func (x TransactionTraceStatus) Enum() *TransactionTraceStatus { + p := new(TransactionTraceStatus) + *p = x + return p +} + +func (x TransactionTraceStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (TransactionTraceStatus) Descriptor() protoreflect.EnumDescriptor { + return file_sf_ethereum_type_v2_type_proto_enumTypes[0].Descriptor() +} + +func (TransactionTraceStatus) Type() protoreflect.EnumType { + return &file_sf_ethereum_type_v2_type_proto_enumTypes[0] +} + +func (x TransactionTraceStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use TransactionTraceStatus.Descriptor instead. +func (TransactionTraceStatus) EnumDescriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{0} +} + +type CallType int32 + +const ( + CallType_UNSPECIFIED CallType = 0 + CallType_CALL CallType = 1 // direct? what's the name for `Call` alone? + CallType_CALLCODE CallType = 2 + CallType_DELEGATE CallType = 3 + CallType_STATIC CallType = 4 + CallType_CREATE CallType = 5 // create2 ? any other form of calls? +) + +// Enum value maps for CallType. +var ( + CallType_name = map[int32]string{ + 0: "UNSPECIFIED", + 1: "CALL", + 2: "CALLCODE", + 3: "DELEGATE", + 4: "STATIC", + 5: "CREATE", + } + CallType_value = map[string]int32{ + "UNSPECIFIED": 0, + "CALL": 1, + "CALLCODE": 2, + "DELEGATE": 3, + "STATIC": 4, + "CREATE": 5, + } +) + +func (x CallType) Enum() *CallType { + p := new(CallType) + *p = x + return p +} + +func (x CallType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (CallType) Descriptor() protoreflect.EnumDescriptor { + return file_sf_ethereum_type_v2_type_proto_enumTypes[1].Descriptor() +} + +func (CallType) Type() protoreflect.EnumType { + return &file_sf_ethereum_type_v2_type_proto_enumTypes[1] +} + +func (x CallType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use CallType.Descriptor instead. +func (CallType) EnumDescriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{1} +} + +type Block_DetailLevel int32 + +const ( + Block_DETAILLEVEL_EXTENDED Block_DetailLevel = 0 + // DETAILLEVEL_TRACE = 1; // TBD + Block_DETAILLEVEL_BASE Block_DetailLevel = 2 +) + +// Enum value maps for Block_DetailLevel. +var ( + Block_DetailLevel_name = map[int32]string{ + 0: "DETAILLEVEL_EXTENDED", + 2: "DETAILLEVEL_BASE", + } + Block_DetailLevel_value = map[string]int32{ + "DETAILLEVEL_EXTENDED": 0, + "DETAILLEVEL_BASE": 2, + } +) + +func (x Block_DetailLevel) Enum() *Block_DetailLevel { + p := new(Block_DetailLevel) + *p = x + return p +} + +func (x Block_DetailLevel) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Block_DetailLevel) Descriptor() protoreflect.EnumDescriptor { + return file_sf_ethereum_type_v2_type_proto_enumTypes[2].Descriptor() +} + +func (Block_DetailLevel) Type() protoreflect.EnumType { + return &file_sf_ethereum_type_v2_type_proto_enumTypes[2] +} + +func (x Block_DetailLevel) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Block_DetailLevel.Descriptor instead. +func (Block_DetailLevel) EnumDescriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{0, 0} +} + +type TransactionTrace_Type int32 + +const ( + // All transactions that ever existed prior Berlin fork before EIP-2718 was implemented. + TransactionTrace_TRX_TYPE_LEGACY TransactionTrace_Type = 0 + // Transaction that specicy an access list of contract/storage_keys that is going to be used + // in this transaction. + // + // Added in Berlin fork (EIP-2930). + TransactionTrace_TRX_TYPE_ACCESS_LIST TransactionTrace_Type = 1 + // Transaction that specifis an access list just like TRX_TYPE_ACCESS_LIST but in addition defines the + // max base gas gee and max priority gas fee to pay for this transaction. Transaction's of those type are + // executed against EIP-1559 rules which dictates a dynamic gas cost based on the congestion of the network. + TransactionTrace_TRX_TYPE_DYNAMIC_FEE TransactionTrace_Type = 2 + // Transaction which contain a large amount of data that cannot be accessed by EVM execution, but whose commitment + // can be accessed. The format is intended to be fully compatible with the format that will be used in full sharding. + // + // Transaction that defines specifis an access list just like TRX_TYPE_ACCESS_LIST and enables dynamic fee just like + // TRX_TYPE_DYNAMIC_FEE but in addition defines the fields 'max_fee_per_data_gas' of type 'uint256' and the fields + // 'blob_versioned_hashes' field represents a list of hash outputs from 'kzg_to_versioned_hash'. + // + // Activated in Dencun + TransactionTrace_TRX_TYPE_BLOB TransactionTrace_Type = 3 + // Arbitrum-specific transactions + TransactionTrace_TRX_TYPE_ARBITRUM_DEPOSIT TransactionTrace_Type = 100 + TransactionTrace_TRX_TYPE_ARBITRUM_UNSIGNED TransactionTrace_Type = 101 + TransactionTrace_TRX_TYPE_ARBITRUM_CONTRACT TransactionTrace_Type = 102 + TransactionTrace_TRX_TYPE_ARBITRUM_RETRY TransactionTrace_Type = 104 + TransactionTrace_TRX_TYPE_ARBITRUM_SUBMIT_RETRYABLE TransactionTrace_Type = 105 + TransactionTrace_TRX_TYPE_ARBITRUM_INTERNAL TransactionTrace_Type = 106 + TransactionTrace_TRX_TYPE_ARBITRUM_LEGACY TransactionTrace_Type = 120 +) + +// Enum value maps for TransactionTrace_Type. +var ( + TransactionTrace_Type_name = map[int32]string{ + 0: "TRX_TYPE_LEGACY", + 1: "TRX_TYPE_ACCESS_LIST", + 2: "TRX_TYPE_DYNAMIC_FEE", + 3: "TRX_TYPE_BLOB", + 100: "TRX_TYPE_ARBITRUM_DEPOSIT", + 101: "TRX_TYPE_ARBITRUM_UNSIGNED", + 102: "TRX_TYPE_ARBITRUM_CONTRACT", + 104: "TRX_TYPE_ARBITRUM_RETRY", + 105: "TRX_TYPE_ARBITRUM_SUBMIT_RETRYABLE", + 106: "TRX_TYPE_ARBITRUM_INTERNAL", + 120: "TRX_TYPE_ARBITRUM_LEGACY", + } + TransactionTrace_Type_value = map[string]int32{ + "TRX_TYPE_LEGACY": 0, + "TRX_TYPE_ACCESS_LIST": 1, + "TRX_TYPE_DYNAMIC_FEE": 2, + "TRX_TYPE_BLOB": 3, + "TRX_TYPE_ARBITRUM_DEPOSIT": 100, + "TRX_TYPE_ARBITRUM_UNSIGNED": 101, + "TRX_TYPE_ARBITRUM_CONTRACT": 102, + "TRX_TYPE_ARBITRUM_RETRY": 104, + "TRX_TYPE_ARBITRUM_SUBMIT_RETRYABLE": 105, + "TRX_TYPE_ARBITRUM_INTERNAL": 106, + "TRX_TYPE_ARBITRUM_LEGACY": 120, + } +) + +func (x TransactionTrace_Type) Enum() *TransactionTrace_Type { + p := new(TransactionTrace_Type) + *p = x + return p +} + +func (x TransactionTrace_Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (TransactionTrace_Type) Descriptor() protoreflect.EnumDescriptor { + return file_sf_ethereum_type_v2_type_proto_enumTypes[3].Descriptor() +} + +func (TransactionTrace_Type) Type() protoreflect.EnumType { + return &file_sf_ethereum_type_v2_type_proto_enumTypes[3] +} + +func (x TransactionTrace_Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use TransactionTrace_Type.Descriptor instead. +func (TransactionTrace_Type) EnumDescriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{5, 0} +} + +// Obtain all balanche change reasons under deep mind repository: +// +// ```shell +// ack -ho 'BalanceChangeReason\(".*"\)' | grep -Eo '".*"' | sort | uniq +// ``` +type BalanceChange_Reason int32 + +const ( + BalanceChange_REASON_UNKNOWN BalanceChange_Reason = 0 + BalanceChange_REASON_REWARD_MINE_UNCLE BalanceChange_Reason = 1 + BalanceChange_REASON_REWARD_MINE_BLOCK BalanceChange_Reason = 2 + BalanceChange_REASON_DAO_REFUND_CONTRACT BalanceChange_Reason = 3 + BalanceChange_REASON_DAO_ADJUST_BALANCE BalanceChange_Reason = 4 + BalanceChange_REASON_TRANSFER BalanceChange_Reason = 5 + BalanceChange_REASON_GENESIS_BALANCE BalanceChange_Reason = 6 + BalanceChange_REASON_GAS_BUY BalanceChange_Reason = 7 + BalanceChange_REASON_REWARD_TRANSACTION_FEE BalanceChange_Reason = 8 + BalanceChange_REASON_REWARD_FEE_RESET BalanceChange_Reason = 14 + BalanceChange_REASON_GAS_REFUND BalanceChange_Reason = 9 + BalanceChange_REASON_TOUCH_ACCOUNT BalanceChange_Reason = 10 + BalanceChange_REASON_SUICIDE_REFUND BalanceChange_Reason = 11 + BalanceChange_REASON_SUICIDE_WITHDRAW BalanceChange_Reason = 13 + BalanceChange_REASON_CALL_BALANCE_OVERRIDE BalanceChange_Reason = 12 + // Used on chain(s) where some Ether burning happens + BalanceChange_REASON_BURN BalanceChange_Reason = 15 + BalanceChange_REASON_WITHDRAWAL BalanceChange_Reason = 16 +) + +// Enum value maps for BalanceChange_Reason. +var ( + BalanceChange_Reason_name = map[int32]string{ + 0: "REASON_UNKNOWN", + 1: "REASON_REWARD_MINE_UNCLE", + 2: "REASON_REWARD_MINE_BLOCK", + 3: "REASON_DAO_REFUND_CONTRACT", + 4: "REASON_DAO_ADJUST_BALANCE", + 5: "REASON_TRANSFER", + 6: "REASON_GENESIS_BALANCE", + 7: "REASON_GAS_BUY", + 8: "REASON_REWARD_TRANSACTION_FEE", + 14: "REASON_REWARD_FEE_RESET", + 9: "REASON_GAS_REFUND", + 10: "REASON_TOUCH_ACCOUNT", + 11: "REASON_SUICIDE_REFUND", + 13: "REASON_SUICIDE_WITHDRAW", + 12: "REASON_CALL_BALANCE_OVERRIDE", + 15: "REASON_BURN", + 16: "REASON_WITHDRAWAL", + } + BalanceChange_Reason_value = map[string]int32{ + "REASON_UNKNOWN": 0, + "REASON_REWARD_MINE_UNCLE": 1, + "REASON_REWARD_MINE_BLOCK": 2, + "REASON_DAO_REFUND_CONTRACT": 3, + "REASON_DAO_ADJUST_BALANCE": 4, + "REASON_TRANSFER": 5, + "REASON_GENESIS_BALANCE": 6, + "REASON_GAS_BUY": 7, + "REASON_REWARD_TRANSACTION_FEE": 8, + "REASON_REWARD_FEE_RESET": 14, + "REASON_GAS_REFUND": 9, + "REASON_TOUCH_ACCOUNT": 10, + "REASON_SUICIDE_REFUND": 11, + "REASON_SUICIDE_WITHDRAW": 13, + "REASON_CALL_BALANCE_OVERRIDE": 12, + "REASON_BURN": 15, + "REASON_WITHDRAWAL": 16, + } +) + +func (x BalanceChange_Reason) Enum() *BalanceChange_Reason { + p := new(BalanceChange_Reason) + *p = x + return p +} + +func (x BalanceChange_Reason) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (BalanceChange_Reason) Descriptor() protoreflect.EnumDescriptor { + return file_sf_ethereum_type_v2_type_proto_enumTypes[4].Descriptor() +} + +func (BalanceChange_Reason) Type() protoreflect.EnumType { + return &file_sf_ethereum_type_v2_type_proto_enumTypes[4] +} + +func (x BalanceChange_Reason) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use BalanceChange_Reason.Descriptor instead. +func (BalanceChange_Reason) EnumDescriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{11, 0} +} + +// Obtain all gas change reasons under deep mind repository: +// +// ```shell +// ack -ho 'GasChangeReason\(".*"\)' | grep -Eo '".*"' | sort | uniq +// ``` +type GasChange_Reason int32 + +const ( + GasChange_REASON_UNKNOWN GasChange_Reason = 0 + // REASON_CALL is the amount of gas that will be charged for a 'CALL' opcode executed by the EVM + GasChange_REASON_CALL GasChange_Reason = 1 + // REASON_CALL_CODE is the amount of gas that will be charged for a 'CALLCODE' opcode executed by the EVM + GasChange_REASON_CALL_CODE GasChange_Reason = 2 + // REASON_CALL_DATA_COPY is the amount of gas that will be charged for a 'CALLDATACOPY' opcode executed by the EVM + GasChange_REASON_CALL_DATA_COPY GasChange_Reason = 3 + // REASON_CODE_COPY is the amount of gas that will be charged for a 'CALLDATACOPY' opcode executed by the EVM + GasChange_REASON_CODE_COPY GasChange_Reason = 4 + // REASON_CODE_STORAGE is the amount of gas that will be charged for code storage + GasChange_REASON_CODE_STORAGE GasChange_Reason = 5 + // REASON_CONTRACT_CREATION is the amount of gas that will be charged for a 'CREATE' opcode executed by the EVM and for the gas + // burned for a CREATE, today controlled by EIP150 rules + GasChange_REASON_CONTRACT_CREATION GasChange_Reason = 6 + // REASON_CONTRACT_CREATION2 is the amount of gas that will be charged for a 'CREATE2' opcode executed by the EVM and for the gas + // burned for a CREATE2, today controlled by EIP150 rules + GasChange_REASON_CONTRACT_CREATION2 GasChange_Reason = 7 + // REASON_DELEGATE_CALL is the amount of gas that will be charged for a 'DELEGATECALL' opcode executed by the EVM + GasChange_REASON_DELEGATE_CALL GasChange_Reason = 8 + // REASON_EVENT_LOG is the amount of gas that will be charged for a 'LOG' opcode executed by the EVM + GasChange_REASON_EVENT_LOG GasChange_Reason = 9 + // REASON_EXT_CODE_COPY is the amount of gas that will be charged for a 'LOG' opcode executed by the EVM + GasChange_REASON_EXT_CODE_COPY GasChange_Reason = 10 + // REASON_FAILED_EXECUTION is the burning of the remaining gas when the execution failed without a revert + GasChange_REASON_FAILED_EXECUTION GasChange_Reason = 11 + // REASON_INTRINSIC_GAS is the amount of gas that will be charged for the intrinsic cost of the transaction, there is + // always exactly one of those per transaction + GasChange_REASON_INTRINSIC_GAS GasChange_Reason = 12 + // GasChangePrecompiledContract is the amount of gas that will be charged for a precompiled contract execution + GasChange_REASON_PRECOMPILED_CONTRACT GasChange_Reason = 13 + // REASON_REFUND_AFTER_EXECUTION is the amount of gas that will be refunded to the caller after the execution of the call, + // if there is left over at the end of execution + GasChange_REASON_REFUND_AFTER_EXECUTION GasChange_Reason = 14 + // REASON_RETURN is the amount of gas that will be charged for a 'RETURN' opcode executed by the EVM + GasChange_REASON_RETURN GasChange_Reason = 15 + // REASON_RETURN_DATA_COPY is the amount of gas that will be charged for a 'RETURNDATACOPY' opcode executed by the EVM + GasChange_REASON_RETURN_DATA_COPY GasChange_Reason = 16 + // REASON_REVERT is the amount of gas that will be charged for a 'REVERT' opcode executed by the EVM + GasChange_REASON_REVERT GasChange_Reason = 17 + // REASON_SELF_DESTRUCT is the amount of gas that will be charged for a 'SELFDESTRUCT' opcode executed by the EVM + GasChange_REASON_SELF_DESTRUCT GasChange_Reason = 18 + // REASON_STATIC_CALL is the amount of gas that will be charged for a 'STATICALL' opcode executed by the EVM + GasChange_REASON_STATIC_CALL GasChange_Reason = 19 + // REASON_STATE_COLD_ACCESS is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules + // + // Added in Berlin fork (Geth 1.10+) + GasChange_REASON_STATE_COLD_ACCESS GasChange_Reason = 20 + // REASON_TX_INITIAL_BALANCE is the initial balance for the call which will be equal to the gasLimit of the call + // + // Added as new tracing reason in Geth, available only on some chains + GasChange_REASON_TX_INITIAL_BALANCE GasChange_Reason = 21 + // REASON_TX_REFUNDS is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared) + // this generates an increase in gas. There is only one such gas change per transaction. + // + // Added as new tracing reason in Geth, available only on some chains + GasChange_REASON_TX_REFUNDS GasChange_Reason = 22 + // REASON_TX_LEFT_OVER_RETURNED is the amount of gas left over at the end of transaction's execution that will be returned + // to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas + // left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller. + // There is at most one of such gas change per transaction. + // + // Added as new tracing reason in Geth, available only on some chains + GasChange_REASON_TX_LEFT_OVER_RETURNED GasChange_Reason = 23 + // REASON_CALL_INITIAL_BALANCE is the initial balance for the call which will be equal to the gasLimit of the call. There is only + // one such gas change per call. + // + // Added as new tracing reason in Geth, available only on some chains + GasChange_REASON_CALL_INITIAL_BALANCE GasChange_Reason = 24 + // REASON_CALL_LEFT_OVER_RETURNED is the amount of gas left over that will be returned to the caller, this change will always + // be a negative change as we "drain" left over gas towards 0. If there was no gas left at the end of execution, no such even + // will be emitted. + GasChange_REASON_CALL_LEFT_OVER_RETURNED GasChange_Reason = 25 +) + +// Enum value maps for GasChange_Reason. +var ( + GasChange_Reason_name = map[int32]string{ + 0: "REASON_UNKNOWN", + 1: "REASON_CALL", + 2: "REASON_CALL_CODE", + 3: "REASON_CALL_DATA_COPY", + 4: "REASON_CODE_COPY", + 5: "REASON_CODE_STORAGE", + 6: "REASON_CONTRACT_CREATION", + 7: "REASON_CONTRACT_CREATION2", + 8: "REASON_DELEGATE_CALL", + 9: "REASON_EVENT_LOG", + 10: "REASON_EXT_CODE_COPY", + 11: "REASON_FAILED_EXECUTION", + 12: "REASON_INTRINSIC_GAS", + 13: "REASON_PRECOMPILED_CONTRACT", + 14: "REASON_REFUND_AFTER_EXECUTION", + 15: "REASON_RETURN", + 16: "REASON_RETURN_DATA_COPY", + 17: "REASON_REVERT", + 18: "REASON_SELF_DESTRUCT", + 19: "REASON_STATIC_CALL", + 20: "REASON_STATE_COLD_ACCESS", + 21: "REASON_TX_INITIAL_BALANCE", + 22: "REASON_TX_REFUNDS", + 23: "REASON_TX_LEFT_OVER_RETURNED", + 24: "REASON_CALL_INITIAL_BALANCE", + 25: "REASON_CALL_LEFT_OVER_RETURNED", + } + GasChange_Reason_value = map[string]int32{ + "REASON_UNKNOWN": 0, + "REASON_CALL": 1, + "REASON_CALL_CODE": 2, + "REASON_CALL_DATA_COPY": 3, + "REASON_CODE_COPY": 4, + "REASON_CODE_STORAGE": 5, + "REASON_CONTRACT_CREATION": 6, + "REASON_CONTRACT_CREATION2": 7, + "REASON_DELEGATE_CALL": 8, + "REASON_EVENT_LOG": 9, + "REASON_EXT_CODE_COPY": 10, + "REASON_FAILED_EXECUTION": 11, + "REASON_INTRINSIC_GAS": 12, + "REASON_PRECOMPILED_CONTRACT": 13, + "REASON_REFUND_AFTER_EXECUTION": 14, + "REASON_RETURN": 15, + "REASON_RETURN_DATA_COPY": 16, + "REASON_REVERT": 17, + "REASON_SELF_DESTRUCT": 18, + "REASON_STATIC_CALL": 19, + "REASON_STATE_COLD_ACCESS": 20, + "REASON_TX_INITIAL_BALANCE": 21, + "REASON_TX_REFUNDS": 22, + "REASON_TX_LEFT_OVER_RETURNED": 23, + "REASON_CALL_INITIAL_BALANCE": 24, + "REASON_CALL_LEFT_OVER_RETURNED": 25, + } +) + +func (x GasChange_Reason) Enum() *GasChange_Reason { + p := new(GasChange_Reason) + *p = x + return p +} + +func (x GasChange_Reason) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (GasChange_Reason) Descriptor() protoreflect.EnumDescriptor { + return file_sf_ethereum_type_v2_type_proto_enumTypes[5].Descriptor() +} + +func (GasChange_Reason) Type() protoreflect.EnumType { + return &file_sf_ethereum_type_v2_type_proto_enumTypes[5] +} + +func (x GasChange_Reason) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use GasChange_Reason.Descriptor instead. +func (GasChange_Reason) EnumDescriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{15, 0} +} + +type Block struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Hash is the block's hash. + Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"` + // Number is the block's height at which this block was mined. + Number uint64 `protobuf:"varint,3,opt,name=number,proto3" json:"number,omitempty"` + // Size is the size in bytes of the RLP encoding of the block according to Ethereum + // rules. + Size uint64 `protobuf:"varint,4,opt,name=size,proto3" json:"size,omitempty"` + // Header contain's the block's header information like its parent hash, the merkel root hash + // and all other information the form a block. + Header *BlockHeader `protobuf:"bytes,5,opt,name=header,proto3" json:"header,omitempty"` + // Uncles represents block produced with a valid solution but were not actually choosen + // as the canonical block for the given height so they are mostly "forked" blocks. + // + // If the Block has been produced using the Proof of Stake consensus algorithm, this + // field will actually be always empty. + Uncles []*BlockHeader `protobuf:"bytes,6,rep,name=uncles,proto3" json:"uncles,omitempty"` + // TransactionTraces hold the execute trace of all the transactions that were executed + // in this block. In in there that you will find most of the Ethereum data model. + // + // They are ordered by the order of execution of the transaction in the block. + TransactionTraces []*TransactionTrace `protobuf:"bytes,10,rep,name=transaction_traces,json=transactionTraces,proto3" json:"transaction_traces,omitempty"` + // BalanceChanges here is the array of ETH transfer that happened at the block level + // outside of the normal transaction flow of a block. The best example of this is mining + // reward for the block mined, the transfer of ETH to the miner happens outside the normal + // transaction flow of the chain and is recorded as a `BalanceChange` here since we cannot + // attached it to any transaction. + // + // Only available in DetailLevel: EXTENDED + BalanceChanges []*BalanceChange `protobuf:"bytes,11,rep,name=balance_changes,json=balanceChanges,proto3" json:"balance_changes,omitempty"` + // DetailLevel affects the data available in this block. + // + // EXTENDED describes the most complete block, with traces, balance changes, storage changes. It is extracted during the execution of the block. + // BASE describes a block that contains only the block header, transaction receipts and event logs: everything that can be extracted using the base JSON-RPC interface (https://ethereum.org/en/developers/docs/apis/json-rpc/#json-rpc-methods) + // + // Furthermore, the eth_getTransactionReceipt call has been avoided because it brings only minimal improvements at the cost of requiring an archive node or a full node with complete transaction index. + DetailLevel Block_DetailLevel `protobuf:"varint,12,opt,name=detail_level,json=detailLevel,proto3,enum=sf.ethereum.type.v2.Block_DetailLevel" json:"detail_level,omitempty"` + // CodeChanges here is the array of smart code change that happened that happened at the block level + // outside of the normal transaction flow of a block. Some Ethereum's fork like BSC and Polygon + // has some capabilities to upgrade internal smart contracts used usually to track the validator + // list. + // + // On hard fork, some procedure runs to upgrade the smart contract code to a new version. In those + // network, a `CodeChange` for each modified smart contract on upgrade would be present here. Note + // that this happen rarely, so the vast majority of block will have an empty list here. + // Only available in DetailLevel: EXTENDED + CodeChanges []*CodeChange `protobuf:"bytes,20,rep,name=code_changes,json=codeChanges,proto3" json:"code_changes,omitempty"` + // System calls are introduced in Cancun, along with blobs. They are executed outside of transactions but affect the state. + // Only available in DetailLevel: EXTENDED + SystemCalls []*Call `protobuf:"bytes,21,rep,name=system_calls,json=systemCalls,proto3" json:"system_calls,omitempty"` + // Ver represents that data model version of the block, it is used internally by Firehose on Ethereum + // as a validation that we are reading the correct version. + Ver int32 `protobuf:"varint,1,opt,name=ver,proto3" json:"ver,omitempty"` +} + +func (x *Block) Reset() { + *x = Block{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Block) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Block) ProtoMessage() {} + +func (x *Block) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Block.ProtoReflect.Descriptor instead. +func (*Block) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{0} +} + +func (x *Block) GetHash() []byte { + if x != nil { + return x.Hash + } + return nil +} + +func (x *Block) GetNumber() uint64 { + if x != nil { + return x.Number + } + return 0 +} + +func (x *Block) GetSize() uint64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *Block) GetHeader() *BlockHeader { + if x != nil { + return x.Header + } + return nil +} + +func (x *Block) GetUncles() []*BlockHeader { + if x != nil { + return x.Uncles + } + return nil +} + +func (x *Block) GetTransactionTraces() []*TransactionTrace { + if x != nil { + return x.TransactionTraces + } + return nil +} + +func (x *Block) GetBalanceChanges() []*BalanceChange { + if x != nil { + return x.BalanceChanges + } + return nil +} + +func (x *Block) GetDetailLevel() Block_DetailLevel { + if x != nil { + return x.DetailLevel + } + return Block_DETAILLEVEL_EXTENDED +} + +func (x *Block) GetCodeChanges() []*CodeChange { + if x != nil { + return x.CodeChanges + } + return nil +} + +func (x *Block) GetSystemCalls() []*Call { + if x != nil { + return x.SystemCalls + } + return nil +} + +func (x *Block) GetVer() int32 { + if x != nil { + return x.Ver + } + return 0 +} + +type BlockHeader struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ParentHash []byte `protobuf:"bytes,1,opt,name=parent_hash,json=parentHash,proto3" json:"parent_hash,omitempty"` + // Uncle hash of the block, some reference it as `sha3Uncles`, but `sha3“ is badly worded, so we prefer `uncle_hash`, also + // referred as `ommers` in EIP specification. + // + // If the Block containing this `BlockHeader` has been produced using the Proof of Stake + // consensus algorithm, this field will actually be constant and set to `0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347`. + UncleHash []byte `protobuf:"bytes,2,opt,name=uncle_hash,json=uncleHash,proto3" json:"uncle_hash,omitempty"` + Coinbase []byte `protobuf:"bytes,3,opt,name=coinbase,proto3" json:"coinbase,omitempty"` + StateRoot []byte `protobuf:"bytes,4,opt,name=state_root,json=stateRoot,proto3" json:"state_root,omitempty"` + TransactionsRoot []byte `protobuf:"bytes,5,opt,name=transactions_root,json=transactionsRoot,proto3" json:"transactions_root,omitempty"` + ReceiptRoot []byte `protobuf:"bytes,6,opt,name=receipt_root,json=receiptRoot,proto3" json:"receipt_root,omitempty"` + LogsBloom []byte `protobuf:"bytes,7,opt,name=logs_bloom,json=logsBloom,proto3" json:"logs_bloom,omitempty"` + // Difficulty is the difficulty of the Proof of Work algorithm that was required to compute a solution. + // + // If the Block containing this `BlockHeader` has been produced using the Proof of Stake + // consensus algorithm, this field will actually be constant and set to `0x00`. + Difficulty *BigInt `protobuf:"bytes,8,opt,name=difficulty,proto3" json:"difficulty,omitempty"` + // TotalDifficulty is the sum of all previous blocks difficulty including this block difficulty. + // + // If the Block containing this `BlockHeader` has been produced using the Proof of Stake + // consensus algorithm, this field will actually be constant and set to the terminal total difficulty + // that was required to transition to Proof of Stake algorithm, which varies per network. It is set to + // 58 750 000 000 000 000 000 000 on Ethereum Mainnet and to 10 790 000 on Ethereum Testnet Goerli. + TotalDifficulty *BigInt `protobuf:"bytes,17,opt,name=total_difficulty,json=totalDifficulty,proto3" json:"total_difficulty,omitempty"` + Number uint64 `protobuf:"varint,9,opt,name=number,proto3" json:"number,omitempty"` + GasLimit uint64 `protobuf:"varint,10,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty"` + GasUsed uint64 `protobuf:"varint,11,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` + Timestamp *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // ExtraData is free-form bytes included in the block by the "miner". While on Yellow paper of + // Ethereum this value is maxed to 32 bytes, other consensus algorithm like Clique and some other + // forks are using bigger values to carry special consensus data. + // + // If the Block containing this `BlockHeader` has been produced using the Proof of Stake + // consensus algorithm, this field is strictly enforced to be <= 32 bytes. + ExtraData []byte `protobuf:"bytes,13,opt,name=extra_data,json=extraData,proto3" json:"extra_data,omitempty"` + // MixHash is used to prove, when combined with the `nonce` that sufficient amount of computation has been + // achieved and that the solution found is valid. + MixHash []byte `protobuf:"bytes,14,opt,name=mix_hash,json=mixHash,proto3" json:"mix_hash,omitempty"` + // Nonce is used to prove, when combined with the `mix_hash` that sufficient amount of computation has been + // achieved and that the solution found is valid. + // + // If the Block containing this `BlockHeader` has been produced using the Proof of Stake + // consensus algorithm, this field will actually be constant and set to `0`. + Nonce uint64 `protobuf:"varint,15,opt,name=nonce,proto3" json:"nonce,omitempty"` + // Hash is the hash of the block which is actually the computation: + // + // Keccak256(rlp([ + // parent_hash, + // uncle_hash, + // coinbase, + // state_root, + // transactions_root, + // receipt_root, + // logs_bloom, + // difficulty, + // number, + // gas_limit, + // gas_used, + // timestamp, + // extra_data, + // mix_hash, + // nonce, + // base_fee_per_gas (to be included only if London fork is active) + // withdrawals_root (to be included only if Shangai fork is active) + // blob_gas_used (to be included only if Cancun fork is active) + // excess_blob_gas (to be included only if Cancun fork is active) + // parent_beacon_root (to be included only if Cancun fork is active) + // ])) + Hash []byte `protobuf:"bytes,16,opt,name=hash,proto3" json:"hash,omitempty"` + // Base fee per gas according to EIP-1559 (e.g. London Fork) rules, only set if London is present/active on the chain. + BaseFeePerGas *BigInt `protobuf:"bytes,18,opt,name=base_fee_per_gas,json=baseFeePerGas,proto3" json:"base_fee_per_gas,omitempty"` + // Withdrawals root hash according to EIP-4895 (e.g. Shangai Fork) rules, only set if Shangai is present/active on the chain. + // + // Only available in DetailLevel: EXTENDED + WithdrawalsRoot []byte `protobuf:"bytes,19,opt,name=withdrawals_root,json=withdrawalsRoot,proto3" json:"withdrawals_root,omitempty"` + // Only available in DetailLevel: EXTENDED + TxDependency *Uint64NestedArray `protobuf:"bytes,20,opt,name=tx_dependency,json=txDependency,proto3" json:"tx_dependency,omitempty"` + // BlobGasUsed was added by EIP-4844 and is ignored in legacy headers. + BlobGasUsed *uint64 `protobuf:"varint,22,opt,name=blob_gas_used,json=blobGasUsed,proto3,oneof" json:"blob_gas_used,omitempty"` + // ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers. + ExcessBlobGas *uint64 `protobuf:"varint,23,opt,name=excess_blob_gas,json=excessBlobGas,proto3,oneof" json:"excess_blob_gas,omitempty"` + // ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers. + ParentBeaconRoot []byte `protobuf:"bytes,24,opt,name=parent_beacon_root,json=parentBeaconRoot,proto3" json:"parent_beacon_root,omitempty"` +} + +func (x *BlockHeader) Reset() { + *x = BlockHeader{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BlockHeader) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockHeader) ProtoMessage() {} + +func (x *BlockHeader) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BlockHeader.ProtoReflect.Descriptor instead. +func (*BlockHeader) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{1} +} + +func (x *BlockHeader) GetParentHash() []byte { + if x != nil { + return x.ParentHash + } + return nil +} + +func (x *BlockHeader) GetUncleHash() []byte { + if x != nil { + return x.UncleHash + } + return nil +} + +func (x *BlockHeader) GetCoinbase() []byte { + if x != nil { + return x.Coinbase + } + return nil +} + +func (x *BlockHeader) GetStateRoot() []byte { + if x != nil { + return x.StateRoot + } + return nil +} + +func (x *BlockHeader) GetTransactionsRoot() []byte { + if x != nil { + return x.TransactionsRoot + } + return nil +} + +func (x *BlockHeader) GetReceiptRoot() []byte { + if x != nil { + return x.ReceiptRoot + } + return nil +} + +func (x *BlockHeader) GetLogsBloom() []byte { + if x != nil { + return x.LogsBloom + } + return nil +} + +func (x *BlockHeader) GetDifficulty() *BigInt { + if x != nil { + return x.Difficulty + } + return nil +} + +func (x *BlockHeader) GetTotalDifficulty() *BigInt { + if x != nil { + return x.TotalDifficulty + } + return nil +} + +func (x *BlockHeader) GetNumber() uint64 { + if x != nil { + return x.Number + } + return 0 +} + +func (x *BlockHeader) GetGasLimit() uint64 { + if x != nil { + return x.GasLimit + } + return 0 +} + +func (x *BlockHeader) GetGasUsed() uint64 { + if x != nil { + return x.GasUsed + } + return 0 +} + +func (x *BlockHeader) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + +func (x *BlockHeader) GetExtraData() []byte { + if x != nil { + return x.ExtraData + } + return nil +} + +func (x *BlockHeader) GetMixHash() []byte { + if x != nil { + return x.MixHash + } + return nil +} + +func (x *BlockHeader) GetNonce() uint64 { + if x != nil { + return x.Nonce + } + return 0 +} + +func (x *BlockHeader) GetHash() []byte { + if x != nil { + return x.Hash + } + return nil +} + +func (x *BlockHeader) GetBaseFeePerGas() *BigInt { + if x != nil { + return x.BaseFeePerGas + } + return nil +} + +func (x *BlockHeader) GetWithdrawalsRoot() []byte { + if x != nil { + return x.WithdrawalsRoot + } + return nil +} + +func (x *BlockHeader) GetTxDependency() *Uint64NestedArray { + if x != nil { + return x.TxDependency + } + return nil +} + +func (x *BlockHeader) GetBlobGasUsed() uint64 { + if x != nil && x.BlobGasUsed != nil { + return *x.BlobGasUsed + } + return 0 +} + +func (x *BlockHeader) GetExcessBlobGas() uint64 { + if x != nil && x.ExcessBlobGas != nil { + return *x.ExcessBlobGas + } + return 0 +} + +func (x *BlockHeader) GetParentBeaconRoot() []byte { + if x != nil { + return x.ParentBeaconRoot + } + return nil +} + +type Uint64NestedArray struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Val []*Uint64Array `protobuf:"bytes,1,rep,name=val,proto3" json:"val,omitempty"` +} + +func (x *Uint64NestedArray) Reset() { + *x = Uint64NestedArray{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Uint64NestedArray) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Uint64NestedArray) ProtoMessage() {} + +func (x *Uint64NestedArray) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Uint64NestedArray.ProtoReflect.Descriptor instead. +func (*Uint64NestedArray) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{2} +} + +func (x *Uint64NestedArray) GetVal() []*Uint64Array { + if x != nil { + return x.Val + } + return nil +} + +type Uint64Array struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Val []uint64 `protobuf:"varint,1,rep,packed,name=val,proto3" json:"val,omitempty"` +} + +func (x *Uint64Array) Reset() { + *x = Uint64Array{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Uint64Array) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Uint64Array) ProtoMessage() {} + +func (x *Uint64Array) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Uint64Array.ProtoReflect.Descriptor instead. +func (*Uint64Array) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{3} +} + +func (x *Uint64Array) GetVal() []uint64 { + if x != nil { + return x.Val + } + return nil +} + +type BigInt struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Bytes []byte `protobuf:"bytes,1,opt,name=bytes,proto3" json:"bytes,omitempty"` +} + +func (x *BigInt) Reset() { + *x = BigInt{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BigInt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BigInt) ProtoMessage() {} + +func (x *BigInt) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BigInt.ProtoReflect.Descriptor instead. +func (*BigInt) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{4} +} + +func (x *BigInt) GetBytes() []byte { + if x != nil { + return x.Bytes + } + return nil +} + +// TransactionTrace is full trace of execution of the transaction when the +// it actually executed on chain. +// +// It contains all the transaction details like `from`, `to`, `gas`, etc. +// as well as all the internal calls that were made during the transaction. +// +// The `calls` vector contains Call objects which have balance changes, events +// storage changes, etc. +// +// If ordering is important between elements, almost each message like `Log`, +// `Call`, `StorageChange`, etc. have an ordinal field that is represents "execution" +// order of the said element against all other elements in this block. +// +// Due to how the call tree works doing "naively", looping through all calls then +// through a Call's element like `logs` while not yielding the elements in the order +// they were executed on chain. A log in call could have been done before or after +// another in another call depending on the actual call tree. +// +// The `calls` are ordered by creation order and the call tree can be re-computing +// using fields found in `Call` object (parent/child relationship). +// +// Another important thing to note is that even if a transaction succeed, some calls +// within it could have been reverted internally, if this is important to you, you must +// check the field `state_reverted` on the `Call` to determine if it was fully committed +// to the chain or not. +type TransactionTrace struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // consensus + To []byte `protobuf:"bytes,1,opt,name=to,proto3" json:"to,omitempty"` + Nonce uint64 `protobuf:"varint,2,opt,name=nonce,proto3" json:"nonce,omitempty"` + // GasPrice represents the effective price that has been paid for each gas unit of this transaction. Over time, the + // Ethereum rules changes regarding GasPrice field here. Before London fork, the GasPrice was always set to the + // fixed gas price. After London fork, this value has different meaning depending on the transaction type (see `Type` field). + // + // In cases where `TransactionTrace.Type == TRX_TYPE_LEGACY || TRX_TYPE_ACCESS_LIST`, then GasPrice has the same meaning + // as before the London fork. + // + // In cases where `TransactionTrace.Type == TRX_TYPE_DYNAMIC_FEE`, then GasPrice is the effective gas price paid + // for the transaction which is equals to `BlockHeader.BaseFeePerGas + TransactionTrace.` + GasPrice *BigInt `protobuf:"bytes,3,opt,name=gas_price,json=gasPrice,proto3" json:"gas_price,omitempty"` + // GasLimit is the maximum of gas unit the sender of the transaction is willing to consume when perform the EVM + // execution of the whole transaction + GasLimit uint64 `protobuf:"varint,4,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty"` + // Value is the amount of Ether transferred as part of this transaction. + Value *BigInt `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"` + // Input data the transaction will receive for execution of EVM. + Input []byte `protobuf:"bytes,6,opt,name=input,proto3" json:"input,omitempty"` + // V is the recovery ID value for the signature Y point. + V []byte `protobuf:"bytes,7,opt,name=v,proto3" json:"v,omitempty"` + // R is the signature's X point on the elliptic curve (32 bytes). + R []byte `protobuf:"bytes,8,opt,name=r,proto3" json:"r,omitempty"` + // S is the signature's Y point on the elliptic curve (32 bytes). + S []byte `protobuf:"bytes,9,opt,name=s,proto3" json:"s,omitempty"` + // GasUsed is the total amount of gas unit used for the whole execution of the transaction. + GasUsed uint64 `protobuf:"varint,10,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` + // Type represents the Ethereum transaction type, available only since EIP-2718 & EIP-2930 activation which happened on Berlin fork. + // The value is always set even for transaction before Berlin fork because those before the fork are still legacy transactions. + Type TransactionTrace_Type `protobuf:"varint,12,opt,name=type,proto3,enum=sf.ethereum.type.v2.TransactionTrace_Type" json:"type,omitempty"` + // AcccessList represents the storage access this transaction has agreed to do in which case those storage + // access cost less gas unit per access. + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_ACCESS_LIST || TRX_TYPE_DYNAMIC_FEE` which + // is possible only if Berlin (TRX_TYPE_ACCESS_LIST) nor London (TRX_TYPE_DYNAMIC_FEE) fork are active on the chain. + AccessList []*AccessTuple `protobuf:"bytes,14,rep,name=access_list,json=accessList,proto3" json:"access_list,omitempty"` + // MaxFeePerGas is the maximum fee per gas the user is willing to pay for the transaction gas used. + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_DYNAMIC_FEE` which is possible only + // if Londong fork is active on the chain. + // + // Only available in DetailLevel: EXTENDED + MaxFeePerGas *BigInt `protobuf:"bytes,11,opt,name=max_fee_per_gas,json=maxFeePerGas,proto3" json:"max_fee_per_gas,omitempty"` + // MaxPriorityFeePerGas is priority fee per gas the user to pay in extra to the miner on top of the block's + // base fee. + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_DYNAMIC_FEE` which is possible only + // if London fork is active on the chain. + // + // Only available in DetailLevel: EXTENDED + MaxPriorityFeePerGas *BigInt `protobuf:"bytes,13,opt,name=max_priority_fee_per_gas,json=maxPriorityFeePerGas,proto3" json:"max_priority_fee_per_gas,omitempty"` + // meta + Index uint32 `protobuf:"varint,20,opt,name=index,proto3" json:"index,omitempty"` + Hash []byte `protobuf:"bytes,21,opt,name=hash,proto3" json:"hash,omitempty"` + From []byte `protobuf:"bytes,22,opt,name=from,proto3" json:"from,omitempty"` + // Only available in DetailLevel: EXTENDED + ReturnData []byte `protobuf:"bytes,23,opt,name=return_data,json=returnData,proto3" json:"return_data,omitempty"` + // Only available in DetailLevel: EXTENDED + PublicKey []byte `protobuf:"bytes,24,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + BeginOrdinal uint64 `protobuf:"varint,25,opt,name=begin_ordinal,json=beginOrdinal,proto3" json:"begin_ordinal,omitempty"` + EndOrdinal uint64 `protobuf:"varint,26,opt,name=end_ordinal,json=endOrdinal,proto3" json:"end_ordinal,omitempty"` + // TransactionTraceStatus is the status of the transaction execution and will let you know if the transaction + // was successful or not. + // + // A successful transaction has been recorded to the blockchain's state for calls in it that were successful. + // This means it's possible only a subset of the calls were properly recorded, refer to [calls[].state_reverted] field + // to determine which calls were reverted. + // + // A quirks of the Ethereum protocol is that a transaction `FAILED` or `REVERTED` still affects the blockchain's + // state for **some** of the state changes. Indeed, in those cases, the transactions fees are still paid to the miner + // which means there is a balance change for the transaction's emitter (e.g. `from`) to pay the gas fees, an optional + // balance change for gas refunded to the transaction's emitter (e.g. `from`) and a balance change for the miner who + // received the transaction fees. There is also a nonce change for the transaction's emitter (e.g. `from`). + // + // This means that to properly record the state changes for a transaction, you need to conditionally procees the + // transaction's status. + // + // For a `SUCCEEDED` transaction, you iterate over the `calls` array and record the state changes for each call for + // which `state_reverted == false` (if a transaction succeeded, the call at #0 will always `state_reverted == false` + // because it aligns with the transaction). + // + // For a `FAILED` or `REVERTED` transaction, you iterate over the root call (e.g. at #0, will always exist) for + // balance changes you process those where `reason` is either `REASON_GAS_BUY`, `REASON_GAS_REFUND` or + // `REASON_REWARD_TRANSACTION_FEE` and for nonce change, still on the root call, you pick the nonce change which the + // smallest ordinal (if more than one). + Status TransactionTraceStatus `protobuf:"varint,30,opt,name=status,proto3,enum=sf.ethereum.type.v2.TransactionTraceStatus" json:"status,omitempty"` + Receipt *TransactionReceipt `protobuf:"bytes,31,opt,name=receipt,proto3" json:"receipt,omitempty"` + // Only available in DetailLevel: EXTENDED + Calls []*Call `protobuf:"bytes,32,rep,name=calls,proto3" json:"calls,omitempty"` + // BlobGas is the amount of gas the transaction is going to pay for the blobs, this is a computed value + // equivalent to `self.blob_gas_fee_cap * len(self.blob_hashes)` and provided in the model for convenience. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + BlobGas *uint64 `protobuf:"varint,33,opt,name=blob_gas,json=blobGas,proto3,oneof" json:"blob_gas,omitempty"` + // BlobGasFeeCap is the maximum fee per data gas the user is willing to pay for the data gas used. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + BlobGasFeeCap *BigInt `protobuf:"bytes,34,opt,name=blob_gas_fee_cap,json=blobGasFeeCap,proto3,oneof" json:"blob_gas_fee_cap,omitempty"` + // BlobHashes field represents a list of hash outputs from 'kzg_to_versioned_hash' which + // essentially is a version byte + the sha256 hash of the blob commitment (e.g. + // `BLOB_COMMITMENT_VERSION_KZG + sha256(commitment)[1:]`. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + BlobHashes [][]byte `protobuf:"bytes,35,rep,name=blob_hashes,json=blobHashes,proto3" json:"blob_hashes,omitempty"` +} + +func (x *TransactionTrace) Reset() { + *x = TransactionTrace{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TransactionTrace) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransactionTrace) ProtoMessage() {} + +func (x *TransactionTrace) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransactionTrace.ProtoReflect.Descriptor instead. +func (*TransactionTrace) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{5} +} + +func (x *TransactionTrace) GetTo() []byte { + if x != nil { + return x.To + } + return nil +} + +func (x *TransactionTrace) GetNonce() uint64 { + if x != nil { + return x.Nonce + } + return 0 +} + +func (x *TransactionTrace) GetGasPrice() *BigInt { + if x != nil { + return x.GasPrice + } + return nil +} + +func (x *TransactionTrace) GetGasLimit() uint64 { + if x != nil { + return x.GasLimit + } + return 0 +} + +func (x *TransactionTrace) GetValue() *BigInt { + if x != nil { + return x.Value + } + return nil +} + +func (x *TransactionTrace) GetInput() []byte { + if x != nil { + return x.Input + } + return nil +} + +func (x *TransactionTrace) GetV() []byte { + if x != nil { + return x.V + } + return nil +} + +func (x *TransactionTrace) GetR() []byte { + if x != nil { + return x.R + } + return nil +} + +func (x *TransactionTrace) GetS() []byte { + if x != nil { + return x.S + } + return nil +} + +func (x *TransactionTrace) GetGasUsed() uint64 { + if x != nil { + return x.GasUsed + } + return 0 +} + +func (x *TransactionTrace) GetType() TransactionTrace_Type { + if x != nil { + return x.Type + } + return TransactionTrace_TRX_TYPE_LEGACY +} + +func (x *TransactionTrace) GetAccessList() []*AccessTuple { + if x != nil { + return x.AccessList + } + return nil +} + +func (x *TransactionTrace) GetMaxFeePerGas() *BigInt { + if x != nil { + return x.MaxFeePerGas + } + return nil +} + +func (x *TransactionTrace) GetMaxPriorityFeePerGas() *BigInt { + if x != nil { + return x.MaxPriorityFeePerGas + } + return nil +} + +func (x *TransactionTrace) GetIndex() uint32 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *TransactionTrace) GetHash() []byte { + if x != nil { + return x.Hash + } + return nil +} + +func (x *TransactionTrace) GetFrom() []byte { + if x != nil { + return x.From + } + return nil +} + +func (x *TransactionTrace) GetReturnData() []byte { + if x != nil { + return x.ReturnData + } + return nil +} + +func (x *TransactionTrace) GetPublicKey() []byte { + if x != nil { + return x.PublicKey + } + return nil +} + +func (x *TransactionTrace) GetBeginOrdinal() uint64 { + if x != nil { + return x.BeginOrdinal + } + return 0 +} + +func (x *TransactionTrace) GetEndOrdinal() uint64 { + if x != nil { + return x.EndOrdinal + } + return 0 +} + +func (x *TransactionTrace) GetStatus() TransactionTraceStatus { + if x != nil { + return x.Status + } + return TransactionTraceStatus_UNKNOWN +} + +func (x *TransactionTrace) GetReceipt() *TransactionReceipt { + if x != nil { + return x.Receipt + } + return nil +} + +func (x *TransactionTrace) GetCalls() []*Call { + if x != nil { + return x.Calls + } + return nil +} + +func (x *TransactionTrace) GetBlobGas() uint64 { + if x != nil && x.BlobGas != nil { + return *x.BlobGas + } + return 0 +} + +func (x *TransactionTrace) GetBlobGasFeeCap() *BigInt { + if x != nil { + return x.BlobGasFeeCap + } + return nil +} + +func (x *TransactionTrace) GetBlobHashes() [][]byte { + if x != nil { + return x.BlobHashes + } + return nil +} + +// AccessTuple represents a list of storage keys for a given contract's address and is used +// for AccessList construction. +type AccessTuple struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + StorageKeys [][]byte `protobuf:"bytes,2,rep,name=storage_keys,json=storageKeys,proto3" json:"storage_keys,omitempty"` +} + +func (x *AccessTuple) Reset() { + *x = AccessTuple{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AccessTuple) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AccessTuple) ProtoMessage() {} + +func (x *AccessTuple) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AccessTuple.ProtoReflect.Descriptor instead. +func (*AccessTuple) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{6} +} + +func (x *AccessTuple) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *AccessTuple) GetStorageKeys() [][]byte { + if x != nil { + return x.StorageKeys + } + return nil +} + +type TransactionReceipt struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // State root is an intermediate state_root hash, computed in-between transactions to make + // **sure** you could build a proof and point to state in the middle of a block. Geth client + // uses `PostState + root + PostStateOrStatus“ while Parity used `status_code, root...“ this piles + // hardforks, see (read the EIPs first): + // - https://github.com/ethereum/EIPs/blob/master/EIPS/eip-658.md + // + // Moreover, the notion of `Outcome“ in parity, which segregates the two concepts, which are + // stored in the same field `status_code“ can be computed based on such a hack of the `state_root` + // field, following `EIP-658`. + // + // Before Byzantinium hard fork, this field is always empty. + StateRoot []byte `protobuf:"bytes,1,opt,name=state_root,json=stateRoot,proto3" json:"state_root,omitempty"` + CumulativeGasUsed uint64 `protobuf:"varint,2,opt,name=cumulative_gas_used,json=cumulativeGasUsed,proto3" json:"cumulative_gas_used,omitempty"` + LogsBloom []byte `protobuf:"bytes,3,opt,name=logs_bloom,json=logsBloom,proto3" json:"logs_bloom,omitempty"` + Logs []*Log `protobuf:"bytes,4,rep,name=logs,proto3" json:"logs,omitempty"` + // BlobGasUsed is the amount of blob gas that has been used within this transaction. At time + // of writing, this is equal to `self.blob_gas_fee_cap * len(self.blob_hashes)`. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + BlobGasUsed *uint64 `protobuf:"varint,5,opt,name=blob_gas_used,json=blobGasUsed,proto3,oneof" json:"blob_gas_used,omitempty"` + // BlobGasPrice is the amount to pay per blob item in the transaction. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + BlobGasPrice *BigInt `protobuf:"bytes,6,opt,name=blob_gas_price,json=blobGasPrice,proto3,oneof" json:"blob_gas_price,omitempty"` +} + +func (x *TransactionReceipt) Reset() { + *x = TransactionReceipt{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TransactionReceipt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransactionReceipt) ProtoMessage() {} + +func (x *TransactionReceipt) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransactionReceipt.ProtoReflect.Descriptor instead. +func (*TransactionReceipt) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{7} +} + +func (x *TransactionReceipt) GetStateRoot() []byte { + if x != nil { + return x.StateRoot + } + return nil +} + +func (x *TransactionReceipt) GetCumulativeGasUsed() uint64 { + if x != nil { + return x.CumulativeGasUsed + } + return 0 +} + +func (x *TransactionReceipt) GetLogsBloom() []byte { + if x != nil { + return x.LogsBloom + } + return nil +} + +func (x *TransactionReceipt) GetLogs() []*Log { + if x != nil { + return x.Logs + } + return nil +} + +func (x *TransactionReceipt) GetBlobGasUsed() uint64 { + if x != nil && x.BlobGasUsed != nil { + return *x.BlobGasUsed + } + return 0 +} + +func (x *TransactionReceipt) GetBlobGasPrice() *BigInt { + if x != nil { + return x.BlobGasPrice + } + return nil +} + +type Log struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Topics [][]byte `protobuf:"bytes,2,rep,name=topics,proto3" json:"topics,omitempty"` + Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + // Index is the index of the log relative to the transaction. This index + // is always populated regardless of the state revertion of the the call + // that emitted this log. + // + // Only available in DetailLevel: EXTENDED + Index uint32 `protobuf:"varint,4,opt,name=index,proto3" json:"index,omitempty"` + // BlockIndex represents the index of the log relative to the Block. + // + // An **important** notice is that this field will be 0 when the call + // that emitted the log has been reverted by the chain. + // + // Currently, there is two locations where a Log can be obtained: + // - block.transaction_traces[].receipt.logs[] + // - block.transaction_traces[].calls[].logs[] + // + // In the `receipt` case, the logs will be populated only when the call + // that emitted them has not been reverted by the chain and when in this + // position, the `blockIndex` is always populated correctly. + // + // In the case of `calls` case, for `call` where `stateReverted == true`, + // the `blockIndex` value will always be 0. + BlockIndex uint32 `protobuf:"varint,6,opt,name=blockIndex,proto3" json:"blockIndex,omitempty"` + Ordinal uint64 `protobuf:"varint,7,opt,name=ordinal,proto3" json:"ordinal,omitempty"` +} + +func (x *Log) Reset() { + *x = Log{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Log) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Log) ProtoMessage() {} + +func (x *Log) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Log.ProtoReflect.Descriptor instead. +func (*Log) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{8} +} + +func (x *Log) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *Log) GetTopics() [][]byte { + if x != nil { + return x.Topics + } + return nil +} + +func (x *Log) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *Log) GetIndex() uint32 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *Log) GetBlockIndex() uint32 { + if x != nil { + return x.BlockIndex + } + return 0 +} + +func (x *Log) GetOrdinal() uint64 { + if x != nil { + return x.Ordinal + } + return 0 +} + +type Call struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Index uint32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + ParentIndex uint32 `protobuf:"varint,2,opt,name=parent_index,json=parentIndex,proto3" json:"parent_index,omitempty"` + Depth uint32 `protobuf:"varint,3,opt,name=depth,proto3" json:"depth,omitempty"` + CallType CallType `protobuf:"varint,4,opt,name=call_type,json=callType,proto3,enum=sf.ethereum.type.v2.CallType" json:"call_type,omitempty"` + Caller []byte `protobuf:"bytes,5,opt,name=caller,proto3" json:"caller,omitempty"` + Address []byte `protobuf:"bytes,6,opt,name=address,proto3" json:"address,omitempty"` + Value *BigInt `protobuf:"bytes,7,opt,name=value,proto3" json:"value,omitempty"` + GasLimit uint64 `protobuf:"varint,8,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty"` + GasConsumed uint64 `protobuf:"varint,9,opt,name=gas_consumed,json=gasConsumed,proto3" json:"gas_consumed,omitempty"` + ReturnData []byte `protobuf:"bytes,13,opt,name=return_data,json=returnData,proto3" json:"return_data,omitempty"` + Input []byte `protobuf:"bytes,14,opt,name=input,proto3" json:"input,omitempty"` + ExecutedCode bool `protobuf:"varint,15,opt,name=executed_code,json=executedCode,proto3" json:"executed_code,omitempty"` + Suicide bool `protobuf:"varint,16,opt,name=suicide,proto3" json:"suicide,omitempty"` + // hex representation of the hash -> preimage + KeccakPreimages map[string]string `protobuf:"bytes,20,rep,name=keccak_preimages,json=keccakPreimages,proto3" json:"keccak_preimages,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + StorageChanges []*StorageChange `protobuf:"bytes,21,rep,name=storage_changes,json=storageChanges,proto3" json:"storage_changes,omitempty"` + BalanceChanges []*BalanceChange `protobuf:"bytes,22,rep,name=balance_changes,json=balanceChanges,proto3" json:"balance_changes,omitempty"` + NonceChanges []*NonceChange `protobuf:"bytes,24,rep,name=nonce_changes,json=nonceChanges,proto3" json:"nonce_changes,omitempty"` + Logs []*Log `protobuf:"bytes,25,rep,name=logs,proto3" json:"logs,omitempty"` + CodeChanges []*CodeChange `protobuf:"bytes,26,rep,name=code_changes,json=codeChanges,proto3" json:"code_changes,omitempty"` + GasChanges []*GasChange `protobuf:"bytes,28,rep,name=gas_changes,json=gasChanges,proto3" json:"gas_changes,omitempty"` + // In Ethereum, a call can be either: + // - Successfull, execution passes without any problem encountered + // - Failed, execution failed, and remaining gas should be consumed + // - Reverted, execution failed, but only gas consumed so far is billed, remaining gas is refunded + // + // When a call is either `failed` or `reverted`, the `status_failed` field + // below is set to `true`. If the status is `reverted`, then both `status_failed` + // and `status_reverted` are going to be set to `true`. + StatusFailed bool `protobuf:"varint,10,opt,name=status_failed,json=statusFailed,proto3" json:"status_failed,omitempty"` + StatusReverted bool `protobuf:"varint,12,opt,name=status_reverted,json=statusReverted,proto3" json:"status_reverted,omitempty"` + // Populated when a call either failed or reverted, so when `status_failed == true`, + // see above for details about those flags. + FailureReason string `protobuf:"bytes,11,opt,name=failure_reason,json=failureReason,proto3" json:"failure_reason,omitempty"` + // This field represents wheter or not the state changes performed + // by this call were correctly recorded by the blockchain. + // + // On Ethereum, a transaction can record state changes even if some + // of its inner nested calls failed. This is problematic however since + // a call will invalidate all its state changes as well as all state + // changes performed by its child call. This means that even if a call + // has a status of `SUCCESS`, the chain might have reverted all the state + // changes it performed. + // + // ```text + // + // Trx 1 + // Call #1 + // Call #2 + // Call #3 + // |--- Failure here + // Call #4 + // + // ``` + // + // In the transaction above, while Call #2 and Call #3 would have the + // status `EXECUTED`. + // + // If you check all calls and check only `state_reverted` flag, you might be missing + // some balance changes and nonce changes. This is because when a full transaction fails + // in ethereum (e.g. `calls.all(x.state_reverted == true)`), there is still the transaction + // fee that are recorded to the chain. + // + // Refer to [TransactionTrace#status] field for more details about the handling you must + // perform. + StateReverted bool `protobuf:"varint,30,opt,name=state_reverted,json=stateReverted,proto3" json:"state_reverted,omitempty"` + BeginOrdinal uint64 `protobuf:"varint,31,opt,name=begin_ordinal,json=beginOrdinal,proto3" json:"begin_ordinal,omitempty"` + EndOrdinal uint64 `protobuf:"varint,32,opt,name=end_ordinal,json=endOrdinal,proto3" json:"end_ordinal,omitempty"` + AccountCreations []*AccountCreation `protobuf:"bytes,33,rep,name=account_creations,json=accountCreations,proto3" json:"account_creations,omitempty"` +} + +func (x *Call) Reset() { + *x = Call{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Call) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Call) ProtoMessage() {} + +func (x *Call) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Call.ProtoReflect.Descriptor instead. +func (*Call) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{9} +} + +func (x *Call) GetIndex() uint32 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *Call) GetParentIndex() uint32 { + if x != nil { + return x.ParentIndex + } + return 0 +} + +func (x *Call) GetDepth() uint32 { + if x != nil { + return x.Depth + } + return 0 +} + +func (x *Call) GetCallType() CallType { + if x != nil { + return x.CallType + } + return CallType_UNSPECIFIED +} + +func (x *Call) GetCaller() []byte { + if x != nil { + return x.Caller + } + return nil +} + +func (x *Call) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *Call) GetValue() *BigInt { + if x != nil { + return x.Value + } + return nil +} + +func (x *Call) GetGasLimit() uint64 { + if x != nil { + return x.GasLimit + } + return 0 +} + +func (x *Call) GetGasConsumed() uint64 { + if x != nil { + return x.GasConsumed + } + return 0 +} + +func (x *Call) GetReturnData() []byte { + if x != nil { + return x.ReturnData + } + return nil +} + +func (x *Call) GetInput() []byte { + if x != nil { + return x.Input + } + return nil +} + +func (x *Call) GetExecutedCode() bool { + if x != nil { + return x.ExecutedCode + } + return false +} + +func (x *Call) GetSuicide() bool { + if x != nil { + return x.Suicide + } + return false +} + +func (x *Call) GetKeccakPreimages() map[string]string { + if x != nil { + return x.KeccakPreimages + } + return nil +} + +func (x *Call) GetStorageChanges() []*StorageChange { + if x != nil { + return x.StorageChanges + } + return nil +} + +func (x *Call) GetBalanceChanges() []*BalanceChange { + if x != nil { + return x.BalanceChanges + } + return nil +} + +func (x *Call) GetNonceChanges() []*NonceChange { + if x != nil { + return x.NonceChanges + } + return nil +} + +func (x *Call) GetLogs() []*Log { + if x != nil { + return x.Logs + } + return nil +} + +func (x *Call) GetCodeChanges() []*CodeChange { + if x != nil { + return x.CodeChanges + } + return nil +} + +func (x *Call) GetGasChanges() []*GasChange { + if x != nil { + return x.GasChanges + } + return nil +} + +func (x *Call) GetStatusFailed() bool { + if x != nil { + return x.StatusFailed + } + return false +} + +func (x *Call) GetStatusReverted() bool { + if x != nil { + return x.StatusReverted + } + return false +} + +func (x *Call) GetFailureReason() string { + if x != nil { + return x.FailureReason + } + return "" +} + +func (x *Call) GetStateReverted() bool { + if x != nil { + return x.StateReverted + } + return false +} + +func (x *Call) GetBeginOrdinal() uint64 { + if x != nil { + return x.BeginOrdinal + } + return 0 +} + +func (x *Call) GetEndOrdinal() uint64 { + if x != nil { + return x.EndOrdinal + } + return 0 +} + +func (x *Call) GetAccountCreations() []*AccountCreation { + if x != nil { + return x.AccountCreations + } + return nil +} + +type StorageChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + OldValue []byte `protobuf:"bytes,3,opt,name=old_value,json=oldValue,proto3" json:"old_value,omitempty"` + NewValue []byte `protobuf:"bytes,4,opt,name=new_value,json=newValue,proto3" json:"new_value,omitempty"` + Ordinal uint64 `protobuf:"varint,5,opt,name=ordinal,proto3" json:"ordinal,omitempty"` +} + +func (x *StorageChange) Reset() { + *x = StorageChange{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StorageChange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StorageChange) ProtoMessage() {} + +func (x *StorageChange) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StorageChange.ProtoReflect.Descriptor instead. +func (*StorageChange) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{10} +} + +func (x *StorageChange) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *StorageChange) GetKey() []byte { + if x != nil { + return x.Key + } + return nil +} + +func (x *StorageChange) GetOldValue() []byte { + if x != nil { + return x.OldValue + } + return nil +} + +func (x *StorageChange) GetNewValue() []byte { + if x != nil { + return x.NewValue + } + return nil +} + +func (x *StorageChange) GetOrdinal() uint64 { + if x != nil { + return x.Ordinal + } + return 0 +} + +type BalanceChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + OldValue *BigInt `protobuf:"bytes,2,opt,name=old_value,json=oldValue,proto3" json:"old_value,omitempty"` + NewValue *BigInt `protobuf:"bytes,3,opt,name=new_value,json=newValue,proto3" json:"new_value,omitempty"` + Reason BalanceChange_Reason `protobuf:"varint,4,opt,name=reason,proto3,enum=sf.ethereum.type.v2.BalanceChange_Reason" json:"reason,omitempty"` + Ordinal uint64 `protobuf:"varint,5,opt,name=ordinal,proto3" json:"ordinal,omitempty"` +} + +func (x *BalanceChange) Reset() { + *x = BalanceChange{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BalanceChange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BalanceChange) ProtoMessage() {} + +func (x *BalanceChange) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BalanceChange.ProtoReflect.Descriptor instead. +func (*BalanceChange) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{11} +} + +func (x *BalanceChange) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *BalanceChange) GetOldValue() *BigInt { + if x != nil { + return x.OldValue + } + return nil +} + +func (x *BalanceChange) GetNewValue() *BigInt { + if x != nil { + return x.NewValue + } + return nil +} + +func (x *BalanceChange) GetReason() BalanceChange_Reason { + if x != nil { + return x.Reason + } + return BalanceChange_REASON_UNKNOWN +} + +func (x *BalanceChange) GetOrdinal() uint64 { + if x != nil { + return x.Ordinal + } + return 0 +} + +type NonceChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + OldValue uint64 `protobuf:"varint,2,opt,name=old_value,json=oldValue,proto3" json:"old_value,omitempty"` + NewValue uint64 `protobuf:"varint,3,opt,name=new_value,json=newValue,proto3" json:"new_value,omitempty"` + Ordinal uint64 `protobuf:"varint,4,opt,name=ordinal,proto3" json:"ordinal,omitempty"` +} + +func (x *NonceChange) Reset() { + *x = NonceChange{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NonceChange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NonceChange) ProtoMessage() {} + +func (x *NonceChange) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NonceChange.ProtoReflect.Descriptor instead. +func (*NonceChange) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{12} +} + +func (x *NonceChange) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *NonceChange) GetOldValue() uint64 { + if x != nil { + return x.OldValue + } + return 0 +} + +func (x *NonceChange) GetNewValue() uint64 { + if x != nil { + return x.NewValue + } + return 0 +} + +func (x *NonceChange) GetOrdinal() uint64 { + if x != nil { + return x.Ordinal + } + return 0 +} + +type AccountCreation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Account []byte `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` + Ordinal uint64 `protobuf:"varint,2,opt,name=ordinal,proto3" json:"ordinal,omitempty"` +} + +func (x *AccountCreation) Reset() { + *x = AccountCreation{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AccountCreation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AccountCreation) ProtoMessage() {} + +func (x *AccountCreation) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AccountCreation.ProtoReflect.Descriptor instead. +func (*AccountCreation) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{13} +} + +func (x *AccountCreation) GetAccount() []byte { + if x != nil { + return x.Account + } + return nil +} + +func (x *AccountCreation) GetOrdinal() uint64 { + if x != nil { + return x.Ordinal + } + return 0 +} + +type CodeChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + OldHash []byte `protobuf:"bytes,2,opt,name=old_hash,json=oldHash,proto3" json:"old_hash,omitempty"` + OldCode []byte `protobuf:"bytes,3,opt,name=old_code,json=oldCode,proto3" json:"old_code,omitempty"` + NewHash []byte `protobuf:"bytes,4,opt,name=new_hash,json=newHash,proto3" json:"new_hash,omitempty"` + NewCode []byte `protobuf:"bytes,5,opt,name=new_code,json=newCode,proto3" json:"new_code,omitempty"` + Ordinal uint64 `protobuf:"varint,6,opt,name=ordinal,proto3" json:"ordinal,omitempty"` +} + +func (x *CodeChange) Reset() { + *x = CodeChange{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CodeChange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CodeChange) ProtoMessage() {} + +func (x *CodeChange) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CodeChange.ProtoReflect.Descriptor instead. +func (*CodeChange) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{14} +} + +func (x *CodeChange) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *CodeChange) GetOldHash() []byte { + if x != nil { + return x.OldHash + } + return nil +} + +func (x *CodeChange) GetOldCode() []byte { + if x != nil { + return x.OldCode + } + return nil +} + +func (x *CodeChange) GetNewHash() []byte { + if x != nil { + return x.NewHash + } + return nil +} + +func (x *CodeChange) GetNewCode() []byte { + if x != nil { + return x.NewCode + } + return nil +} + +func (x *CodeChange) GetOrdinal() uint64 { + if x != nil { + return x.Ordinal + } + return 0 +} + +// The gas change model represents the reason why some gas cost has occurred. +// The gas is computed per actual op codes. Doing them completely might prove +// overwhelming in most cases. +// +// Hence, we only index some of them, those that are costy like all the calls +// one, log events, return data, etc. +type GasChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OldValue uint64 `protobuf:"varint,1,opt,name=old_value,json=oldValue,proto3" json:"old_value,omitempty"` + NewValue uint64 `protobuf:"varint,2,opt,name=new_value,json=newValue,proto3" json:"new_value,omitempty"` + Reason GasChange_Reason `protobuf:"varint,3,opt,name=reason,proto3,enum=sf.ethereum.type.v2.GasChange_Reason" json:"reason,omitempty"` + Ordinal uint64 `protobuf:"varint,4,opt,name=ordinal,proto3" json:"ordinal,omitempty"` +} + +func (x *GasChange) Reset() { + *x = GasChange{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GasChange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GasChange) ProtoMessage() {} + +func (x *GasChange) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GasChange.ProtoReflect.Descriptor instead. +func (*GasChange) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{15} +} + +func (x *GasChange) GetOldValue() uint64 { + if x != nil { + return x.OldValue + } + return 0 +} + +func (x *GasChange) GetNewValue() uint64 { + if x != nil { + return x.NewValue + } + return 0 +} + +func (x *GasChange) GetReason() GasChange_Reason { + if x != nil { + return x.Reason + } + return GasChange_REASON_UNKNOWN +} + +func (x *GasChange) GetOrdinal() uint64 { + if x != nil { + return x.Ordinal + } + return 0 +} + +// HeaderOnlyBlock is used to optimally unpack the [Block] structure (note the +// corresponding message number for the `header` field) while consuming less +// memory, when only the `header` is desired. +// +// WARN: this is a client-side optimization pattern and should be moved in the +// consuming code. +type HeaderOnlyBlock struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Header *BlockHeader `protobuf:"bytes,5,opt,name=header,proto3" json:"header,omitempty"` +} + +func (x *HeaderOnlyBlock) Reset() { + *x = HeaderOnlyBlock{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HeaderOnlyBlock) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HeaderOnlyBlock) ProtoMessage() {} + +func (x *HeaderOnlyBlock) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HeaderOnlyBlock.ProtoReflect.Descriptor instead. +func (*HeaderOnlyBlock) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{16} +} + +func (x *HeaderOnlyBlock) GetHeader() *BlockHeader { + if x != nil { + return x.Header + } + return nil +} + +// BlockWithRefs is a lightweight block, with traces and transactions +// purged from the `block` within, and only. It is used in transports +// to pass block data around. +type BlockWithRefs struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Block *Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"` + TransactionTraceRefs *TransactionRefs `protobuf:"bytes,3,opt,name=transaction_trace_refs,json=transactionTraceRefs,proto3" json:"transaction_trace_refs,omitempty"` + Irreversible bool `protobuf:"varint,4,opt,name=irreversible,proto3" json:"irreversible,omitempty"` +} + +func (x *BlockWithRefs) Reset() { + *x = BlockWithRefs{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BlockWithRefs) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockWithRefs) ProtoMessage() {} + +func (x *BlockWithRefs) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BlockWithRefs.ProtoReflect.Descriptor instead. +func (*BlockWithRefs) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{17} +} + +func (x *BlockWithRefs) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *BlockWithRefs) GetBlock() *Block { + if x != nil { + return x.Block + } + return nil +} + +func (x *BlockWithRefs) GetTransactionTraceRefs() *TransactionRefs { + if x != nil { + return x.TransactionTraceRefs + } + return nil +} + +func (x *BlockWithRefs) GetIrreversible() bool { + if x != nil { + return x.Irreversible + } + return false +} + +type TransactionTraceWithBlockRef struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Trace *TransactionTrace `protobuf:"bytes,1,opt,name=trace,proto3" json:"trace,omitempty"` + BlockRef *BlockRef `protobuf:"bytes,2,opt,name=block_ref,json=blockRef,proto3" json:"block_ref,omitempty"` +} + +func (x *TransactionTraceWithBlockRef) Reset() { + *x = TransactionTraceWithBlockRef{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TransactionTraceWithBlockRef) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransactionTraceWithBlockRef) ProtoMessage() {} + +func (x *TransactionTraceWithBlockRef) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransactionTraceWithBlockRef.ProtoReflect.Descriptor instead. +func (*TransactionTraceWithBlockRef) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{18} +} + +func (x *TransactionTraceWithBlockRef) GetTrace() *TransactionTrace { + if x != nil { + return x.Trace + } + return nil +} + +func (x *TransactionTraceWithBlockRef) GetBlockRef() *BlockRef { + if x != nil { + return x.BlockRef + } + return nil +} + +type TransactionRefs struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hashes [][]byte `protobuf:"bytes,1,rep,name=hashes,proto3" json:"hashes,omitempty"` +} + +func (x *TransactionRefs) Reset() { + *x = TransactionRefs{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TransactionRefs) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransactionRefs) ProtoMessage() {} + +func (x *TransactionRefs) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransactionRefs.ProtoReflect.Descriptor instead. +func (*TransactionRefs) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{19} +} + +func (x *TransactionRefs) GetHashes() [][]byte { + if x != nil { + return x.Hashes + } + return nil +} + +type BlockRef struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hash []byte `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + Number uint64 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"` +} + +func (x *BlockRef) Reset() { + *x = BlockRef{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BlockRef) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockRef) ProtoMessage() {} + +func (x *BlockRef) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BlockRef.ProtoReflect.Descriptor instead. +func (*BlockRef) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{20} +} + +func (x *BlockRef) GetHash() []byte { + if x != nil { + return x.Hash + } + return nil +} + +func (x *BlockRef) GetNumber() uint64 { + if x != nil { + return x.Number + } + return 0 +} + +var File_sf_ethereum_type_v2_type_proto protoreflect.FileDescriptor + +var file_sf_ethereum_type_v2_type_proto_rawDesc = []byte{ + 0x0a, 0x1e, 0x73, 0x66, 0x2f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x74, 0x79, + 0x70, 0x65, 0x2f, 0x76, 0x32, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x13, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, + 0x70, 0x65, 0x2e, 0x76, 0x32, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8e, 0x05, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x68, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, + 0x12, 0x38, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x20, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, + 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x06, 0x75, 0x6e, + 0x63, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x66, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, + 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x75, 0x6e, + 0x63, 0x6c, 0x65, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x25, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, + 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x0f, 0x62, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, + 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0e, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x49, 0x0a, 0x0c, 0x64, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, + 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x0b, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x12, 0x42, 0x0a, 0x0c, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x43, + 0x6f, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b, 0x63, 0x6f, 0x64, 0x65, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0c, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, + 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, + 0x76, 0x32, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, + 0x61, 0x6c, 0x6c, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x03, 0x76, 0x65, 0x72, 0x22, 0x3d, 0x0a, 0x0b, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x4c, + 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x4e, 0x44, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x14, 0x0a, 0x10, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x42, + 0x41, 0x53, 0x45, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x28, 0x10, 0x29, 0x4a, 0x04, 0x08, 0x29, 0x10, + 0x2a, 0x4a, 0x04, 0x08, 0x2a, 0x10, 0x2b, 0x22, 0xd2, 0x07, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, + 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x6e, 0x63, 0x6c, + 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x75, 0x6e, + 0x63, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x62, + 0x61, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x62, + 0x61, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, + 0x6f, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x12, + 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x52, 0x6f, + 0x6f, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x6f, 0x6d, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x73, 0x42, 0x6c, 0x6f, 0x6f, + 0x6d, 0x12, 0x3b, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, + 0x6e, 0x74, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x46, + 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, + 0x74, 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, + 0x69, 0x67, 0x49, 0x6e, 0x74, 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x44, 0x69, 0x66, 0x66, + 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1b, + 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, + 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, + 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0d, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x78, 0x74, 0x72, 0x61, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x19, 0x0a, 0x08, 0x6d, 0x69, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x6d, 0x69, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, + 0x6e, 0x63, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x68, 0x61, 0x73, 0x68, 0x12, 0x44, 0x0a, 0x10, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, + 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x61, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, + 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, 0x6e, 0x74, 0x52, 0x0d, 0x62, 0x61, 0x73, + 0x65, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, 0x61, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x77, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x13, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, + 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x4b, 0x0a, 0x0d, 0x74, 0x78, 0x5f, 0x64, 0x65, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x73, + 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, + 0x76, 0x32, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x52, 0x0c, 0x74, 0x78, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, + 0x63, 0x79, 0x12, 0x27, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x75, + 0x73, 0x65, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x0b, 0x62, 0x6c, 0x6f, + 0x62, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x0f, 0x65, + 0x78, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x18, 0x17, + 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x0d, 0x65, 0x78, 0x63, 0x65, 0x73, 0x73, 0x42, 0x6c, + 0x6f, 0x62, 0x47, 0x61, 0x73, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x61, 0x72, 0x65, + 0x6e, 0x74, 0x5f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x18, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x65, 0x61, 0x63, + 0x6f, 0x6e, 0x52, 0x6f, 0x6f, 0x74, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, + 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x65, 0x78, 0x63, + 0x65, 0x73, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x22, 0x47, 0x0a, 0x11, + 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x41, 0x72, 0x72, 0x61, + 0x79, 0x12, 0x32, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, + 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x41, 0x72, 0x72, 0x61, 0x79, + 0x52, 0x03, 0x76, 0x61, 0x6c, 0x22, 0x1f, 0x0a, 0x0b, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x04, 0x52, 0x03, 0x76, 0x61, 0x6c, 0x22, 0x1e, 0x0a, 0x06, 0x42, 0x69, 0x67, 0x49, 0x6e, 0x74, + 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x22, 0xab, 0x0b, 0x0a, 0x10, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x74, + 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x6e, + 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x12, 0x38, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, 0x6e, + 0x74, 0x52, 0x08, 0x67, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x67, + 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, + 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, + 0x67, 0x49, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x12, 0x0c, 0x0a, 0x01, 0x76, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x01, 0x76, 0x12, + 0x0c, 0x0a, 0x01, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x01, 0x72, 0x12, 0x0c, 0x0a, + 0x01, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x01, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x67, + 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, + 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x41, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x66, + 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, + 0x32, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x52, 0x0a, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0f, 0x6d, 0x61, 0x78, + 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x61, 0x73, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, 0x6e, 0x74, 0x52, + 0x0c, 0x6d, 0x61, 0x78, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, 0x61, 0x73, 0x12, 0x53, 0x0a, + 0x18, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x66, 0x65, + 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x61, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, + 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, 0x6e, 0x74, 0x52, 0x14, 0x6d, 0x61, + 0x78, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, + 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x14, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x15, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, + 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, + 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x17, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x18, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, + 0x6c, 0x18, 0x19, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x4f, 0x72, + 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x64, 0x5f, 0x6f, 0x72, 0x64, + 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x4f, + 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x43, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x41, 0x0a, 0x07, 0x72, + 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x73, + 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, + 0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x63, 0x65, 0x69, 0x70, 0x74, 0x52, 0x07, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x2f, + 0x0a, 0x05, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x18, 0x20, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x05, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x12, + 0x1e, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x18, 0x21, 0x20, 0x01, 0x28, + 0x04, 0x48, 0x00, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x62, 0x47, 0x61, 0x73, 0x88, 0x01, 0x01, 0x12, + 0x49, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x66, 0x65, 0x65, 0x5f, + 0x63, 0x61, 0x70, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, + 0x42, 0x69, 0x67, 0x49, 0x6e, 0x74, 0x48, 0x01, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x62, 0x47, 0x61, + 0x73, 0x46, 0x65, 0x65, 0x43, 0x61, 0x70, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, + 0x6f, 0x62, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x23, 0x20, 0x03, 0x28, 0x0c, 0x52, + 0x0a, 0x62, 0x6c, 0x6f, 0x62, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0xc4, 0x02, 0x0a, 0x04, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x52, 0x58, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x4c, 0x49, 0x53, + 0x54, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x02, 0x12, 0x11, 0x0a, + 0x0d, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4c, 0x4f, 0x42, 0x10, 0x03, + 0x12, 0x1d, 0x0a, 0x19, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x52, 0x42, + 0x49, 0x54, 0x52, 0x55, 0x4d, 0x5f, 0x44, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x10, 0x64, 0x12, + 0x1e, 0x0a, 0x1a, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x52, 0x42, 0x49, + 0x54, 0x52, 0x55, 0x4d, 0x5f, 0x55, 0x4e, 0x53, 0x49, 0x47, 0x4e, 0x45, 0x44, 0x10, 0x65, 0x12, + 0x1e, 0x0a, 0x1a, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x52, 0x42, 0x49, + 0x54, 0x52, 0x55, 0x4d, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x54, 0x10, 0x66, 0x12, + 0x1b, 0x0a, 0x17, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x52, 0x42, 0x49, + 0x54, 0x52, 0x55, 0x4d, 0x5f, 0x52, 0x45, 0x54, 0x52, 0x59, 0x10, 0x68, 0x12, 0x26, 0x0a, 0x22, + 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x52, 0x42, 0x49, 0x54, 0x52, 0x55, + 0x4d, 0x5f, 0x53, 0x55, 0x42, 0x4d, 0x49, 0x54, 0x5f, 0x52, 0x45, 0x54, 0x52, 0x59, 0x41, 0x42, + 0x4c, 0x45, 0x10, 0x69, 0x12, 0x1e, 0x0a, 0x1a, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x41, 0x52, 0x42, 0x49, 0x54, 0x52, 0x55, 0x4d, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, + 0x41, 0x4c, 0x10, 0x6a, 0x12, 0x1c, 0x0a, 0x18, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x41, 0x52, 0x42, 0x49, 0x54, 0x52, 0x55, 0x4d, 0x5f, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, + 0x10, 0x78, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x42, + 0x13, 0x0a, 0x11, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x66, 0x65, 0x65, + 0x5f, 0x63, 0x61, 0x70, 0x22, 0x4a, 0x0a, 0x0b, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x75, + 0x70, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x73, + 0x22, 0xc6, 0x02, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x47, + 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x73, 0x5f, 0x62, + 0x6c, 0x6f, 0x6f, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x73, + 0x42, 0x6c, 0x6f, 0x6f, 0x6d, 0x12, 0x2c, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, + 0x6f, 0x67, 0x73, 0x12, 0x27, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, + 0x75, 0x73, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x0b, 0x62, 0x6c, + 0x6f, 0x62, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x46, 0x0a, 0x0e, + 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, 0x6e, + 0x74, 0x48, 0x01, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x62, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, + 0x65, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, + 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, + 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x22, 0x9b, 0x01, 0x0a, 0x03, 0x4c, 0x6f, + 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x74, + 0x6f, 0x70, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x6f, 0x70, + 0x69, 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1e, 0x0a, + 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x18, 0x0a, + 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, + 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x22, 0xb2, 0x0a, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, + 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x70, + 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x64, 0x65, 0x70, 0x74, 0x68, 0x12, + 0x3a, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, + 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x61, 0x6c, + 0x6c, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x31, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, + 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, + 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x21, 0x0a, + 0x0c, 0x67, 0x61, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0b, 0x67, 0x61, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, + 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x73, 0x75, 0x69, 0x63, 0x69, 0x64, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, + 0x75, 0x69, 0x63, 0x69, 0x64, 0x65, 0x12, 0x59, 0x0a, 0x10, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, + 0x5f, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2e, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, + 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x2e, 0x4b, 0x65, 0x63, 0x63, + 0x61, 0x6b, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x0f, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x73, 0x12, 0x4b, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x66, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, + 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0e, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x4b, + 0x0a, 0x0f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0e, 0x62, 0x61, 0x6c, + 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x45, 0x0a, 0x0d, 0x6e, + 0x6f, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x18, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0c, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x19, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, + 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, + 0x12, 0x42, 0x0a, 0x0c, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x18, 0x1a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x64, + 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x0b, 0x67, 0x61, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x73, 0x18, 0x1c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x66, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, + 0x47, 0x61, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x67, 0x61, 0x73, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, + 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x18, 0x0c, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x76, 0x65, 0x72, + 0x74, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x66, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x65, + 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x69, 0x6e, + 0x61, 0x6c, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x4f, + 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x64, 0x5f, 0x6f, 0x72, + 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x20, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x6e, 0x64, + 0x4f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x51, 0x0a, 0x11, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x21, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x42, 0x0a, 0x14, 0x4b, 0x65, + 0x63, 0x63, 0x61, 0x6b, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, + 0x08, 0x1b, 0x10, 0x1c, 0x4a, 0x04, 0x08, 0x1d, 0x10, 0x1e, 0x4a, 0x04, 0x08, 0x32, 0x10, 0x33, + 0x4a, 0x04, 0x08, 0x33, 0x10, 0x34, 0x4a, 0x04, 0x08, 0x3c, 0x10, 0x3d, 0x22, 0x8f, 0x01, 0x0a, + 0x0d, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x6c, + 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6f, + 0x6c, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x22, 0xcc, + 0x05, 0x0a, 0x0d, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x6f, 0x6c, + 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x6c, 0x64, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, + 0x67, 0x49, 0x6e, 0x74, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x41, + 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, + 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, + 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x22, 0xcf, 0x03, 0x0a, 0x06, + 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, + 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x4d, 0x49, 0x4e, 0x45, + 0x5f, 0x55, 0x4e, 0x43, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x4d, 0x49, 0x4e, 0x45, 0x5f, 0x42, + 0x4c, 0x4f, 0x43, 0x4b, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x44, 0x41, 0x4f, 0x5f, 0x52, 0x45, 0x46, 0x55, 0x4e, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x54, + 0x52, 0x41, 0x43, 0x54, 0x10, 0x03, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x44, 0x41, 0x4f, 0x5f, 0x41, 0x44, 0x4a, 0x55, 0x53, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, + 0x4e, 0x43, 0x45, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x10, 0x05, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x45, + 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x53, 0x49, 0x53, 0x5f, 0x42, 0x41, 0x4c, + 0x41, 0x4e, 0x43, 0x45, 0x10, 0x06, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x47, 0x41, 0x53, 0x5f, 0x42, 0x55, 0x59, 0x10, 0x07, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, + 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x54, 0x52, 0x41, 0x4e, + 0x53, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x08, 0x12, 0x1b, 0x0a, + 0x17, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x46, + 0x45, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x54, 0x10, 0x0e, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, + 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x47, 0x41, 0x53, 0x5f, 0x52, 0x45, 0x46, 0x55, 0x4e, 0x44, 0x10, + 0x09, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x4f, 0x55, 0x43, + 0x48, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x0a, 0x12, 0x19, 0x0a, 0x15, 0x52, + 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x55, 0x49, 0x43, 0x49, 0x44, 0x45, 0x5f, 0x52, 0x45, + 0x46, 0x55, 0x4e, 0x44, 0x10, 0x0b, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x53, 0x55, 0x49, 0x43, 0x49, 0x44, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x44, 0x52, 0x41, + 0x57, 0x10, 0x0d, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, + 0x4c, 0x4c, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x52, + 0x49, 0x44, 0x45, 0x10, 0x0c, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x42, 0x55, 0x52, 0x4e, 0x10, 0x0f, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x57, 0x49, 0x54, 0x48, 0x44, 0x52, 0x41, 0x57, 0x41, 0x4c, 0x10, 0x10, 0x22, 0x7b, 0x0a, + 0x0b, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x6c, 0x64, 0x5f, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6f, 0x6c, 0x64, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x22, 0x45, 0x0a, 0x0f, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, + 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, + 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, + 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, + 0x6c, 0x22, 0xac, 0x01, 0x0a, 0x0a, 0x43, 0x6f, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x6c, + 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x6c, + 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x64, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x6c, 0x64, 0x43, 0x6f, 0x64, 0x65, + 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x48, 0x61, 0x73, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6e, + 0x65, 0x77, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6e, + 0x65, 0x77, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, + 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, + 0x22, 0xe0, 0x06, 0x0a, 0x09, 0x47, 0x61, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x6f, 0x6c, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x08, 0x6f, 0x6c, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, + 0x65, 0x77, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, + 0x6e, 0x65, 0x77, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3d, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x47, + 0x61, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, + 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, + 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, + 0x6c, 0x22, 0xbf, 0x05, 0x0a, 0x06, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x0e, + 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, + 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x10, + 0x01, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4c, 0x4c, + 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x41, 0x53, 0x4f, + 0x4e, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x43, 0x4f, 0x50, 0x59, + 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x44, + 0x45, 0x5f, 0x43, 0x4f, 0x50, 0x59, 0x10, 0x04, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x10, + 0x05, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x54, + 0x52, 0x41, 0x43, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x06, 0x12, + 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x41, + 0x43, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x32, 0x10, 0x07, 0x12, 0x18, + 0x0a, 0x14, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x47, 0x41, 0x54, + 0x45, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x10, 0x08, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x4c, 0x4f, 0x47, 0x10, 0x09, 0x12, 0x18, + 0x0a, 0x14, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x58, 0x54, 0x5f, 0x43, 0x4f, 0x44, + 0x45, 0x5f, 0x43, 0x4f, 0x50, 0x59, 0x10, 0x0a, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, + 0x49, 0x4f, 0x4e, 0x10, 0x0b, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x49, 0x4e, 0x54, 0x52, 0x49, 0x4e, 0x53, 0x49, 0x43, 0x5f, 0x47, 0x41, 0x53, 0x10, 0x0c, 0x12, + 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x45, 0x43, 0x4f, 0x4d, + 0x50, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x54, 0x10, 0x0d, + 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x46, 0x55, 0x4e, + 0x44, 0x5f, 0x41, 0x46, 0x54, 0x45, 0x52, 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x49, 0x4f, + 0x4e, 0x10, 0x0e, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x45, + 0x54, 0x55, 0x52, 0x4e, 0x10, 0x0f, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4e, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x43, 0x4f, 0x50, + 0x59, 0x10, 0x10, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x45, + 0x56, 0x45, 0x52, 0x54, 0x10, 0x11, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x44, 0x45, 0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x10, 0x12, + 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x49, + 0x43, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x10, 0x13, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x4f, 0x4c, 0x44, 0x5f, 0x41, 0x43, + 0x43, 0x45, 0x53, 0x53, 0x10, 0x14, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x54, 0x58, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x5f, 0x42, 0x41, 0x4c, 0x41, + 0x4e, 0x43, 0x45, 0x10, 0x15, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x54, 0x58, 0x5f, 0x52, 0x45, 0x46, 0x55, 0x4e, 0x44, 0x53, 0x10, 0x16, 0x12, 0x20, 0x0a, 0x1c, + 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x58, 0x5f, 0x4c, 0x45, 0x46, 0x54, 0x5f, 0x4f, + 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4e, 0x45, 0x44, 0x10, 0x17, 0x12, 0x1f, + 0x0a, 0x1b, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x5f, 0x49, 0x4e, + 0x49, 0x54, 0x49, 0x41, 0x4c, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x18, 0x12, + 0x22, 0x0a, 0x1e, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x5f, 0x4c, + 0x45, 0x46, 0x54, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4e, 0x45, + 0x44, 0x10, 0x19, 0x22, 0x4b, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4f, 0x6e, 0x6c, + 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x38, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x22, 0xd1, 0x01, 0x0a, 0x0d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x57, 0x69, 0x74, 0x68, 0x52, 0x65, + 0x66, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x30, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x5a, 0x0a, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x73, 0x52, 0x14, 0x74, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x65, 0x66, 0x73, + 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x72, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x69, 0x62, 0x6c, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x72, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x62, 0x6c, 0x65, 0x22, 0x97, 0x01, 0x0a, 0x1c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x57, 0x69, 0x74, 0x68, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x66, 0x12, 0x3b, 0x0a, 0x05, 0x74, 0x72, 0x61, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x66, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x65, 0x66, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x66, 0x22, 0x29, + 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0x36, 0x0a, 0x08, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x66, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x2a, 0x4e, 0x0a, 0x16, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, + 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, + 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, + 0x44, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x56, 0x45, 0x52, 0x54, 0x45, 0x44, 0x10, + 0x03, 0x2a, 0x59, 0x0a, 0x08, 0x43, 0x61, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, + 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x08, + 0x0a, 0x04, 0x43, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x41, 0x4c, 0x4c, + 0x43, 0x4f, 0x44, 0x45, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x45, 0x4c, 0x45, 0x47, 0x41, + 0x54, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x10, 0x04, + 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x05, 0x42, 0x4f, 0x5a, 0x4d, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x69, 0x6e, 0x67, 0x66, 0x61, 0x73, 0x74, 0x2f, 0x66, 0x69, 0x72, 0x65, 0x68, 0x6f, 0x73, + 0x65, 0x2d, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2f, 0x70, 0x62, 0x2f, 0x73, 0x66, 0x2f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, + 0x74, 0x79, 0x70, 0x65, 0x2f, 0x76, 0x32, 0x3b, 0x70, 0x62, 0x65, 0x74, 0x68, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_sf_ethereum_type_v2_type_proto_rawDescOnce sync.Once + file_sf_ethereum_type_v2_type_proto_rawDescData = file_sf_ethereum_type_v2_type_proto_rawDesc +) + +func file_sf_ethereum_type_v2_type_proto_rawDescGZIP() []byte { + file_sf_ethereum_type_v2_type_proto_rawDescOnce.Do(func() { + file_sf_ethereum_type_v2_type_proto_rawDescData = protoimpl.X.CompressGZIP(file_sf_ethereum_type_v2_type_proto_rawDescData) + }) + return file_sf_ethereum_type_v2_type_proto_rawDescData +} + +var file_sf_ethereum_type_v2_type_proto_enumTypes = make([]protoimpl.EnumInfo, 6) +var file_sf_ethereum_type_v2_type_proto_msgTypes = make([]protoimpl.MessageInfo, 22) +var file_sf_ethereum_type_v2_type_proto_goTypes = []interface{}{ + (TransactionTraceStatus)(0), // 0: sf.ethereum.type.v2.TransactionTraceStatus + (CallType)(0), // 1: sf.ethereum.type.v2.CallType + (Block_DetailLevel)(0), // 2: sf.ethereum.type.v2.Block.DetailLevel + (TransactionTrace_Type)(0), // 3: sf.ethereum.type.v2.TransactionTrace.Type + (BalanceChange_Reason)(0), // 4: sf.ethereum.type.v2.BalanceChange.Reason + (GasChange_Reason)(0), // 5: sf.ethereum.type.v2.GasChange.Reason + (*Block)(nil), // 6: sf.ethereum.type.v2.Block + (*BlockHeader)(nil), // 7: sf.ethereum.type.v2.BlockHeader + (*Uint64NestedArray)(nil), // 8: sf.ethereum.type.v2.Uint64NestedArray + (*Uint64Array)(nil), // 9: sf.ethereum.type.v2.Uint64Array + (*BigInt)(nil), // 10: sf.ethereum.type.v2.BigInt + (*TransactionTrace)(nil), // 11: sf.ethereum.type.v2.TransactionTrace + (*AccessTuple)(nil), // 12: sf.ethereum.type.v2.AccessTuple + (*TransactionReceipt)(nil), // 13: sf.ethereum.type.v2.TransactionReceipt + (*Log)(nil), // 14: sf.ethereum.type.v2.Log + (*Call)(nil), // 15: sf.ethereum.type.v2.Call + (*StorageChange)(nil), // 16: sf.ethereum.type.v2.StorageChange + (*BalanceChange)(nil), // 17: sf.ethereum.type.v2.BalanceChange + (*NonceChange)(nil), // 18: sf.ethereum.type.v2.NonceChange + (*AccountCreation)(nil), // 19: sf.ethereum.type.v2.AccountCreation + (*CodeChange)(nil), // 20: sf.ethereum.type.v2.CodeChange + (*GasChange)(nil), // 21: sf.ethereum.type.v2.GasChange + (*HeaderOnlyBlock)(nil), // 22: sf.ethereum.type.v2.HeaderOnlyBlock + (*BlockWithRefs)(nil), // 23: sf.ethereum.type.v2.BlockWithRefs + (*TransactionTraceWithBlockRef)(nil), // 24: sf.ethereum.type.v2.TransactionTraceWithBlockRef + (*TransactionRefs)(nil), // 25: sf.ethereum.type.v2.TransactionRefs + (*BlockRef)(nil), // 26: sf.ethereum.type.v2.BlockRef + nil, // 27: sf.ethereum.type.v2.Call.KeccakPreimagesEntry + (*timestamppb.Timestamp)(nil), // 28: google.protobuf.Timestamp +} +var file_sf_ethereum_type_v2_type_proto_depIdxs = []int32{ + 7, // 0: sf.ethereum.type.v2.Block.header:type_name -> sf.ethereum.type.v2.BlockHeader + 7, // 1: sf.ethereum.type.v2.Block.uncles:type_name -> sf.ethereum.type.v2.BlockHeader + 11, // 2: sf.ethereum.type.v2.Block.transaction_traces:type_name -> sf.ethereum.type.v2.TransactionTrace + 17, // 3: sf.ethereum.type.v2.Block.balance_changes:type_name -> sf.ethereum.type.v2.BalanceChange + 2, // 4: sf.ethereum.type.v2.Block.detail_level:type_name -> sf.ethereum.type.v2.Block.DetailLevel + 20, // 5: sf.ethereum.type.v2.Block.code_changes:type_name -> sf.ethereum.type.v2.CodeChange + 15, // 6: sf.ethereum.type.v2.Block.system_calls:type_name -> sf.ethereum.type.v2.Call + 10, // 7: sf.ethereum.type.v2.BlockHeader.difficulty:type_name -> sf.ethereum.type.v2.BigInt + 10, // 8: sf.ethereum.type.v2.BlockHeader.total_difficulty:type_name -> sf.ethereum.type.v2.BigInt + 28, // 9: sf.ethereum.type.v2.BlockHeader.timestamp:type_name -> google.protobuf.Timestamp + 10, // 10: sf.ethereum.type.v2.BlockHeader.base_fee_per_gas:type_name -> sf.ethereum.type.v2.BigInt + 8, // 11: sf.ethereum.type.v2.BlockHeader.tx_dependency:type_name -> sf.ethereum.type.v2.Uint64NestedArray + 9, // 12: sf.ethereum.type.v2.Uint64NestedArray.val:type_name -> sf.ethereum.type.v2.Uint64Array + 10, // 13: sf.ethereum.type.v2.TransactionTrace.gas_price:type_name -> sf.ethereum.type.v2.BigInt + 10, // 14: sf.ethereum.type.v2.TransactionTrace.value:type_name -> sf.ethereum.type.v2.BigInt + 3, // 15: sf.ethereum.type.v2.TransactionTrace.type:type_name -> sf.ethereum.type.v2.TransactionTrace.Type + 12, // 16: sf.ethereum.type.v2.TransactionTrace.access_list:type_name -> sf.ethereum.type.v2.AccessTuple + 10, // 17: sf.ethereum.type.v2.TransactionTrace.max_fee_per_gas:type_name -> sf.ethereum.type.v2.BigInt + 10, // 18: sf.ethereum.type.v2.TransactionTrace.max_priority_fee_per_gas:type_name -> sf.ethereum.type.v2.BigInt + 0, // 19: sf.ethereum.type.v2.TransactionTrace.status:type_name -> sf.ethereum.type.v2.TransactionTraceStatus + 13, // 20: sf.ethereum.type.v2.TransactionTrace.receipt:type_name -> sf.ethereum.type.v2.TransactionReceipt + 15, // 21: sf.ethereum.type.v2.TransactionTrace.calls:type_name -> sf.ethereum.type.v2.Call + 10, // 22: sf.ethereum.type.v2.TransactionTrace.blob_gas_fee_cap:type_name -> sf.ethereum.type.v2.BigInt + 14, // 23: sf.ethereum.type.v2.TransactionReceipt.logs:type_name -> sf.ethereum.type.v2.Log + 10, // 24: sf.ethereum.type.v2.TransactionReceipt.blob_gas_price:type_name -> sf.ethereum.type.v2.BigInt + 1, // 25: sf.ethereum.type.v2.Call.call_type:type_name -> sf.ethereum.type.v2.CallType + 10, // 26: sf.ethereum.type.v2.Call.value:type_name -> sf.ethereum.type.v2.BigInt + 27, // 27: sf.ethereum.type.v2.Call.keccak_preimages:type_name -> sf.ethereum.type.v2.Call.KeccakPreimagesEntry + 16, // 28: sf.ethereum.type.v2.Call.storage_changes:type_name -> sf.ethereum.type.v2.StorageChange + 17, // 29: sf.ethereum.type.v2.Call.balance_changes:type_name -> sf.ethereum.type.v2.BalanceChange + 18, // 30: sf.ethereum.type.v2.Call.nonce_changes:type_name -> sf.ethereum.type.v2.NonceChange + 14, // 31: sf.ethereum.type.v2.Call.logs:type_name -> sf.ethereum.type.v2.Log + 20, // 32: sf.ethereum.type.v2.Call.code_changes:type_name -> sf.ethereum.type.v2.CodeChange + 21, // 33: sf.ethereum.type.v2.Call.gas_changes:type_name -> sf.ethereum.type.v2.GasChange + 19, // 34: sf.ethereum.type.v2.Call.account_creations:type_name -> sf.ethereum.type.v2.AccountCreation + 10, // 35: sf.ethereum.type.v2.BalanceChange.old_value:type_name -> sf.ethereum.type.v2.BigInt + 10, // 36: sf.ethereum.type.v2.BalanceChange.new_value:type_name -> sf.ethereum.type.v2.BigInt + 4, // 37: sf.ethereum.type.v2.BalanceChange.reason:type_name -> sf.ethereum.type.v2.BalanceChange.Reason + 5, // 38: sf.ethereum.type.v2.GasChange.reason:type_name -> sf.ethereum.type.v2.GasChange.Reason + 7, // 39: sf.ethereum.type.v2.HeaderOnlyBlock.header:type_name -> sf.ethereum.type.v2.BlockHeader + 6, // 40: sf.ethereum.type.v2.BlockWithRefs.block:type_name -> sf.ethereum.type.v2.Block + 25, // 41: sf.ethereum.type.v2.BlockWithRefs.transaction_trace_refs:type_name -> sf.ethereum.type.v2.TransactionRefs + 11, // 42: sf.ethereum.type.v2.TransactionTraceWithBlockRef.trace:type_name -> sf.ethereum.type.v2.TransactionTrace + 26, // 43: sf.ethereum.type.v2.TransactionTraceWithBlockRef.block_ref:type_name -> sf.ethereum.type.v2.BlockRef + 44, // [44:44] is the sub-list for method output_type + 44, // [44:44] is the sub-list for method input_type + 44, // [44:44] is the sub-list for extension type_name + 44, // [44:44] is the sub-list for extension extendee + 0, // [0:44] is the sub-list for field type_name +} + +func init() { file_sf_ethereum_type_v2_type_proto_init() } +func file_sf_ethereum_type_v2_type_proto_init() { + if File_sf_ethereum_type_v2_type_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_sf_ethereum_type_v2_type_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Block); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlockHeader); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Uint64NestedArray); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Uint64Array); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BigInt); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionTrace); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AccessTuple); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionReceipt); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Log); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Call); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StorageChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BalanceChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NonceChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AccountCreation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CodeChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GasChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HeaderOnlyBlock); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlockWithRefs); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionTraceWithBlockRef); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionRefs); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlockRef); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[1].OneofWrappers = []interface{}{} + file_sf_ethereum_type_v2_type_proto_msgTypes[5].OneofWrappers = []interface{}{} + file_sf_ethereum_type_v2_type_proto_msgTypes[7].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_sf_ethereum_type_v2_type_proto_rawDesc, + NumEnums: 6, + NumMessages: 22, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_sf_ethereum_type_v2_type_proto_goTypes, + DependencyIndexes: file_sf_ethereum_type_v2_type_proto_depIdxs, + EnumInfos: file_sf_ethereum_type_v2_type_proto_enumTypes, + MessageInfos: file_sf_ethereum_type_v2_type_proto_msgTypes, + }.Build() + File_sf_ethereum_type_v2_type_proto = out.File + file_sf_ethereum_type_v2_type_proto_rawDesc = nil + file_sf_ethereum_type_v2_type_proto_goTypes = nil + file_sf_ethereum_type_v2_type_proto_depIdxs = nil +} diff --git a/firehose/testdata/firehose/reorder-ordinals-empty.golden.json b/firehose/testdata/firehose/reorder-ordinals-empty.golden.json new file mode 100755 index 0000000000..b2ac4a9e58 --- /dev/null +++ b/firehose/testdata/firehose/reorder-ordinals-empty.golden.json @@ -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 +} \ No newline at end of file diff --git a/go.mod b/go.mod index 492d8c7624..a9dd5a14fc 100644 --- a/go.mod +++ b/go.mod @@ -50,6 +50,7 @@ require ( github.com/tidwall/gjson v1.17.1 github.com/tidwall/sjson v1.2.5 github.com/tyler-smith/go-bip39 v1.1.0 + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 golang.org/x/net v0.27.0 golang.org/x/sync v0.7.0 golang.org/x/text v0.16.0 @@ -250,7 +251,6 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.25.0 // indirect - golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/term v0.22.0 // indirect diff --git a/server/config/config.go b/server/config/config.go index 8e9167719b..27c9e4bd5e 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -106,7 +106,7 @@ var ( // DefaultRosettaGasPrices defines the default list of prices to suggest DefaultRosettaGasPrices = sdk.NewDecCoins(sdk.NewDecCoin(DefaultRosettaDenomToSuggest, sdkmath.NewInt(4_000_000))) - evmTracers = []string{"json", "markdown", "struct", "access_list"} + evmTracers = []string{"json", "markdown", "struct", "access_list", "firehose"} blockExecutors = []string{BlockExecutorSequential, BlockExecutorBlockSTM} ) diff --git a/server/config/toml.go b/server/config/toml.go index b82d492c7f..d01ec80a04 100644 --- a/server/config/toml.go +++ b/server/config/toml.go @@ -25,7 +25,7 @@ const DefaultConfigTemplate = ` # Tracer defines the 'vm.Tracer' type that the EVM will use when the node is run in # debug mode. To enable tracing use the '--evm.tracer' flag when starting your node. -# Valid types are: json|struct|access_list|markdown +# Valid types are: json|struct|access_list|markdown|firehose tracer = "{{ .EVM.Tracer }}" # MaxTxGasWanted defines the gas wanted for each eth tx returned in ante handler in check tx mode. diff --git a/x/evm/types/tracer.go b/x/evm/types/tracer.go index 4e0e83bb2a..a4ce30ba7c 100644 --- a/x/evm/types/tracer.go +++ b/x/evm/types/tracer.go @@ -19,7 +19,10 @@ import ( "os" "github.com/ethereum/go-ethereum/eth/tracers" + _ "github.com/ethereum/go-ethereum/eth/tracers/live" + _ "github.com/evmos/ethermint/firehose" + "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/core" @@ -33,6 +36,7 @@ const ( TracerJSON = "json" TracerStruct = "struct" TracerMarkdown = "markdown" + Firehose = "firehose" ) // NewTracer creates a new Logger tracer to collect execution traces from an @@ -55,6 +59,8 @@ func NewTracer(tracer string, msg *core.Message, rules params.Rules) *tracers.Tr hooks = logger.NewMarkdownLogger(logCfg, os.Stdout).Hooks() // TODO: Stderr ? case TracerStruct: hooks = logger.NewStructLogger(logCfg).Hooks() + case Firehose: + hooks, _ = tracers.LiveDirectory.New("firehose", nil) default: hooks, _ = tracers.LiveDirectory.New("noop", nil) } From b5b917d02631ba5004129f66e7b0d7bf959c3ee4 Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Mon, 23 Sep 2024 09:37:21 +0200 Subject: [PATCH 02/45] chore: add firehose test case in TestApplyMessageWithConfig --- x/evm/keeper/state_transition_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 282f6a66a0..e99329ec5a 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -673,6 +673,26 @@ func (suite *StateTransitionTestSuite) TestApplyMessageWithConfig() { }, false, }, + { + "messsage applied with firehose tracer", + func() { + msg, err = newNativeMessage( + vmdb.GetNonce(suite.Address), + suite.Ctx.BlockHeight(), + suite.Address, + chainCfg, + suite.Signer, + signer, + ethtypes.AccessListTxType, + nil, + nil, + ) + suite.Require().NoError(err) + + config.Tracer = types.NewTracer("firehose", msg, params.Rules{}) + }, + false, + }, { "call contract tx with config param EnableCall = false", func() { From 8a39df1c38505daa61c162a4d6a2a145acc8deaa Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Mon, 23 Sep 2024 09:58:01 +0200 Subject: [PATCH 03/45] chore: fix firehose test case --- x/evm/keeper/state_transition_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index e99329ec5a..e4777cf3d8 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -18,6 +18,7 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/tracing" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" @@ -738,6 +739,24 @@ func (suite *StateTransitionTestSuite) TestApplyMessageWithConfig() { config.TxConfig = suite.App.EvmKeeper.TxConfig(suite.Ctx, common.Hash{}) tc.malleate() + + if config.Tracer != nil { + config.Tracer.OnBlockchainInit(params.AllEthashProtocolChanges) + config.Tracer.OnBlockStart(tracing.BlockEvent{ + Block: ethtypes.NewBlockWithHeader( + ðtypes.Header{ + Number: big.NewInt(suite.Ctx.BlockHeight()), + Time: uint64(suite.Ctx.BlockHeader().Time.Unix()), + Difficulty: big.NewInt(0), + }, + ), + TD: big.NewInt(0), + }) + defer func() { + config.Tracer.OnBlockEnd(nil) + }() + } + res, err := suite.App.EvmKeeper.ApplyMessageWithConfig(suite.Ctx, msg, config, true) if tc.expErr { From 279bd4280d306044d36aeae0af7fdf6bbff09e31 Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Mon, 23 Sep 2024 11:50:22 +0200 Subject: [PATCH 04/45] chore: make firehose the default tracer This breaks some tests and shows that we need to add calls to OnBlockStart to prevent panics. --- x/evm/keeper/msg_server_test.go | 2 ++ x/evm/types/tracer.go | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/x/evm/keeper/msg_server_test.go b/x/evm/keeper/msg_server_test.go index a835e3c359..9fb242ebd9 100644 --- a/x/evm/keeper/msg_server_test.go +++ b/x/evm/keeper/msg_server_test.go @@ -78,11 +78,13 @@ func (suite *MsgServerTestSuite) TestEthereumTx() { vmdb = suite.StateDB() tc.malleate() + res, err := suite.App.EvmKeeper.EthereumTx(suite.Ctx, msg) if tc.expErr { suite.Require().Error(err) return } + suite.Require().NoError(err) suite.Require().Equal(expectedGasUsed, res.GasUsed) suite.Require().False(res.Failed()) diff --git a/x/evm/types/tracer.go b/x/evm/types/tracer.go index a4ce30ba7c..d02f746938 100644 --- a/x/evm/types/tracer.go +++ b/x/evm/types/tracer.go @@ -62,7 +62,9 @@ func NewTracer(tracer string, msg *core.Message, rules params.Rules) *tracers.Tr case Firehose: hooks, _ = tracers.LiveDirectory.New("firehose", nil) default: - hooks, _ = tracers.LiveDirectory.New("noop", nil) + // XXX + // hooks, _ = tracers.LiveDirectory.New("noop", nil) + hooks, _ = tracers.LiveDirectory.New("firehose", nil) } return &tracers.Tracer{ From f3b5e0bdb9613b1631b1623323a9b661d2e6dd22 Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Mon, 30 Sep 2024 09:47:52 +0200 Subject: [PATCH 05/45] chore: cleanup comment about default tracer --- x/evm/types/tracer.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/x/evm/types/tracer.go b/x/evm/types/tracer.go index d02f746938..ca55e38830 100644 --- a/x/evm/types/tracer.go +++ b/x/evm/types/tracer.go @@ -62,9 +62,12 @@ func NewTracer(tracer string, msg *core.Message, rules params.Rules) *tracers.Tr case Firehose: hooks, _ = tracers.LiveDirectory.New("firehose", nil) default: - // XXX - // hooks, _ = tracers.LiveDirectory.New("noop", nil) hooks, _ = tracers.LiveDirectory.New("firehose", nil) + // TODO use noop as default + // current default is firehose because it uncovers issues when running + // unittests + // hooks, _ = tracers.LiveDirectory.New("noop", nil) + } return &tracers.Tracer{ From 2084cda7f6f16944613e14383000614112111453 Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Mon, 30 Sep 2024 10:01:55 +0200 Subject: [PATCH 06/45] chore: move test case with firehose tracer --- x/evm/keeper/state_transition_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index e4777cf3d8..2ae5b0f612 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -657,7 +657,7 @@ func (suite *StateTransitionTestSuite) TestApplyMessageWithConfig() { expErr bool }{ { - "messsage applied ok", + "messsage applied with firehose tracer", func() { msg, err = newNativeMessage( vmdb.GetNonce(suite.Address), @@ -671,11 +671,13 @@ func (suite *StateTransitionTestSuite) TestApplyMessageWithConfig() { nil, ) suite.Require().NoError(err) + + config.Tracer = types.NewTracer("firehose", msg, params.Rules{}) }, false, }, { - "messsage applied with firehose tracer", + "messsage applied ok", func() { msg, err = newNativeMessage( vmdb.GetNonce(suite.Address), @@ -689,8 +691,6 @@ func (suite *StateTransitionTestSuite) TestApplyMessageWithConfig() { nil, ) suite.Require().NoError(err) - - config.Tracer = types.NewTracer("firehose", msg, params.Rules{}) }, false, }, From f19c3c0c4f8d39d0c421055454c90c4c8427a248 Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Thu, 3 Oct 2024 13:51:58 -0400 Subject: [PATCH 07/45] - add firehose evm tracer hooks - move some files around to keep them contained inside of the x/evm package - adding env file ignore in gitignore --- .gitignore | 3 + app/app.go | 66 ++++++++++++++++++- go.mod | 18 +++-- go.sum | 57 +++++++--------- .../pb => pb}/sf/ethereum/type/v2/type.go | 0 .../pb => pb}/sf/ethereum/type/v2/type.pb.go | 0 x/evm/genesis.go | 2 + x/evm/handler_test.go | 3 +- x/evm/keeper/abci.go | 35 ++++++++++ x/evm/keeper/config.go | 19 ++++-- x/evm/keeper/grpc_query_test.go | 7 +- x/evm/keeper/integration_test.go | 6 +- x/evm/keeper/keeper.go | 13 +++- x/evm/keeper/state_transition.go | 48 ++++++++++---- x/evm/keeper/state_transition_test.go | 9 +-- x/evm/statedb/statedb.go | 65 ++++++++++++++++-- x/evm/statedb/statedb_test.go | 2 + {firehose => x/evm/tracers}/firehose.go | 50 ++++++++++---- {firehose => x/evm/tracers}/firehose_test.go | 2 +- .../reorder-ordinals-empty.golden.json | 0 x/evm/types/tracer.go | 9 +-- x/evm/types/utils.go | 2 +- 22 files changed, 322 insertions(+), 94 deletions(-) rename {firehose/pb => pb}/sf/ethereum/type/v2/type.go (100%) rename {firehose/pb => pb}/sf/ethereum/type/v2/type.pb.go (100%) rename {firehose => x/evm/tracers}/firehose.go (97%) rename {firehose => x/evm/tracers}/firehose_test.go (99%) rename {firehose => x/evm/tracers}/testdata/firehose/reorder-ordinals-empty.golden.json (100%) diff --git a/.gitignore b/.gitignore index 4b1097261f..c78e944ec9 100644 --- a/.gitignore +++ b/.gitignore @@ -84,3 +84,6 @@ contracts/@openzeppelin/* # direnv /.envrc /.direnv + +# env file +.env diff --git a/app/app.go b/app/app.go index e4ade45fef..72f276b297 100644 --- a/app/app.go +++ b/app/app.go @@ -18,8 +18,10 @@ package app import ( "encoding/json" "fmt" + ethparams "github.com/ethereum/go-ethereum/params" "io" "io/fs" + "math/big" "net/http" "os" "path/filepath" @@ -39,6 +41,7 @@ import ( "cosmossdk.io/log" abci "github.com/cometbft/cometbft/abci/types" tmos "github.com/cometbft/cometbft/libs/os" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/cosmos-sdk/baseapp" @@ -115,7 +118,6 @@ import ( "github.com/cosmos/ibc-go/modules/capability" capabilitykeeper "github.com/cosmos/ibc-go/modules/capability/keeper" capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" - "github.com/cosmos/ibc-go/v8/modules/apps/transfer" ibctransferkeeper "github.com/cosmos/ibc-go/v8/modules/apps/transfer/keeper" ibctransfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" @@ -126,7 +128,10 @@ import ( ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" ibckeeper "github.com/cosmos/ibc-go/v8/modules/core/keeper" ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" - + ethcommon "github.com/ethereum/go-ethereum/common" + ethhexutil "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/tracers" "github.com/evmos/ethermint/client/docs" "github.com/evmos/ethermint/app/ante" @@ -241,6 +246,9 @@ type EthermintApp struct { EvmKeeper *evmkeeper.Keeper FeeMarketKeeper feemarketkeeper.Keeper + // EVMTracer + EvmTracer *tracers.Tracer + // the module manager ModuleManager *module.Manager BasicModuleManager module.BasicManager @@ -480,6 +488,7 @@ func NewEthermintApp( authAddr, ) + // Firehose tracer will be set downstream by the srvflag evm tracer firehose tracer := cast.ToString(appOpts.Get(srvflags.EVMTracer)) // Create Ethermint keepers @@ -502,6 +511,15 @@ func NewEthermintApp( nil, ) + if app.EvmTracer == nil { + if tracer == evmtypes.TracerAccessList { + panic("Ethermint App with tracer value 'access-list' is not supported") + } + + app.EvmTracer = evmtypes.NewTracer(tracer, nil, ethparams.Rules{}) + app.EvmKeeper.SetTracer(app.EvmTracer) + } + // register the proposal types govRouter := govv1beta1.NewRouter() govRouter.AddRoute(govtypes.RouterKey, govv1beta1.ProposalHandler). @@ -877,6 +895,10 @@ func (app *EthermintApp) InitChainer(ctx sdk.Context, req *abci.RequestInitChain if err := app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap()); err != nil { return nil, err } + if app.EvmTracer != nil && app.EvmTracer.OnBlockchainInit != nil { + app.EvmKeeper.WithChainID(ctx) + app.EvmTracer.OnBlockchainInit(evmtypes.DefaultChainConfig().EthereumConfig(app.EvmKeeper.ChainID())) + } return app.ModuleManager.InitGenesis(ctx, app.appCodec, genesisState) } @@ -1112,3 +1134,43 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino paramsKeeper.Subspace(feemarkettypes.ModuleName).WithKeyTable(feemarkettypes.ParamKeyTable()) return paramsKeeper } + +// Call this method in to OnBlockStart call +func TmBlockHeaderToEVM( + ctx sdk.Context, + block tmproto.Header, + k *evmkeeper.Keeper, +) (header *ethtypes.Header) { + number := big.NewInt(block.Height) + lastHash := ethcommon.BytesToHash(block.LastBlockId.Hash) + appHash := ethcommon.BytesToHash(block.AppHash) + txHash := ethcommon.BytesToHash(block.DataHash) + resultHash := ethcommon.BytesToHash(block.LastResultsHash) + miner := ethcommon.BytesToAddress(block.ProposerAddress) + gasLimit, gasWanted := uint64(0), uint64(0) + + header = ðtypes.Header{ + Number: number, + ParentHash: lastHash, + Nonce: ethtypes.BlockNonce{}, // todo: check if this applies to injective + MixDigest: ethcommon.Hash{}, // todo: check if this applies to injective + UncleHash: ethtypes.EmptyUncleHash, // todo: check if this applies to injective + Bloom: ethtypes.Bloom{}, // k.GetBlockBloom(ctx, block.Height), // todo: check how to implement this + Root: appHash, + Coinbase: miner, + Difficulty: big.NewInt(0), // todo: check if this applies to injective + Extra: ethhexutil.Bytes{}, // todo: check if this applies to injective + GasLimit: gasLimit, + GasUsed: gasWanted, + Time: uint64(block.Time.Unix()), + TxHash: txHash, + ReceiptHash: resultHash, + BaseFee: nil, // k.GetBaseFeePerGas(ctx).RoundInt().BigInt(), // todo: check how to implement this + } + + return +} + +func ptr[T any](t T) *T { + return &t +} diff --git a/go.mod b/go.mod index a9dd5a14fc..bd10294684 100644 --- a/go.mod +++ b/go.mod @@ -51,9 +51,9 @@ require ( github.com/tidwall/sjson v1.2.5 github.com/tyler-smith/go-bip39 v1.1.0 golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 - golang.org/x/net v0.27.0 - golang.org/x/sync v0.7.0 - golang.org/x/text v0.16.0 + golang.org/x/net v0.29.0 + golang.org/x/sync v0.8.0 + golang.org/x/text v0.18.0 google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 @@ -96,6 +96,7 @@ require ( github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/cockroachdb/errors v1.11.3 // indirect @@ -188,6 +189,8 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/klauspost/compress v1.17.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/knz/go-libedit v1.10.1 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/lib/pq v1.10.7 // indirect @@ -207,7 +210,7 @@ require ( github.com/oklog/run v1.1.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/outcaste-io/ristretto v0.2.3 // indirect - github.com/pelletier/go-toml/v2 v2.1.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 // indirect github.com/philhofer/fwd v1.1.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -250,10 +253,10 @@ require ( go.opentelemetry.io/otel/trace v1.24.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.25.0 // indirect + golang.org/x/crypto v0.27.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/term v0.22.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/term v0.24.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.22.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect @@ -278,4 +281,5 @@ replace ( github.com/cometbft/cometbft => github.com/InjectiveLabs/cometbft v0.38.11-inj-2 github.com/cosmos/cosmos-sdk => github.com/InjectiveLabs/cosmos-sdk v0.50.9-0.20240904140803-b4127ecb5410 github.com/ethereum/go-ethereum => github.com/InjectiveLabs/go-ethereum v1.9.22-0.20240923100242-5e28e23d353e + nhooyr.io/websocket => github.com/coder/websocket v1.8.10 // replaced as instructed here:https://coder.com/blog/websocket ) diff --git a/go.sum b/go.sum index 35f460c099..8a58d44395 100644 --- a/go.sum +++ b/go.sum @@ -353,6 +353,8 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= +github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= @@ -396,6 +398,8 @@ github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coder/websocket v1.8.10 h1:K+NrQte1lq04N7V/E3avmuuuCGEaInbjTWukHZsN17g= +github.com/coder/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= github.com/coinbase/rosetta-sdk-go/types v1.0.0 h1:jpVIwLcPoOeCR6o1tU+Xv7r5bMONNbHU7MuEHboiFuA= github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c= github.com/cometbft/cometbft-db v0.12.0 h1:v77/z0VyfSU7k682IzZeZPFZrQAKiQwkqGN0QzAjMi0= @@ -539,8 +543,6 @@ github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -567,10 +569,6 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= @@ -578,9 +576,6 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -731,7 +726,6 @@ github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -860,11 +854,15 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1 h1:0pHpWtx9vcvC0xGZqEQlQdfSQs7WRlAjuPvk3fOZDCo= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -881,7 +879,6 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= @@ -1007,8 +1004,8 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= -github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 h1:jik8PHtAIsPlCRJjJzl4udgEf7hawInF9texMeO2jrU= @@ -1147,7 +1144,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -1181,8 +1177,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= @@ -1262,8 +1256,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1366,8 +1360,8 @@ golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfS golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1410,8 +1404,8 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1509,19 +1503,20 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1534,8 +1529,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1900,9 +1895,7 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q= -nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/firehose/pb/sf/ethereum/type/v2/type.go b/pb/sf/ethereum/type/v2/type.go similarity index 100% rename from firehose/pb/sf/ethereum/type/v2/type.go rename to pb/sf/ethereum/type/v2/type.go diff --git a/firehose/pb/sf/ethereum/type/v2/type.pb.go b/pb/sf/ethereum/type/v2/type.pb.go similarity index 100% rename from firehose/pb/sf/ethereum/type/v2/type.pb.go rename to pb/sf/ethereum/type/v2/type.pb.go diff --git a/x/evm/genesis.go b/x/evm/genesis.go index db3799d35b..1aa6edf16a 100644 --- a/x/evm/genesis.go +++ b/x/evm/genesis.go @@ -82,6 +82,8 @@ func InitGenesis( } } + // todo: should we call FHTracer.OnGenesisBlock here? + return []abci.ValidatorUpdate{} } diff --git a/x/evm/handler_test.go b/x/evm/handler_test.go index d0e97c5152..af038088a1 100644 --- a/x/evm/handler_test.go +++ b/x/evm/handler_test.go @@ -104,7 +104,7 @@ func (suite *HandlerTestSuite) TestHandleMsgEthereumTx() { true, }, { - "insufficient balance", + "insufficient balance", // todo: shouldn't this be insufficient gas func() { tx = types.NewTxContract(suite.chainID, 0, big.NewInt(100), 0, big.NewInt(10000), nil, nil, nil, nil) suite.signTx(tx) @@ -136,6 +136,7 @@ func (suite *HandlerTestSuite) TestHandleMsgEthereumTx() { for _, tc := range testCases { suite.Run(tc.msg, func() { + // todo: it seems we are missing a hook on a balance change? or something like that? suite.SetupTest() // reset //nolint tc.malleate() diff --git a/x/evm/keeper/abci.go b/x/evm/keeper/abci.go index d890eb53f3..692dcf378d 100644 --- a/x/evm/keeper/abci.go +++ b/x/evm/keeper/abci.go @@ -17,6 +17,10 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "math/big" ) // BeginBlock sets the sdk Context and EIP155 chain id to the Keeper. @@ -28,6 +32,26 @@ func (k *Keeper) BeginBlock(ctx sdk.Context) error { return err } + cfg, err := k.EVMConfig(ctx, k.ChainID(), common.Hash{}) + if err != nil { + return err + } + + if cfg.Tracer != nil { + b := types.NewBlock(&types.Header{ + Number: big.NewInt(ctx.BlockHeight()), + Time: uint64(ctx.BlockTime().Unix()), + }, nil, nil, nil) + + cfg.Tracer.OnBlockStart(tracing.BlockEvent{ + Block: b, + TD: big.NewInt(1), + // Finalized: , // todo: how to set up the header here? + // Safe: , // todo: how to set up the header here? + }) + + } + return nil } @@ -37,5 +61,16 @@ func (k *Keeper) BeginBlock(ctx sdk.Context) error { func (k *Keeper) EndBlock(ctx sdk.Context) error { k.CollectTxBloom(ctx) k.RemoveParamsCache(ctx) + + // TODO: call EndBlock on Tracer + //config, err := k.EVMConfig(ctx, k.ChainID(), common.Hash{}) + //if err != nil { + // return err + //} + // + //defer func() { + // config.Tracer.OnBlockEnd(nil) + //}() + return nil } diff --git a/x/evm/keeper/config.go b/x/evm/keeper/config.go index 113d8b27cb..fc00a823ad 100644 --- a/x/evm/keeper/config.go +++ b/x/evm/keeper/config.go @@ -126,10 +126,16 @@ func (k *Keeper) EVMConfig(ctx sdk.Context, chainID *big.Int, txHash common.Hash txConfig = k.TxConfig(ctx, txHash) } - return &EVMConfig{ + cfg := &EVMConfig{ EVMBlockConfig: blockCfg, TxConfig: txConfig, - }, nil + } + + if k.evmTracer != nil { + cfg.Tracer = k.evmTracer + } + + return cfg, nil } // TxConfig loads `TxConfig` from current transient storage @@ -149,9 +155,14 @@ func (k Keeper) VMConfig(ctx sdk.Context, cfg *EVMConfig) vm.Config { noBaseFee = cfg.FeeMarketParams.NoBaseFee } - return vm.Config{ - Tracer: cfg.Tracer.Hooks, + vmCfg := vm.Config{ NoBaseFee: noBaseFee, ExtraEips: cfg.Params.EIPs(), } + + if vmCfg.Tracer == nil && cfg.Tracer != nil && cfg.Tracer.Hooks != nil { + vmCfg.Tracer = cfg.Tracer.Hooks + } + + return vmCfg } diff --git a/x/evm/keeper/grpc_query_test.go b/x/evm/keeper/grpc_query_test.go index fb4b52f4c9..0816ad06b6 100644 --- a/x/evm/keeper/grpc_query_test.go +++ b/x/evm/keeper/grpc_query_test.go @@ -1043,7 +1043,7 @@ func (suite *GRPCServerTestSuiteSuite) TestTraceTx() { res, err := suite.EvmQueryClient.TraceTx(suite.Ctx, &traceReq) if tc.expPass { suite.Require().NoError(err) - // if data is to big, slice the result + // if data is too big, slice the result if len(res.Data) > 150 { suite.Require().Equal(tc.traceResponse, string(res.Data[:150])) } else { @@ -1200,7 +1200,8 @@ func (suite *GRPCServerTestSuiteSuite) TestTraceBlock() { // Deploy contract contractAddr := suite.deployTestContract(suite.Address) // set some balance to handle fees - suite.App.EvmKeeper.SetBalance(suite.Ctx, suite.Address, big.NewInt(1000000000000000000), types.DefaultEVMDenom) + err := suite.App.EvmKeeper.SetBalance(suite.Ctx, suite.Address, big.NewInt(1000000000000000000), types.DefaultEVMDenom) + suite.Require().NoError(err) suite.Commit(suite.T()) // Generate token transfer transaction txMsg := suite.transferERC20Token(suite.T(), contractAddr, suite.Address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt()) @@ -1220,7 +1221,7 @@ func (suite *GRPCServerTestSuiteSuite) TestTraceBlock() { res, err := suite.EvmQueryClient.TraceBlock(suite.Ctx, &traceReq) if tc.expPass { suite.Require().NoError(err) - // if data is to big, slice the result + // if data is too big, slice the result if len(res.Data) > 150 { suite.Require().Equal(tc.traceResponse, string(res.Data[:150])) } else { diff --git a/x/evm/keeper/integration_test.go b/x/evm/keeper/integration_test.go index baac6ce588..22156b6ed9 100644 --- a/x/evm/keeper/integration_test.go +++ b/x/evm/keeper/integration_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + srvflags "github.com/evmos/ethermint/server/flags" "math/big" "testing" @@ -136,7 +137,10 @@ func setupTest(minGasPrice sdkmath.LegacyDec, baseFee *big.Int) { genesis[feemarkettypes.ModuleName] = app.AppCodec().MustMarshalJSON(feemarketGenesis) return genesis }, - simtestutil.AppOptionsMap{server.FlagMinGasPrices: "1" + evmtypes.DefaultEVMDenom}, + simtestutil.AppOptionsMap{ + server.FlagMinGasPrices: "1" + evmtypes.DefaultEVMDenom, + srvflags.EVMTracer: evmtypes.Firehose, + }, ) amount, ok := sdkmath.NewIntFromString("10000000000000000000") s.Require().True(ok) diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 632350b718..8d3a58fdd9 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -68,6 +68,9 @@ type Keeper struct { // Tracer used to collect execution traces from the EVM transaction execution tracer string + // EVM Tracer + evmTracer *tracers.Tracer + // EVM Hooks for tx post-processing hooks types.EvmHooks @@ -201,7 +204,15 @@ func (k *Keeper) PostTxProcessing(ctx sdk.Context, msg *core.Message, receipt *e // Tracer return a default vm.Tracer based on current keeper state func (k Keeper) Tracer(msg *core.Message, rules params.Rules) *tracers.Tracer { - return types.NewTracer(k.tracer, msg, rules) + if k.evmTracer == nil { + t := types.NewTracer(k.tracer, msg, rules) + k.evmTracer = t + } + return k.evmTracer +} + +func (k *Keeper) SetTracer(tracer *tracers.Tracer) { + k.evmTracer = tracer } // GetAccount load nonce and codehash without balance, diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 8ddff9f54a..b728cdf885 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -71,9 +71,12 @@ func (k *Keeper) NewEVM( cfg.BlockOverrides.Apply(&blockCtx) } txCtx := core.NewEVMTxContext(msg) + + // Set Config Tracer if it was not already initialized if cfg.Tracer == nil { cfg.Tracer = k.Tracer(msg, cfg.Rules) } + vmConfig := k.VMConfig(ctx, cfg) contracts := make(map[common.Address]vm.PrecompiledContract) active := make([]common.Address, 0) @@ -312,7 +315,7 @@ func (k *Keeper) ApplyMessageWithConfig( msg *core.Message, cfg *EVMConfig, commit bool, -) (*types.MsgEthereumTxResponse, error) { +) (resp *types.MsgEthereumTxResponse, err error) { var ( ret []byte // return bytes from evm execution gasUsed uint64 @@ -338,16 +341,11 @@ func (k *Keeper) ApplyMessageWithConfig( sender := vm.AccountRef(msg.From) // Allow the tracer captures the tx level events, mainly the gas consumption. vmCfg := evm.Config + + if k.evmTracer != nil { + stateDB.SetTracer(k.evmTracer) + } if vmCfg.Tracer != nil { - if cfg.DebugTrace { - // msg.GasPrice should have been set to effective gas price - stateDB.SubBalance( - sender.Address(), - uint256.MustFromBig(new(big.Int).Mul(msg.GasPrice, new(big.Int).SetUint64(msg.GasLimit))), - tracing.BalanceChangeUnspecified, - ) - stateDB.SetNonce(sender.Address(), stateDB.GetNonce(sender.Address())+1) - } vmCfg.Tracer.OnTxStart( evm.GetVMContext(), ethtypes.NewTx(ðtypes.LegacyTx{ @@ -360,6 +358,18 @@ func (k *Keeper) ApplyMessageWithConfig( }), msg.From, ) + + if cfg.DebugTrace { + // msg.GasPrice should have been set to effective gas price + senderAddr := sender.Address() + stateDB.SubBalance( + sender.Address(), + uint256.MustFromBig(new(big.Int).Mul(msg.GasPrice, new(big.Int).SetUint64(msg.GasLimit))), + tracing.BalanceChangeUnspecified, + ) + stateDB.SetNonce(senderAddr, stateDB.GetNonce(senderAddr)+1) + } + defer func() { if cfg.DebugTrace { stateDB.AddBalance( @@ -368,15 +378,25 @@ func (k *Keeper) ApplyMessageWithConfig( tracing.BalanceChangeUnspecified, ) } + + traceErr := err + if vmErr != nil { + traceErr = vmErr + } + vmCfg.Tracer.OnTxEnd( ðtypes.Receipt{ GasUsed: gasUsed, }, - vmErr, + traceErr, ) }() } + //todo: on each of the exit conditions before the calls to if contractCreation { + // and manually catch them in the defer.vmcfg.tracer.ontxend + // also, I think that we have to manually craft a Call/Create? + rules := cfg.Rules contractCreation := msg.To == nil intrinsicGas, err := k.GetEthIntrinsicGas(msg, rules, contractCreation) @@ -459,9 +479,15 @@ func (k *Keeper) ApplyMessageWithConfig( } gasUsed = sdkmath.LegacyMaxDec(minimumGasUsed, sdkmath.LegacyNewDec(int64(temporaryGasUsed))).TruncateInt().Uint64() + // reset leftoverGas, to be used by the tracer leftoverGas = msg.GasLimit - gasUsed + if k.evmTracer != nil && k.evmTracer.OnGasChange != nil { + //TODO: add test to make sure the onGasChange is not doubly called + k.evmTracer.OnGasChange(msg.GasLimit, leftoverGas, tracing.GasChangeTxLeftOverReturned) + } + return &types.MsgEthereumTxResponse{ GasUsed: gasUsed, VmError: vmError, diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 2ae5b0f612..2448e74de8 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -2,6 +2,7 @@ package keeper_test import ( "fmt" + "github.com/ethereum/go-ethereum/core/tracing" "math" "math/big" "testing" @@ -18,7 +19,6 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/tracing" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" @@ -657,7 +657,7 @@ func (suite *StateTransitionTestSuite) TestApplyMessageWithConfig() { expErr bool }{ { - "messsage applied with firehose tracer", + "message applied with firehose tracer", func() { msg, err = newNativeMessage( vmdb.GetNonce(suite.Address), @@ -671,13 +671,11 @@ func (suite *StateTransitionTestSuite) TestApplyMessageWithConfig() { nil, ) suite.Require().NoError(err) - - config.Tracer = types.NewTracer("firehose", msg, params.Rules{}) }, false, }, { - "messsage applied ok", + "message applied ok", func() { msg, err = newNativeMessage( vmdb.GetNonce(suite.Address), @@ -741,7 +739,6 @@ func (suite *StateTransitionTestSuite) TestApplyMessageWithConfig() { tc.malleate() if config.Tracer != nil { - config.Tracer.OnBlockchainInit(params.AllEthashProtocolChanges) config.Tracer.OnBlockStart(tracing.BlockEvent{ Block: ethtypes.NewBlockWithHeader( ðtypes.Header{ diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index df0c3aa88a..d0eefd91d6 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -20,6 +20,8 @@ import ( "math/big" "sort" + "github.com/ethereum/go-ethereum/eth/tracers" + errorsmod "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" "cosmossdk.io/store/cachemulti" @@ -59,6 +61,7 @@ var _ vm.StateDB = &StateDB{} // nested states. It's the general query interface to retrieve: // * Contracts // * Accounts + type StateDB struct { keeper Keeper // origCtx is the context passed in by the caller @@ -99,6 +102,9 @@ type StateDB struct { // events emitted by native action nativeEvents sdk.Events + // EVM Tracer + evmTracer *tracers.Tracer + // handle balances natively evmDenom string err error @@ -142,6 +148,10 @@ func NewWithParams(ctx sdk.Context, keeper Keeper, txConfig TxConfig, evmDenom s return db } +func (s *StateDB) SetTracer(tracer *tracers.Tracer) { + s.evmTracer = tracer +} + func (s *StateDB) NativeEvents() sdk.Events { return s.nativeEvents } @@ -378,7 +388,7 @@ func (s *StateDB) revertNativeStateToSnapshot(ms cachemulti.Store) { } // ExecuteNativeAction executes native action in isolate, -// the writes will be revert when either the native action itself fail +// the writes will be reverted when either the native action itself fail // or the wrapping message call reverted. func (s *StateDB) ExecuteNativeAction(contract common.Address, converter EventConverter, action func(ctx sdk.Context) error) error { snapshot := s.snapshotNativeState() @@ -432,51 +442,83 @@ func (s *StateDB) Transfer(sender, recipient common.Address, amount *big.Int) { } // AddBalance adds amount to the account associated with addr. -func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int, _ tracing.BalanceChangeReason) { +func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) { if amount.Sign() == 0 { return } if amount.Sign() < 0 { panic("negative amount") } + + balance := s.GetBalance(addr) coins := sdk.Coins{sdk.NewCoin(s.evmDenom, sdkmath.NewIntFromBigInt(amount.ToBig()))} if err := s.ExecuteNativeAction(common.Address{}, nil, func(ctx sdk.Context) error { - return s.keeper.AddBalance(ctx, sdk.AccAddress(addr.Bytes()), coins) + return s.keeper.AddBalance(ctx, addr.Bytes(), coins) }); err != nil { s.err = err } + + if s.err != nil { + if s.evmTracer != nil && s.evmTracer.OnBalanceChange != nil { + newBalance := new(big.Int) + newBalance.Add(balance.ToBig(), amount.ToBig()) + s.evmTracer.OnBalanceChange(addr, balance.ToBig(), newBalance, reason) + } + } } // SubBalance subtracts amount from the account associated with addr. -func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int, _ tracing.BalanceChangeReason) { +func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tracing.BalanceChangeReason) { if amount.Sign() == 0 { return } if amount.Sign() < 0 { panic("negative amount") } + + balance := s.GetBalance(addr) coins := sdk.Coins{sdk.NewCoin(s.evmDenom, sdkmath.NewIntFromBigInt(amount.ToBig()))} if err := s.ExecuteNativeAction(common.Address{}, nil, func(ctx sdk.Context) error { - return s.keeper.SubBalance(ctx, sdk.AccAddress(addr.Bytes()), coins) + return s.keeper.SubBalance(ctx, addr.Bytes(), coins) }); err != nil { s.err = err } + + if s.err != nil { + if s.evmTracer != nil && s.evmTracer.OnBalanceChange != nil { + newBalance := new(big.Int) + new(big.Int).Sub(balance.ToBig(), amount.ToBig()) + s.evmTracer.OnBalanceChange(addr, balance.ToBig(), newBalance, reason) + } + } } // SetBalance is called by state override func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) { + balance := s.GetBalance(addr) if err := s.ExecuteNativeAction(common.Address{}, nil, func(ctx sdk.Context) error { return s.keeper.SetBalance(ctx, addr, amount, s.evmDenom) }); err != nil { s.err = err } + + if s.err != nil { + if s.evmTracer != nil && s.evmTracer.OnBalanceChange != nil { + s.evmTracer.OnBalanceChange(addr, balance.ToBig(), amount, tracing.BalanceChangeUnspecified) + } + } } // SetNonce sets the nonce of account. func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { stateObject := s.getOrNewStateObject(addr) if stateObject != nil { + oldNonce := s.GetNonce(addr) stateObject.SetNonce(nonce) + + if s.evmTracer != nil && s.evmTracer.OnNonceChange != nil { + s.evmTracer.OnNonceChange(addr, oldNonce, nonce) + } } } @@ -484,7 +526,12 @@ func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { func (s *StateDB) SetCode(addr common.Address, code []byte) { stateObject := s.getOrNewStateObject(addr) if stateObject != nil { + oldCode := s.GetCode(addr) stateObject.SetCode(crypto.Keccak256Hash(code), code) + + if s.evmTracer != nil && s.evmTracer.OnCodeChange != nil { + s.evmTracer.OnCodeChange(addr, crypto.Keccak256Hash(oldCode), oldCode, crypto.Keccak256Hash(code), code) + } } } @@ -492,14 +539,20 @@ func (s *StateDB) SetCode(addr common.Address, code []byte) { func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { stateObject := s.getOrNewStateObject(addr) stateObject.SetState(key, value) + + if s.evmTracer != nil && s.evmTracer.OnStorageChange != nil { + s.evmTracer.OnStorageChange(addr, key, s.GetState(addr, key), value) + } } // SetStorage replaces the entire storage for the specified account with given // storage. This function should only be used for debugging and the mutations -// must be discarded afterwards. +// must be discarded afterward. func (s *StateDB) SetStorage(addr common.Address, storage Storage) { stateObject := s.getOrNewStateObject(addr) stateObject.SetStorage(storage) + + // TODO: should we call a tracer hook here? } // Suicide marks the given account as suicided. diff --git a/x/evm/statedb/statedb_test.go b/x/evm/statedb/statedb_test.go index 1e5ea30258..b43ab28891 100644 --- a/x/evm/statedb/statedb_test.go +++ b/x/evm/statedb/statedb_test.go @@ -49,6 +49,8 @@ type StateDBTestSuite struct { suite.Suite } +// TODO: add tests with the Tracer + func (suite *StateDBTestSuite) TestAccount() { key1 := common.BigToHash(big.NewInt(1)) value1 := common.BigToHash(big.NewInt(2)) diff --git a/firehose/firehose.go b/x/evm/tracers/firehose.go similarity index 97% rename from firehose/firehose.go rename to x/evm/tracers/firehose.go index b0a7ffe5bd..8acbc44f63 100644 --- a/firehose/firehose.go +++ b/x/evm/tracers/firehose.go @@ -36,7 +36,18 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" - pbeth "github.com/evmos/ethermint/firehose/pb/sf/ethereum/type/v2" + pbeth "github.com/evmos/ethermint/pb/sf/ethereum/type/v2" +) + +const ( + firehoseTraceLevel = "trace" + firehoseDebugLevel = "debug" + firehoseInfoLevel = "info" +) + +const ( + callSourceRoot = "root" + callSourceChild = "child" ) // Here what you can expect from the debugging levels: @@ -44,9 +55,9 @@ import ( // - Debug == Info + call start/end + error // - Trace == Debug + state db changes, log, balance, nonce, code, storage, gas var firehoseTracerLogLevel = strings.ToLower(os.Getenv("FIREHOSE_ETHEREUM_TRACER_LOG_LEVEL")) -var isFirehoseInfoEnabled = firehoseTracerLogLevel == "info" || firehoseTracerLogLevel == "debug" || firehoseTracerLogLevel == "trace" -var isFirehoseDebugEnabled = firehoseTracerLogLevel == "debug" || firehoseTracerLogLevel == "trace" -var isFirehoseTracerEnabled = firehoseTracerLogLevel == "trace" +var isFirehoseInfoEnabled = firehoseTracerLogLevel == firehoseInfoLevel || firehoseTracerLogLevel == firehoseDebugLevel || firehoseTracerLogLevel == firehoseTraceLevel +var isFirehoseDebugEnabled = firehoseTracerLogLevel == firehoseDebugLevel || firehoseTracerLogLevel == firehoseTraceLevel +var isFirehoseTracerEnabled = firehoseTracerLogLevel == firehoseTraceLevel var emptyCommonAddress = common.Address{} var emptyCommonHash = common.Hash{} @@ -86,7 +97,7 @@ func NewTracingHooksFromFirehose(tracer *Firehose) *tracing.Hooks { OnOpcode: tracer.OnOpcode, OnFault: tracer.OnOpcodeFault, - OnBalanceChange: tracer.OnBalanceChange, + OnBalanceChange: tracer.OnBalanceChange, // todo: this should be called when the block is done to calculate the balance changes in terms of rewards to the miner?? OnNonceChange: tracer.OnNonceChange, OnCodeChange: tracer.OnCodeChange, OnStorageChange: tracer.OnStorageChange, @@ -607,7 +618,9 @@ func (f *Firehose) completeTransaction(receipt *types.Receipt) *pbeth.Transactio rootCall := f.transaction.Calls[0] if !f.deferredCallState.IsEmpty() { - f.deferredCallState.MaybePopulateCallAndReset("root", rootCall) + if err := f.deferredCallState.MaybePopulateCallAndReset(callSourceRoot, rootCall); err != nil { + panic(fmt.Errorf("failed to populate deferred call state: %w", err)) + } } // Receipt can be nil if an error occurred during the transaction execution, right now we don't have it @@ -618,6 +631,8 @@ func (f *Firehose) completeTransaction(receipt *types.Receipt) *pbeth.Transactio f.transaction.Status = transactionStatusFromChainTxReceipt(receipt.Status) } + // todo: in some blockchains, when there is not received, it means that the transaction is failed, what about here? should it be the same? + // It's possible that the transaction was reverted, but we still have a receipt, in that case, we must // check the root call if rootCall.StatusReverted { @@ -827,7 +842,9 @@ func (f *Firehose) onOpcodeKeccak256(call *pbeth.Call, stack []uint256.Int, memo // We should have exclusive access to the hasher, we can safely reset it. f.hasher.Reset() f.hasher.Write(preImage) - f.hasher.Read(f.hasherBuf[:]) + if _, err := f.hasher.Read(f.hasherBuf[:]); err != nil { + panic(fmt.Errorf("failed to read keccak256 hash: %w", err)) + } encodedData := hex.EncodeToString(preImage) @@ -938,7 +955,7 @@ func (f *Firehose) callStart(source string, callType pbeth.CallType, from common // be assigned back to 0 because of a bug in the console reader. remove on new chain. // // New chain integration should remove this `if` statement - if source == "root" { + if source == callSourceRoot { call.BeginOrdinal = 0 } } @@ -998,7 +1015,7 @@ func (f *Firehose) callEnd(source string, output []byte, gasUsed uint64, err err firehoseDebug("call end (source=%s index=%d output=%s gasUsed=%d err=%s reverted=%t)", source, f.callStack.ActiveIndex(), outputView(output), gasUsed, errorView(err), reverted) if f.latestCallEnterSuicided { - if source != "child" { + if source != callSourceChild { panic(fmt.Errorf("unexpected source for suicided call end, expected child but got %s, suicide are always produced on a 'child' source", source)) } @@ -1054,10 +1071,10 @@ func (f *Firehose) callEnd(source string, output []byte, gasUsed uint64, err err func computeCallSource(depth int) string { if depth == 0 { - return "root" + return callSourceRoot } - return "child" + return callSourceChild } func (f *Firehose) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { @@ -1076,6 +1093,8 @@ func (f *Firehose) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { for _, addr := range sortedKeys(alloc) { account := alloc[addr] + // todo: in some blockchains, we check if we set applyBackwardCompatibility and if so, call OnNewAccount... should we do it here? + if account.Balance != nil && account.Balance.Sign() != 0 { activeCall := f.callStack.Peek() activeCall.BalanceChanges = append(activeCall.BalanceChanges, f.newBalanceChange("genesis", addr, common.Big0, account.Balance, pbeth.BalanceChange_REASON_GENESIS_BALANCE)) @@ -1425,6 +1444,10 @@ func (f *Firehose) printBlockToFirehose(block *pbeth.Block, finalityStatus *Fina } } + if block.Number-libNum >= 200 { + libNum = block.Number - 200 + } + // **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())) @@ -1480,7 +1503,10 @@ func (f *Firehose) flushToFirehose(in []byte) { } errstr := fmt.Sprintf("\nFIREHOSE FAILED WRITING %dx: %s\n", loops, err) - os.WriteFile("/tmp/firehose_writer_failed_print.log", []byte(errstr), 0644) + if err := os.WriteFile("/tmp/firehose_writer_failed_print.log", []byte(errstr), 0644); err != nil { + fmt.Println(errstr) + } + fmt.Fprint(writer, errstr) } diff --git a/firehose/firehose_test.go b/x/evm/tracers/firehose_test.go similarity index 99% rename from firehose/firehose_test.go rename to x/evm/tracers/firehose_test.go index 8a9cc54bf2..fcf84bdeb3 100644 --- a/firehose/firehose_test.go +++ b/x/evm/tracers/firehose_test.go @@ -21,7 +21,7 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" - pbeth "github.com/evmos/ethermint/firehose/pb/sf/ethereum/type/v2" + pbeth "github.com/evmos/ethermint/pb/sf/ethereum/type/v2" ) func TestFirehoseCallStack_Push(t *testing.T) { diff --git a/firehose/testdata/firehose/reorder-ordinals-empty.golden.json b/x/evm/tracers/testdata/firehose/reorder-ordinals-empty.golden.json similarity index 100% rename from firehose/testdata/firehose/reorder-ordinals-empty.golden.json rename to x/evm/tracers/testdata/firehose/reorder-ordinals-empty.golden.json diff --git a/x/evm/types/tracer.go b/x/evm/types/tracer.go index ca55e38830..b5ca217134 100644 --- a/x/evm/types/tracer.go +++ b/x/evm/types/tracer.go @@ -21,7 +21,7 @@ import ( "github.com/ethereum/go-ethereum/eth/tracers" _ "github.com/ethereum/go-ethereum/eth/tracers/live" - _ "github.com/evmos/ethermint/firehose" + _ "github.com/evmos/ethermint/x/evm/tracers" "github.com/ethereum/go-ethereum/eth/tracers/logger" @@ -62,11 +62,8 @@ func NewTracer(tracer string, msg *core.Message, rules params.Rules) *tracers.Tr case Firehose: hooks, _ = tracers.LiveDirectory.New("firehose", nil) default: - hooks, _ = tracers.LiveDirectory.New("firehose", nil) - // TODO use noop as default - // current default is firehose because it uncovers issues when running - // unittests - // hooks, _ = tracers.LiveDirectory.New("noop", nil) + // Use noop tracer by default + hooks, _ = tracers.LiveDirectory.New("noop", nil) } diff --git a/x/evm/types/utils.go b/x/evm/types/utils.go index f4260335b0..957941e6dc 100644 --- a/x/evm/types/utils.go +++ b/x/evm/types/utils.go @@ -223,7 +223,7 @@ func BinSearch(lo, hi uint64, executable func(uint64) (bool, *MsgEthereumTxRespo failed, _, err := executable(mid) // If the error is not nil(consensus error), it means the provided message // call or transaction will never be accepted no matter how much gas it is - // assigned. Return the error directly, don't struggle any more. + // assigned. Return the error directly, don't struggle anymore. if err != nil { return 0, err } From 94e80337d103c896d076f299cb02194fe3ef752f Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Fri, 4 Oct 2024 10:00:05 -0400 Subject: [PATCH 08/45] Tracer refactoring - Removing tracer from the EthermintApp struct - Updating tests which referenced the tracer from the EVMKeeper - Adding InitChainer method on keeper to call when a block chain is initialized --- app/app.go | 22 ---------------------- x/evm/keeper/keeper.go | 21 +++++++-------------- x/evm/keeper/state_transition.go | 12 +++++------- x/evm/keeper/state_transition_test.go | 2 +- x/evm/statedb/statedb.go | 6 ++---- x/evm/statedb/statedb_test.go | 1 - x/evm/types/tracer.go | 13 ++++++++++++- 7 files changed, 27 insertions(+), 50 deletions(-) diff --git a/app/app.go b/app/app.go index 72f276b297..49a616651a 100644 --- a/app/app.go +++ b/app/app.go @@ -18,7 +18,6 @@ package app import ( "encoding/json" "fmt" - ethparams "github.com/ethereum/go-ethereum/params" "io" "io/fs" "math/big" @@ -131,7 +130,6 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" ethhexutil "github.com/ethereum/go-ethereum/common/hexutil" ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/tracers" "github.com/evmos/ethermint/client/docs" "github.com/evmos/ethermint/app/ante" @@ -246,9 +244,6 @@ type EthermintApp struct { EvmKeeper *evmkeeper.Keeper FeeMarketKeeper feemarketkeeper.Keeper - // EVMTracer - EvmTracer *tracers.Tracer - // the module manager ModuleManager *module.Manager BasicModuleManager module.BasicManager @@ -488,9 +483,6 @@ func NewEthermintApp( authAddr, ) - // Firehose tracer will be set downstream by the srvflag evm tracer firehose - tracer := cast.ToString(appOpts.Get(srvflags.EVMTracer)) - // Create Ethermint keepers feeMarketSs := app.GetSubspace(feemarkettypes.ModuleName) app.FeeMarketKeeper = feemarketkeeper.NewKeeper( @@ -506,20 +498,10 @@ func NewEthermintApp( appCodec, keys[evmtypes.StoreKey], okeys[evmtypes.ObjectStoreKey], authtypes.NewModuleAddress(govtypes.ModuleName), app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.FeeMarketKeeper, - tracer, evmSs, nil, ) - if app.EvmTracer == nil { - if tracer == evmtypes.TracerAccessList { - panic("Ethermint App with tracer value 'access-list' is not supported") - } - - app.EvmTracer = evmtypes.NewTracer(tracer, nil, ethparams.Rules{}) - app.EvmKeeper.SetTracer(app.EvmTracer) - } - // register the proposal types govRouter := govv1beta1.NewRouter() govRouter.AddRoute(govtypes.RouterKey, govv1beta1.ProposalHandler). @@ -895,10 +877,6 @@ func (app *EthermintApp) InitChainer(ctx sdk.Context, req *abci.RequestInitChain if err := app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap()); err != nil { return nil, err } - if app.EvmTracer != nil && app.EvmTracer.OnBlockchainInit != nil { - app.EvmKeeper.WithChainID(ctx) - app.EvmTracer.OnBlockchainInit(evmtypes.DefaultChainConfig().EthereumConfig(app.EvmKeeper.ChainID())) - } return app.ModuleManager.InitGenesis(ctx, app.appCodec, genesisState) } diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 8d3a58fdd9..45a1a026d1 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -33,6 +33,7 @@ import ( ethermint "github.com/evmos/ethermint/types" "github.com/evmos/ethermint/x/evm/statedb" "github.com/evmos/ethermint/x/evm/types" + evmtypes "github.com/evmos/ethermint/x/evm/types" ) // CustomContractFn defines a custom precompiled contract generator with ctx, rules and returns a precompiled contract. @@ -65,9 +66,6 @@ type Keeper struct { // chain ID number obtained from the context's chain id eip155ChainID *big.Int - // Tracer used to collect execution traces from the EVM transaction execution - tracer string - // EVM Tracer evmTracer *tracers.Tracer @@ -88,7 +86,6 @@ func NewKeeper( bankKeeper types.BankKeeper, sk types.StakingKeeper, fmk types.FeeMarketKeeper, - tracer string, ss paramstypes.Subspace, customContractFns []CustomContractFn, ) *Keeper { @@ -112,7 +109,6 @@ func NewKeeper( feeMarketKeeper: fmk, storeKey: storeKey, objectKey: objectKey, - tracer: tracer, ss: ss, customContractFns: customContractFns, } @@ -143,6 +139,12 @@ func (k Keeper) ChainID() *big.Int { return k.eip155ChainID } +func (k *Keeper) InitChainer() { + if k.evmTracer != nil && k.evmTracer.OnBlockchainInit != nil { + k.evmTracer.OnBlockchainInit(evmtypes.DefaultChainConfig().EthereumConfig(k.ChainID())) + } +} + // ---------------------------------------------------------------------------- // Block Bloom // Required by Web3 API. @@ -202,15 +204,6 @@ func (k *Keeper) PostTxProcessing(ctx sdk.Context, msg *core.Message, receipt *e return k.hooks.PostTxProcessing(ctx, msg, receipt) } -// Tracer return a default vm.Tracer based on current keeper state -func (k Keeper) Tracer(msg *core.Message, rules params.Rules) *tracers.Tracer { - if k.evmTracer == nil { - t := types.NewTracer(k.tracer, msg, rules) - k.evmTracer = t - } - return k.evmTracer -} - func (k *Keeper) SetTracer(tracer *tracers.Tracer) { k.evmTracer = tracer } diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index b728cdf885..5ce0291ef1 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -73,8 +73,8 @@ func (k *Keeper) NewEVM( txCtx := core.NewEVMTxContext(msg) // Set Config Tracer if it was not already initialized - if cfg.Tracer == nil { - cfg.Tracer = k.Tracer(msg, cfg.Rules) + if k.evmTracer != nil { + cfg.Tracer = k.evmTracer } vmConfig := k.VMConfig(ctx, cfg) @@ -227,7 +227,7 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) res.VmError = types.ErrPostTxProcessing.Error() k.Logger(ctx).Error("tx post processing failed", "error", err) - // If the tx failed in post processing hooks, we should clear the logs + // If the tx failed in post-processing hooks, we should clear the logs res.Logs = nil } else if commit != nil { // PostTxProcessing is successful, commit the tmpCtx @@ -259,7 +259,6 @@ func (k *Keeper) ApplyMessage(ctx sdk.Context, msg *core.Message, tracer *tracer return nil, errorsmod.Wrap(err, "failed to load evm config") } - cfg.Tracer = tracer return k.ApplyMessageWithConfig(ctx, msg, cfg, commit) } @@ -339,13 +338,12 @@ func (k *Keeper) ApplyMessageWithConfig( evm = k.NewEVM(ctx, msg, cfg, stateDB) leftoverGas := msg.GasLimit sender := vm.AccountRef(msg.From) + // Allow the tracer captures the tx level events, mainly the gas consumption. vmCfg := evm.Config - if k.evmTracer != nil { - stateDB.SetTracer(k.evmTracer) - } if vmCfg.Tracer != nil { + stateDB.SetTracer(vmCfg.Tracer) vmCfg.Tracer.OnTxStart( evm.GetVMContext(), ethtypes.NewTx(ðtypes.LegacyTx{ diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 2448e74de8..8a05eccb4a 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -616,7 +616,7 @@ func (suite *StateTransitionTestSuite) TestApplyMessage() { chainCfg := keeperParams.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()) rules := chainCfg.Rules(big.NewInt(suite.Ctx.BlockHeight()), chainCfg.MergeNetsplitBlock != nil, uint64(suite.Ctx.BlockHeader().Time.Unix())) signer := ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) - tracer := suite.App.EvmKeeper.Tracer(msg, rules) + tracer := types.NewTracer("", msg, rules) vmdb := suite.StateDB() msg, err = newNativeMessage( diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index d0eefd91d6..15c1c61228 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -20,8 +20,6 @@ import ( "math/big" "sort" - "github.com/ethereum/go-ethereum/eth/tracers" - errorsmod "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" "cosmossdk.io/store/cachemulti" @@ -103,7 +101,7 @@ type StateDB struct { nativeEvents sdk.Events // EVM Tracer - evmTracer *tracers.Tracer + evmTracer *tracing.Hooks // handle balances natively evmDenom string @@ -148,7 +146,7 @@ func NewWithParams(ctx sdk.Context, keeper Keeper, txConfig TxConfig, evmDenom s return db } -func (s *StateDB) SetTracer(tracer *tracers.Tracer) { +func (s *StateDB) SetTracer(tracer *tracing.Hooks) { s.evmTracer = tracer } diff --git a/x/evm/statedb/statedb_test.go b/x/evm/statedb/statedb_test.go index b43ab28891..63621f2431 100644 --- a/x/evm/statedb/statedb_test.go +++ b/x/evm/statedb/statedb_test.go @@ -830,7 +830,6 @@ func newTestKeeper(t *testing.T, cms storetypes.MultiStore) (sdk.Context, *evmke appCodec, testStoreKeys[evmtypes.StoreKey], testObjKeys[evmtypes.ObjectStoreKey], authtypes.NewModuleAddress(govtypes.ModuleName), accountKeeper, bankKeeper, nil, nil, - "", paramstypes.Subspace{}, nil, ) diff --git a/x/evm/types/tracer.go b/x/evm/types/tracer.go index b5ca217134..ac4c034d77 100644 --- a/x/evm/types/tracer.go +++ b/x/evm/types/tracer.go @@ -16,6 +16,7 @@ package types import ( + "fmt" "os" "github.com/ethereum/go-ethereum/eth/tracers" @@ -64,7 +65,6 @@ func NewTracer(tracer string, msg *core.Message, rules params.Rules) *tracers.Tr default: // Use noop tracer by default hooks, _ = tracers.LiveDirectory.New("noop", nil) - } return &tracers.Tracer{ @@ -72,6 +72,17 @@ func NewTracer(tracer string, msg *core.Message, rules params.Rules) *tracers.Tr } } +func NewLiveTracer(tracer string) (*tracers.Tracer, error) { + h, err := tracers.LiveDirectory.New(tracer, nil) + if err != nil { + return nil, fmt.Errorf("initializing live tracer %s: %w", tracer, err) + } + + return &tracers.Tracer{ + Hooks: h, + }, nil +} + // TxTraceResult is the result of a single transaction trace during a block trace. type TxTraceResult struct { Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer From 5376e54a57170c1599c66e6615effaa75ac3280d Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Fri, 4 Oct 2024 13:09:02 -0400 Subject: [PATCH 09/45] Adding EndBlock tracer and using the k.evmTracer instead of the tracer from the config --- x/evm/keeper/abci.go | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/x/evm/keeper/abci.go b/x/evm/keeper/abci.go index 692dcf378d..bb6be97f59 100644 --- a/x/evm/keeper/abci.go +++ b/x/evm/keeper/abci.go @@ -17,7 +17,6 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "math/big" @@ -32,24 +31,18 @@ func (k *Keeper) BeginBlock(ctx sdk.Context) error { return err } - cfg, err := k.EVMConfig(ctx, k.ChainID(), common.Hash{}) - if err != nil { - return err - } - - if cfg.Tracer != nil { + if k.evmTracer != nil && k.evmTracer.OnBlockStart != nil { b := types.NewBlock(&types.Header{ Number: big.NewInt(ctx.BlockHeight()), Time: uint64(ctx.BlockTime().Unix()), }, nil, nil, nil) - cfg.Tracer.OnBlockStart(tracing.BlockEvent{ + k.evmTracer.OnBlockStart(tracing.BlockEvent{ Block: b, TD: big.NewInt(1), // Finalized: , // todo: how to set up the header here? // Safe: , // todo: how to set up the header here? }) - } return nil @@ -62,15 +55,11 @@ func (k *Keeper) EndBlock(ctx sdk.Context) error { k.CollectTxBloom(ctx) k.RemoveParamsCache(ctx) - // TODO: call EndBlock on Tracer - //config, err := k.EVMConfig(ctx, k.ChainID(), common.Hash{}) - //if err != nil { - // return err - //} - // - //defer func() { - // config.Tracer.OnBlockEnd(nil) - //}() + if k.evmTracer != nil && k.evmTracer.OnBlockEnd != nil { + defer func() { + k.evmTracer.OnBlockEnd(nil) + }() + } return nil } From 65f117b0b7af7a0e60fc3c2d65e6c7e6c7bb86cc Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Fri, 4 Oct 2024 14:08:31 -0400 Subject: [PATCH 10/45] Update finalized header when begin block hook is called --- x/evm/keeper/abci.go | 22 ++++++++++++++++------ x/evm/tracers/firehose.go | 4 ---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/x/evm/keeper/abci.go b/x/evm/keeper/abci.go index bb6be97f59..118b6a9dd9 100644 --- a/x/evm/keeper/abci.go +++ b/x/evm/keeper/abci.go @@ -17,6 +17,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "math/big" @@ -33,15 +34,24 @@ func (k *Keeper) BeginBlock(ctx sdk.Context) error { if k.evmTracer != nil && k.evmTracer.OnBlockStart != nil { b := types.NewBlock(&types.Header{ - Number: big.NewInt(ctx.BlockHeight()), - Time: uint64(ctx.BlockTime().Unix()), + Number: big.NewInt(ctx.BlockHeight()), + Time: uint64(ctx.BlockTime().Unix()), + ParentHash: ethcommon.BytesToHash(ctx.BlockHeader().LastBlockId.Hash), }, nil, nil, nil) + finalizedHeaderNumber := ctx.BlockHeight() - 1 + if ctx.BlockHeight() == 0 { + finalizedHeaderNumber = 0 + } + + finalizedHeader := &types.Header{ + Number: big.NewInt(finalizedHeaderNumber), + } + k.evmTracer.OnBlockStart(tracing.BlockEvent{ - Block: b, - TD: big.NewInt(1), - // Finalized: , // todo: how to set up the header here? - // Safe: , // todo: how to set up the header here? + Block: b, + TD: big.NewInt(1), + Finalized: finalizedHeader, }) } diff --git a/x/evm/tracers/firehose.go b/x/evm/tracers/firehose.go index 8acbc44f63..e731ff8b9c 100644 --- a/x/evm/tracers/firehose.go +++ b/x/evm/tracers/firehose.go @@ -1444,10 +1444,6 @@ func (f *Firehose) printBlockToFirehose(block *pbeth.Block, finalityStatus *Fina } } - if block.Number-libNum >= 200 { - libNum = block.Number - 200 - } - // **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())) From 7821ea64b1e4bfbf5fe297a02c7c3bf82ad48fd9 Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Fri, 4 Oct 2024 17:33:55 -0400 Subject: [PATCH 11/45] Removing unused method and setting header values --- app/app.go | 45 -------------------------------------------- x/evm/keeper/abci.go | 3 ++- 2 files changed, 2 insertions(+), 46 deletions(-) diff --git a/app/app.go b/app/app.go index 49a616651a..adf6bb23c3 100644 --- a/app/app.go +++ b/app/app.go @@ -20,7 +20,6 @@ import ( "fmt" "io" "io/fs" - "math/big" "net/http" "os" "path/filepath" @@ -40,7 +39,6 @@ import ( "cosmossdk.io/log" abci "github.com/cometbft/cometbft/abci/types" tmos "github.com/cometbft/cometbft/libs/os" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/cosmos-sdk/baseapp" @@ -127,9 +125,6 @@ import ( ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" ibckeeper "github.com/cosmos/ibc-go/v8/modules/core/keeper" ibctm "github.com/cosmos/ibc-go/v8/modules/light-clients/07-tendermint" - ethcommon "github.com/ethereum/go-ethereum/common" - ethhexutil "github.com/ethereum/go-ethereum/common/hexutil" - ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/evmos/ethermint/client/docs" "github.com/evmos/ethermint/app/ante" @@ -1112,43 +1107,3 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino paramsKeeper.Subspace(feemarkettypes.ModuleName).WithKeyTable(feemarkettypes.ParamKeyTable()) return paramsKeeper } - -// Call this method in to OnBlockStart call -func TmBlockHeaderToEVM( - ctx sdk.Context, - block tmproto.Header, - k *evmkeeper.Keeper, -) (header *ethtypes.Header) { - number := big.NewInt(block.Height) - lastHash := ethcommon.BytesToHash(block.LastBlockId.Hash) - appHash := ethcommon.BytesToHash(block.AppHash) - txHash := ethcommon.BytesToHash(block.DataHash) - resultHash := ethcommon.BytesToHash(block.LastResultsHash) - miner := ethcommon.BytesToAddress(block.ProposerAddress) - gasLimit, gasWanted := uint64(0), uint64(0) - - header = ðtypes.Header{ - Number: number, - ParentHash: lastHash, - Nonce: ethtypes.BlockNonce{}, // todo: check if this applies to injective - MixDigest: ethcommon.Hash{}, // todo: check if this applies to injective - UncleHash: ethtypes.EmptyUncleHash, // todo: check if this applies to injective - Bloom: ethtypes.Bloom{}, // k.GetBlockBloom(ctx, block.Height), // todo: check how to implement this - Root: appHash, - Coinbase: miner, - Difficulty: big.NewInt(0), // todo: check if this applies to injective - Extra: ethhexutil.Bytes{}, // todo: check if this applies to injective - GasLimit: gasLimit, - GasUsed: gasWanted, - Time: uint64(block.Time.Unix()), - TxHash: txHash, - ReceiptHash: resultHash, - BaseFee: nil, // k.GetBaseFeePerGas(ctx).RoundInt().BigInt(), // todo: check how to implement this - } - - return -} - -func ptr[T any](t T) *T { - return &t -} diff --git a/x/evm/keeper/abci.go b/x/evm/keeper/abci.go index 118b6a9dd9..8fcefb7a70 100644 --- a/x/evm/keeper/abci.go +++ b/x/evm/keeper/abci.go @@ -37,6 +37,7 @@ func (k *Keeper) BeginBlock(ctx sdk.Context) error { Number: big.NewInt(ctx.BlockHeight()), Time: uint64(ctx.BlockTime().Unix()), ParentHash: ethcommon.BytesToHash(ctx.BlockHeader().LastBlockId.Hash), + Coinbase: ethcommon.BytesToAddress(ctx.BlockHeader().ProposerAddress), }, nil, nil, nil) finalizedHeaderNumber := ctx.BlockHeight() - 1 @@ -50,7 +51,7 @@ func (k *Keeper) BeginBlock(ctx sdk.Context) error { k.evmTracer.OnBlockStart(tracing.BlockEvent{ Block: b, - TD: big.NewInt(1), + TD: big.NewInt(0), Finalized: finalizedHeader, }) } From 5f5cd2cfad7b327a15e3d321d440dfd2a2026cba Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Tue, 8 Oct 2024 21:08:05 -0400 Subject: [PATCH 12/45] Update tracer with custom OnCosmosBlockStart and OnCosmosBlockEnd hooks --- x/evm/keeper/abci.go | 42 +++----- x/evm/keeper/config.go | 9 +- x/evm/keeper/keeper.go | 6 +- x/evm/keeper/keeper_firehose.go | 58 +++++++++++ x/evm/keeper/state_transition.go | 11 ++- x/evm/tracers/firehose.go | 160 +++++++++++++++++++++++++++++-- x/evm/tracers/firehose_test.go | 3 +- x/evm/tracers/registry.go | 31 ++++++ x/evm/tracers/tracing.go | 12 +++ x/evm/tracing/hooks.go | 32 +++++++ x/evm/types/tracer.go | 11 +++ 11 files changed, 329 insertions(+), 46 deletions(-) create mode 100644 x/evm/keeper/keeper_firehose.go create mode 100644 x/evm/tracers/registry.go create mode 100644 x/evm/tracers/tracing.go create mode 100644 x/evm/tracing/hooks.go diff --git a/x/evm/keeper/abci.go b/x/evm/keeper/abci.go index 8fcefb7a70..da6774aab1 100644 --- a/x/evm/keeper/abci.go +++ b/x/evm/keeper/abci.go @@ -17,10 +17,6 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/types" - "math/big" ) // BeginBlock sets the sdk Context and EIP155 chain id to the Keeper. @@ -28,32 +24,20 @@ func (k *Keeper) BeginBlock(ctx sdk.Context) error { k.WithChainID(ctx) // cache parameters that's common for the whole block. - if _, err := k.EVMBlockConfig(ctx, k.ChainID()); err != nil { + evmBlockConfig, err := k.EVMBlockConfig(ctx, k.ChainID()) + if err != nil { return err } - if k.evmTracer != nil && k.evmTracer.OnBlockStart != nil { - b := types.NewBlock(&types.Header{ - Number: big.NewInt(ctx.BlockHeight()), - Time: uint64(ctx.BlockTime().Unix()), - ParentHash: ethcommon.BytesToHash(ctx.BlockHeader().LastBlockId.Hash), - Coinbase: ethcommon.BytesToAddress(ctx.BlockHeader().ProposerAddress), - }, nil, nil, nil) - - finalizedHeaderNumber := ctx.BlockHeight() - 1 - if ctx.BlockHeight() == 0 { - finalizedHeaderNumber = 0 - } - - finalizedHeader := &types.Header{ - Number: big.NewInt(finalizedHeaderNumber), - } - - k.evmTracer.OnBlockStart(tracing.BlockEvent{ - Block: b, - TD: big.NewInt(0), - Finalized: finalizedHeader, - }) + if k.evmTracer != nil && k.evmTracer.OnCosmosBlockStart != nil { + k.evmTracer.OnCosmosBlockStart( + ToCosmosStartBlockEvent( + k, + ctx, + evmBlockConfig.CoinBase.Bytes(), + ctx.BlockHeader(), + ), + ) } return nil @@ -66,9 +50,9 @@ func (k *Keeper) EndBlock(ctx sdk.Context) error { k.CollectTxBloom(ctx) k.RemoveParamsCache(ctx) - if k.evmTracer != nil && k.evmTracer.OnBlockEnd != nil { + if k.evmTracer != nil && k.evmTracer.OnCosmosBlockEnd != nil { defer func() { - k.evmTracer.OnBlockEnd(nil) + k.evmTracer.OnCosmosBlockEnd(ToCosmosEndBlockEvent(k, ctx), nil) }() } diff --git a/x/evm/keeper/config.go b/x/evm/keeper/config.go index fc00a823ad..61d9abff1d 100644 --- a/x/evm/keeper/config.go +++ b/x/evm/keeper/config.go @@ -16,13 +16,13 @@ package keeper import ( + "github.com/ethereum/go-ethereum/eth/tracers" "math/big" errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/params" rpctypes "github.com/evmos/ethermint/rpc/types" "github.com/evmos/ethermint/x/evm/statedb" @@ -132,7 +132,12 @@ func (k *Keeper) EVMConfig(ctx sdk.Context, chainID *big.Int, txHash common.Hash } if k.evmTracer != nil { - cfg.Tracer = k.evmTracer + t := &tracers.Tracer{ + Hooks: k.evmTracer.Hooks, + GetResult: nil, + Stop: nil, + } + cfg.Tracer = t } return cfg, nil diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 45a1a026d1..4c44d3d60e 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -16,6 +16,7 @@ package keeper import ( + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "math/big" errorsmod "cosmossdk.io/errors" @@ -28,7 +29,6 @@ import ( "github.com/ethereum/go-ethereum/core" ethtypes "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/params" ethermint "github.com/evmos/ethermint/types" "github.com/evmos/ethermint/x/evm/statedb" @@ -67,7 +67,7 @@ type Keeper struct { eip155ChainID *big.Int // EVM Tracer - evmTracer *tracers.Tracer + evmTracer *cosmostracing.Hooks // EVM Hooks for tx post-processing hooks types.EvmHooks @@ -204,7 +204,7 @@ func (k *Keeper) PostTxProcessing(ctx sdk.Context, msg *core.Message, receipt *e return k.hooks.PostTxProcessing(ctx, msg, receipt) } -func (k *Keeper) SetTracer(tracer *tracers.Tracer) { +func (k *Keeper) SetTracer(tracer *cosmostracing.Hooks) { k.evmTracer = tracer } diff --git a/x/evm/keeper/keeper_firehose.go b/x/evm/keeper/keeper_firehose.go new file mode 100644 index 0000000000..f14ecc507c --- /dev/null +++ b/x/evm/keeper/keeper_firehose.go @@ -0,0 +1,58 @@ +package keeper + +import ( + "cosmossdk.io/store/prefix" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cosmostypes "github.com/cometbft/cometbft/types" + sdk "github.com/cosmos/cosmos-sdk/types" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/evmos/ethermint/x/evm/tracing" + "github.com/evmos/ethermint/x/evm/types" + "math/big" +) + +func BlocksBloom(k *Keeper, ctx sdk.Context) *big.Int { + store := prefix.NewObjStore(ctx.ObjectStore(k.objectKey), types.KeyPrefixObjectBloom) + it := store.Iterator(nil, nil) + defer it.Close() + + bloom := new(big.Int) + for ; it.Valid(); it.Next() { + bloom.Or(bloom, it.Value().(*big.Int)) + } + return bloom +} + +func ToCosmosStartBlockEvent(k *Keeper, ctx sdk.Context, coinbaseBytes []byte, blockHeader cmtproto.Header) tracing.CosmosStartBlockEvent { + // ignore the errors as we are sure that the block header is valid + h, _ := cosmostypes.HeaderFromProto(&blockHeader) + h.ValidatorsHash = ctx.CometInfo().GetValidatorsHash() + + keeperParams := k.GetParams(ctx) + ethCfg := keeperParams.ChainConfig.EthereumConfig(k.ChainID()) + baseFee := k.GetBaseFee(ctx, ethCfg) + gasLimit := uint64(ctx.ConsensusParams().Block.MaxGas) + + finalizedHeaderNumber := h.Height - 1 + if h.Height == 0 { + finalizedHeaderNumber = 0 + } + + finalizedHeader := ðtypes.Header{ + Number: big.NewInt(finalizedHeaderNumber), + } + + return tracing.CosmosStartBlockEvent{ + CosmosHeader: h, + BaseFee: baseFee, + GasLimit: gasLimit, + Coinbase: coinbaseBytes, + Finalized: finalizedHeader, + } +} + +func ToCosmosEndBlockEvent(k *Keeper, ctx sdk.Context) tracing.CosmosEndBlockEvent { + return tracing.CosmosEndBlockEvent{ + LogsBloom: BlocksBloom(k, ctx).Bytes(), + } +} diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 5ce0291ef1..4e6dca2958 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -47,7 +47,7 @@ import ( // beneficiary of the coinbase transaction (since we're not mining). // // NOTE: the RANDOM opcode is currently not supported since it requires -// RANDAO implementation. See https://github.com/evmos/ethermint/pull/1520#pullrequestreview-1200504697 +// RANDOM implementation. See https://github.com/evmos/ethermint/pull/1520#pullrequestreview-1200504697 // for more information. func (k *Keeper) NewEVM( ctx sdk.Context, @@ -74,7 +74,12 @@ func (k *Keeper) NewEVM( // Set Config Tracer if it was not already initialized if k.evmTracer != nil { - cfg.Tracer = k.evmTracer + t := &tracers.Tracer{ + Hooks: k.evmTracer.Hooks, + GetResult: nil, + Stop: nil, + } + cfg.Tracer = t } vmConfig := k.VMConfig(ctx, cfg) @@ -179,7 +184,7 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) } msg := msgEth.AsMessage(cfg.BaseFee) - // snapshot to contain the tx processing and post processing in same scope + // snapshot to contain the tx processing and post-processing in same scope var commit func() tmpCtx := ctx if k.hooks != nil { diff --git a/x/evm/tracers/firehose.go b/x/evm/tracers/firehose.go index e731ff8b9c..f407e32f3f 100644 --- a/x/evm/tracers/firehose.go +++ b/x/evm/tracers/firehose.go @@ -8,6 +8,8 @@ import ( "encoding/json" "errors" "fmt" + "github.com/ethereum/go-ethereum/eth/tracers" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "io" "math/big" "os" @@ -21,13 +23,13 @@ import ( "sync/atomic" "time" + cosmostypes "github.com/cometbft/cometbft/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "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/crypto" - "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" @@ -66,6 +68,7 @@ func init() { staticFirehoseChainValidationOnInit() tracers.LiveDirectory.Register("firehose", newFirehoseTracer) + GlobalLiveTracerRegistry.Register("firehose", NewCosmosFirehoseTracer) // Those 2 are defined but not used in this branch, they are kept because used in other branches // so it's easier to keep them here and suppress the warning by faking a usage. @@ -82,6 +85,22 @@ func newFirehoseTracer(cfg json.RawMessage) (*tracing.Hooks, error) { return NewTracingHooksFromFirehose(firehoseTracer), nil } +func NewCosmosFirehoseTracer(backwardCompatibility bool) (*cosmostracing.Hooks, error) { + firehoseConfig := new(FirehoseConfig) + if backwardCompatibility { + firehoseConfig.ApplyBackwardCompatibility = ptr(true) + } + + f, err := newFirehose(firehoseConfig) + if err != nil { + return nil, fmt.Errorf("failed to create Firehose tracer: %w", err) + } + + hooks := NewTracingHooksFromFirehose(f) + + return NewCosmosTracingHooksFromFirehose(hooks, f), nil +} + func NewTracingHooksFromFirehose(tracer *Firehose) *tracing.Hooks { return &tracing.Hooks{ OnBlockchainInit: tracer.OnBlockchainInit, @@ -111,6 +130,15 @@ func NewTracingHooksFromFirehose(tracer *Firehose) *tracing.Hooks { } } +func NewCosmosTracingHooksFromFirehose(hooks *tracing.Hooks, firehose *Firehose) *cosmostracing.Hooks { + return &cosmostracing.Hooks{ + Hooks: hooks, + + OnCosmosBlockStart: firehose.OnCosmosBlockStart, + OnCosmosBlockEnd: firehose.OnCosmosBlockEnd, + } +} + type FirehoseConfig struct { ApplyBackwardCompatibility *bool `json:"applyBackwardCompatibility"` @@ -154,7 +182,13 @@ type Firehose struct { applyBackwardCompatibility *bool // Block state - block *pbeth.Block + block *pbeth.Block + cosmosBlockHeader cosmostypes.Header + + //fixme: this is a hack, waiting for proto changes + lastBlockHash []byte + lastParentBlockHash []byte + blockBaseFee *big.Int blockOrdinal *Ordinal blockFinality *FinalityStatus @@ -204,10 +238,15 @@ func NewFirehoseFromRawJSON(cfg json.RawMessage) (*Firehose, error) { } } - return NewFirehose(&config), nil + f, err := newFirehose(&config) + if err != nil { + return nil, err + } + + return f, nil } -func NewFirehose(config *FirehoseConfig) *Firehose { +func newFirehose(config *FirehoseConfig) (*Firehose, error) { log.Info("Firehose tracer created", config.LogKeyValues()...) firehose := &Firehose{ @@ -240,7 +279,7 @@ func NewFirehose(config *FirehoseConfig) *Firehose { } } - return firehose + return firehose, nil } func (f *Firehose) newIsolatedTransactionTracer(tracerID string) *Firehose { @@ -349,7 +388,7 @@ func (f *Firehose) OnBlockStart(event tracing.BlockEvent) { f.blockIsPrecompiledAddr = getActivePrecompilesChecker(f.blockRules) f.block = &pbeth.Block{ - Hash: b.Hash().Bytes(), + Hash: b.Header().Hash().Bytes(), Number: b.Number().Uint64(), Header: newBlockHeaderFromChainHeader(b.Header(), firehoseBigIntFromNative(new(big.Int).Add(event.TD, b.Difficulty()))), Size: b.Size(), @@ -372,6 +411,53 @@ func (f *Firehose) OnBlockStart(event tracing.BlockEvent) { f.blockFinality.populateFromChain(event.Finalized) } +func (f *Firehose) OnCosmosBlockStart(event cosmostracing.CosmosStartBlockEvent) { + header := event.CosmosHeader + + coinbase := event.Coinbase + gasLimit := event.GasLimit + baseFee := event.BaseFee + + f.cosmosBlockHeader = header + + firehoseInfo("block start (number=%d)", header.Height) + + f.ensureBlockChainInit() + + f.blockRules = f.chainConfig.Rules(new(big.Int).SetInt64(header.Height), f.chainConfig.TerminalTotalDifficultyPassed, uint64(header.Time.Unix())) + f.blockIsPrecompiledAddr = getActivePrecompilesChecker(f.blockRules) + + f.block = &pbeth.Block{ + Number: uint64(header.Height), + Header: newBlockHeaderFromCosmosChainHeader(header, coinbase, gasLimit, baseFee), + Ver: 4, + } + + // TODO: fetch the real parentHash from the event + if header.Height == 1 { + f.lastParentBlockHash = []byte("0000000000000000000000000000000000000000000000000000000000000000") + } + + if header.Height > 1 { + // move the parentHash to the previous block + f.lastParentBlockHash = f.lastBlockHash + fmt.Println("doudou", f.lastParentBlockHash) + f.block.Header.ParentHash = f.lastParentBlockHash + } + + f.lastBlockHash = f.block.Header.Hash + + if *f.applyBackwardCompatibility { + f.block.Ver = 3 + } + + if f.block.Header.BaseFeePerGas != nil { + f.blockBaseFee = f.block.Header.BaseFeePerGas.Native() + } + + f.blockFinality.populateFromChain(event.Finalized) +} + func (f *Firehose) OnSkippedBlock(event tracing.BlockEvent) { // Blocks that are skipped from blockchain that were known and should contain 0 transactions. // It happened in the past, on Polygon if I recall right, that we missed block because some block @@ -426,6 +512,35 @@ func (f *Firehose) OnBlockEnd(err error) { firehoseInfo("block end") } +func (f *Firehose) OnCosmosBlockEnd(event cosmostracing.CosmosEndBlockEvent, err error) { + firehoseInfo("block ending (err=%s)", errorView(err)) + f.block.Hash = f.cosmosBlockHeader.Hash() + f.block.Header.LogsBloom = types.BytesToBloom(event.LogsBloom).Bytes() + + f.block.Size = 10 // todo: find the right size + + for _, trx := range f.block.TransactionTraces { + f.block.Header.GasUsed += trx.GasUsed + } + + if err == nil { + if f.blockReorderOrdinal { + f.reorderIsolatedTransactionsAndOrdinals() + } + + f.ensureInBlockAndNotInTrx() + f.printBlockToFirehose(f.block, f.blockFinality) + } else { + // An error occurred, could have happen in transaction/call context, we must not check if in trx/call, only check in block + f.ensureInBlock(0) + } + + f.resetBlock() + f.resetTransaction() + + firehoseInfo("block end") +} + // reorderIsolatedTransactionsAndOrdinals is called right after all transactions have completed execution. It will sort transactions // according to their index. // @@ -1506,7 +1621,7 @@ func (f *Firehose) flushToFirehose(in []byte) { fmt.Fprint(writer, errstr) } -// TestingBuffer is an internal method only used for testing purposes +// InternalTestingBuffer TestingBuffer is an internal method only used for testing purposes // that should never be used in production code. // // There is no public api guaranteed for this method. @@ -1514,7 +1629,6 @@ func (f *Firehose) InternalTestingBuffer() *bytes.Buffer { return f.testingBuffer } -// FIXME: Create a unit test that is going to fail as soon as any header is added in func newBlockHeaderFromChainHeader(h *types.Header, td *pbeth.BigInt) *pbeth.BlockHeader { var withdrawalsHashBytes []byte if hash := h.WithdrawalsHash; hash != nil { @@ -1561,6 +1675,36 @@ func newBlockHeaderFromChainHeader(h *types.Header, td *pbeth.BigInt) *pbeth.Blo return pbHead } +// FIXME: Create a unit test that is going to fail as soon as any header is added in +func newBlockHeaderFromCosmosChainHeader(h cosmostypes.Header, coinbase []byte, gasLimit uint64, baseFee *big.Int) *pbeth.BlockHeader { + difficulty := firehoseBigIntFromNative(new(big.Int).SetInt64(0)) + + pbHead := &pbeth.BlockHeader{ + Hash: h.Hash().Bytes(), + Number: uint64(h.Height), + //ParentHash: h.LastBlockID.Hash, + UncleHash: types.EmptyUncleHash.Bytes(), // No uncles in Tendermint + Coinbase: coinbase, + StateRoot: h.AppHash, + TransactionsRoot: h.DataHash, + ReceiptRoot: types.EmptyRootHash.Bytes(), + LogsBloom: types.EmptyVerkleHash.Bytes(), // TODO: fix this + Difficulty: difficulty, + TotalDifficulty: difficulty, + GasLimit: gasLimit, + Timestamp: timestamppb.New(h.Time), + ExtraData: []byte("0x"), + MixHash: common.Hash{}.Bytes(), + Nonce: types.BlockNonce{}.Uint64(), // PoW specific, + BaseFeePerGas: firehoseBigIntFromNative(baseFee), + + // Only set on Polygon fork(s) + TxDependency: nil, + } + + return pbHead +} + // FIXME: Bring back Firehose test that ensures no new tx type are missed func transactionTypeFromChainTxType(txType uint8) pbeth.TransactionTrace_Type { switch txType { diff --git a/x/evm/tracers/firehose_test.go b/x/evm/tracers/firehose_test.go index fcf84bdeb3..6556518352 100644 --- a/x/evm/tracers/firehose_test.go +++ b/x/evm/tracers/firehose_test.go @@ -339,9 +339,10 @@ func TestFirehose_reorderIsolatedTransactionsAndOrdinals(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - f := NewFirehose(&FirehoseConfig{ + f, err := newFirehose(&FirehoseConfig{ ApplyBackwardCompatibility: ptr(false), }) + require.NoError(t, err) f.OnBlockchainInit(params.AllEthashProtocolChanges) tt.populate(f) diff --git a/x/evm/tracers/registry.go b/x/evm/tracers/registry.go new file mode 100644 index 0000000000..71731343dd --- /dev/null +++ b/x/evm/tracers/registry.go @@ -0,0 +1,31 @@ +package tracers + +var GlobalLiveTracerRegistry = NewLiveTracerRegistry() + +type LiveTracerRegistry interface { + GetFactoryByID(id string) (BlockchainTracerFactory, bool) + Register(id string, factory BlockchainTracerFactory) +} + +var _ LiveTracerRegistry = (*liveTracerRegistry)(nil) + +func NewLiveTracerRegistry() LiveTracerRegistry { + return &liveTracerRegistry{ + tracers: make(map[string]BlockchainTracerFactory), + } +} + +type liveTracerRegistry struct { + tracers map[string]BlockchainTracerFactory +} + +// Register implements LiveTracerRegistry. +func (r *liveTracerRegistry) Register(id string, factory BlockchainTracerFactory) { + r.tracers[id] = factory +} + +// GetFactoryByID implements LiveTracerRegistry. +func (r *liveTracerRegistry) GetFactoryByID(id string) (BlockchainTracerFactory, bool) { + v, found := r.tracers[id] + return v, found +} diff --git a/x/evm/tracers/tracing.go b/x/evm/tracers/tracing.go new file mode 100644 index 0000000000..c8d19ef146 --- /dev/null +++ b/x/evm/tracers/tracing.go @@ -0,0 +1,12 @@ +package tracers + +import ( + "github.com/evmos/ethermint/x/evm/tracing" +) + +// BlockchainTracerFactory is a function that creates a new [BlockchainTracer]. +// It's going to receive the parsed URL from the `live-evm-tracer` flag. +// +// The scheme of the URL is going to be used to determine which tracer to use +// by the registry. +type BlockchainTracerFactory = func(backwardCompatibility bool) (*tracing.Hooks, error) diff --git a/x/evm/tracing/hooks.go b/x/evm/tracing/hooks.go new file mode 100644 index 0000000000..8f4cfadb9c --- /dev/null +++ b/x/evm/tracing/hooks.go @@ -0,0 +1,32 @@ +package tracing + +import ( + "github.com/cometbft/cometbft/types" + "github.com/ethereum/go-ethereum/core/tracing" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "math/big" +) + +type ( + OnCosmosBlockStart func(CosmosStartBlockEvent) + OnCosmosBlockEnd func(CosmosEndBlockEvent, error) +) + +type Hooks struct { + *tracing.Hooks + + OnCosmosBlockStart OnCosmosBlockStart + OnCosmosBlockEnd OnCosmosBlockEnd +} + +type CosmosStartBlockEvent struct { + CosmosHeader types.Header + BaseFee *big.Int + GasLimit uint64 + Coinbase []byte + Finalized *ethtypes.Header +} + +type CosmosEndBlockEvent struct { + LogsBloom []byte +} diff --git a/x/evm/types/tracer.go b/x/evm/types/tracer.go index ac4c034d77..87bafd1a68 100644 --- a/x/evm/types/tracer.go +++ b/x/evm/types/tracer.go @@ -17,9 +17,11 @@ package types import ( "fmt" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "os" "github.com/ethereum/go-ethereum/eth/tracers" + cosmostracers "github.com/evmos/ethermint/x/evm/tracers" _ "github.com/ethereum/go-ethereum/eth/tracers/live" _ "github.com/evmos/ethermint/x/evm/tracers" @@ -83,6 +85,15 @@ func NewLiveTracer(tracer string) (*tracers.Tracer, error) { }, nil } +func NewFirehoseCosmosLiveTracer() (*cosmostracing.Hooks, error) { + h, err := cosmostracers.NewCosmosFirehoseTracer(false) + if err != nil { + return nil, fmt.Errorf("initializing live tracer firehose: %w", err) + } + + return h, nil +} + // TxTraceResult is the result of a single transaction trace during a block trace. type TxTraceResult struct { Result interface{} `json:"result,omitempty"` // Trace results produced by the tracer From 091c32aedbe15464e6644b22e8cf25c4134fac53 Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Wed, 9 Oct 2024 10:01:22 -0400 Subject: [PATCH 13/45] fix block header differences bet/ firehose tracer and eth_getBlock method call --- x/evm/keeper/keeper_firehose.go | 2 +- x/evm/tracers/firehose.go | 60 ++++++++++++++++++++------------- x/evm/tracing/hooks.go | 2 +- 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/x/evm/keeper/keeper_firehose.go b/x/evm/keeper/keeper_firehose.go index f14ecc507c..21a99174af 100644 --- a/x/evm/keeper/keeper_firehose.go +++ b/x/evm/keeper/keeper_firehose.go @@ -43,7 +43,7 @@ func ToCosmosStartBlockEvent(k *Keeper, ctx sdk.Context, coinbaseBytes []byte, b } return tracing.CosmosStartBlockEvent{ - CosmosHeader: h, + CosmosHeader: &h, BaseFee: baseFee, GasLimit: gasLimit, Coinbase: coinbaseBytes, diff --git a/x/evm/tracers/firehose.go b/x/evm/tracers/firehose.go index f407e32f3f..3ad0bcaf4c 100644 --- a/x/evm/tracers/firehose.go +++ b/x/evm/tracers/firehose.go @@ -183,7 +183,7 @@ type Firehose struct { // Block state block *pbeth.Block - cosmosBlockHeader cosmostypes.Header + cosmosBlockHeader *cosmostypes.Header //fixme: this is a hack, waiting for proto changes lastBlockHash []byte @@ -433,20 +433,6 @@ func (f *Firehose) OnCosmosBlockStart(event cosmostracing.CosmosStartBlockEvent) Ver: 4, } - // TODO: fetch the real parentHash from the event - if header.Height == 1 { - f.lastParentBlockHash = []byte("0000000000000000000000000000000000000000000000000000000000000000") - } - - if header.Height > 1 { - // move the parentHash to the previous block - f.lastParentBlockHash = f.lastBlockHash - fmt.Println("doudou", f.lastParentBlockHash) - f.block.Header.ParentHash = f.lastParentBlockHash - } - - f.lastBlockHash = f.block.Header.Hash - if *f.applyBackwardCompatibility { f.block.Ver = 3 } @@ -514,15 +500,37 @@ func (f *Firehose) OnBlockEnd(err error) { func (f *Firehose) OnCosmosBlockEnd(event cosmostracing.CosmosEndBlockEvent, err error) { firehoseInfo("block ending (err=%s)", errorView(err)) - f.block.Hash = f.cosmosBlockHeader.Hash() + + // TODO: fetch the real parentHash from the event, implement this once the proto definition changes + if f.cosmosBlockHeader.Height == 1 { + f.lastParentBlockHash = []byte("0000000000000000000000000000000000000000000000000000000000000000") + } + + if f.cosmosBlockHeader.Height > 1 { + // move the parentHash to the previous block + f.lastParentBlockHash = f.lastBlockHash + f.block.Header.ParentHash = f.lastParentBlockHash + fmt.Println("doudou: parent block hash", f.block.Header.ParentHash) + + f.cosmosBlockHeader.LastBlockID = cosmostypes.BlockID{ + Hash: f.lastParentBlockHash, + // Missing PartSetHeader + } + } + + f.block.Header.Hash = f.cosmosBlockHeader.Hash() + f.block.Hash = f.block.Header.Hash f.block.Header.LogsBloom = types.BytesToBloom(event.LogsBloom).Bytes() - f.block.Size = 10 // todo: find the right size + // todo: find the right size + f.block.Size = 10 for _, trx := range f.block.TransactionTraces { f.block.Header.GasUsed += trx.GasUsed } + f.lastBlockHash = f.block.Header.Hash + if err == nil { if f.blockReorderOrdinal { f.reorderIsolatedTransactionsAndOrdinals() @@ -1676,24 +1684,28 @@ func newBlockHeaderFromChainHeader(h *types.Header, td *pbeth.BigInt) *pbeth.Blo } // FIXME: Create a unit test that is going to fail as soon as any header is added in -func newBlockHeaderFromCosmosChainHeader(h cosmostypes.Header, coinbase []byte, gasLimit uint64, baseFee *big.Int) *pbeth.BlockHeader { +func newBlockHeaderFromCosmosChainHeader(h *cosmostypes.Header, coinbase []byte, gasLimit uint64, baseFee *big.Int) *pbeth.BlockHeader { difficulty := firehoseBigIntFromNative(new(big.Int).SetInt64(0)) + transactionRoot := types.EmptyRootHash.Bytes() + if h.DataHash != nil { + transactionRoot = h.DataHash + } + + // the hash is calculated by the end block as we are missing some data + // same applies with the parent hash pbHead := &pbeth.BlockHeader{ - Hash: h.Hash().Bytes(), - Number: uint64(h.Height), - //ParentHash: h.LastBlockID.Hash, + Number: uint64(h.Height), UncleHash: types.EmptyUncleHash.Bytes(), // No uncles in Tendermint Coinbase: coinbase, StateRoot: h.AppHash, - TransactionsRoot: h.DataHash, + TransactionsRoot: transactionRoot, ReceiptRoot: types.EmptyRootHash.Bytes(), - LogsBloom: types.EmptyVerkleHash.Bytes(), // TODO: fix this Difficulty: difficulty, TotalDifficulty: difficulty, GasLimit: gasLimit, Timestamp: timestamppb.New(h.Time), - ExtraData: []byte("0x"), + ExtraData: []byte(nil), MixHash: common.Hash{}.Bytes(), Nonce: types.BlockNonce{}.Uint64(), // PoW specific, BaseFeePerGas: firehoseBigIntFromNative(baseFee), diff --git a/x/evm/tracing/hooks.go b/x/evm/tracing/hooks.go index 8f4cfadb9c..84ff1e4fd1 100644 --- a/x/evm/tracing/hooks.go +++ b/x/evm/tracing/hooks.go @@ -20,7 +20,7 @@ type Hooks struct { } type CosmosStartBlockEvent struct { - CosmosHeader types.Header + CosmosHeader *types.Header BaseFee *big.Int GasLimit uint64 Coinbase []byte From c6b80d9be7820abc46db8197cf77a2ccff47133a Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Wed, 9 Oct 2024 10:02:09 -0400 Subject: [PATCH 14/45] removing debug log --- x/evm/tracers/firehose.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/evm/tracers/firehose.go b/x/evm/tracers/firehose.go index 3ad0bcaf4c..8989d24ec8 100644 --- a/x/evm/tracers/firehose.go +++ b/x/evm/tracers/firehose.go @@ -510,7 +510,6 @@ func (f *Firehose) OnCosmosBlockEnd(event cosmostracing.CosmosEndBlockEvent, err // move the parentHash to the previous block f.lastParentBlockHash = f.lastBlockHash f.block.Header.ParentHash = f.lastParentBlockHash - fmt.Println("doudou: parent block hash", f.block.Header.ParentHash) f.cosmosBlockHeader.LastBlockID = cosmostypes.BlockID{ Hash: f.lastParentBlockHash, From de3f90e04e849f87fe0483d74022b1d9f70ef64d Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Wed, 9 Oct 2024 18:18:08 -0400 Subject: [PATCH 15/45] update statedb tracer - add tests for statedb balance change - fix add and sub balance changes - update dependencies to cometbft and cosmos-sdk --- go.mod | 17 +++--- go.sum | 29 ++++------ x/evm/keeper/state_transition.go | 36 ++++++------ x/evm/statedb/statedb.go | 31 ++++++++-- x/evm/statedb/statedb_test.go | 99 +++++++++++++++++++++++++++++++- x/evm/tracers/firehose.go | 23 ++------ x/evm/tracers/tracing.go | 54 +++++++++++++++++ 7 files changed, 216 insertions(+), 73 deletions(-) diff --git a/go.mod b/go.mod index bd10294684..09600491ca 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.22.5 require ( cosmossdk.io/api v0.7.5 cosmossdk.io/client/v2 v2.0.0-beta.1 - cosmossdk.io/core v0.11.0 + cosmossdk.io/core v0.11.1 cosmossdk.io/errors v1.0.1 cosmossdk.io/log v1.3.1 cosmossdk.io/math v1.3.0 @@ -15,11 +15,11 @@ require ( cosmossdk.io/tools/confix v0.1.1 cosmossdk.io/x/evidence v0.1.0 cosmossdk.io/x/feegrant v0.1.0 - cosmossdk.io/x/tx v0.13.3 + cosmossdk.io/x/tx v0.13.4 cosmossdk.io/x/upgrade v0.1.1 github.com/btcsuite/btcd v0.24.2 github.com/btcsuite/btcd/btcutil v1.1.5 - github.com/cometbft/cometbft v0.38.9 + github.com/cometbft/cometbft v0.38.10 github.com/cosmos/cosmos-db v1.0.2 github.com/cosmos/cosmos-proto v1.0.0-beta.5 github.com/cosmos/cosmos-sdk v0.50.6 @@ -66,7 +66,7 @@ require ( cloud.google.com/go/iam v1.1.6 // indirect cloud.google.com/go/storage v1.38.0 // indirect cosmossdk.io/collections v0.4.0 // indirect - cosmossdk.io/depinject v1.0.0-alpha.4 // indirect + cosmossdk.io/depinject v1.0.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.2 // indirect @@ -96,7 +96,6 @@ require ( github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/cockroachdb/errors v1.11.3 // indirect @@ -189,8 +188,6 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/klauspost/compress v1.17.7 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect - github.com/knz/go-libedit v1.10.1 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/lib/pq v1.10.7 // indirect @@ -262,7 +259,7 @@ require ( golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/api v0.169.0 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240709173604-40e1e62336c5 // indirect gopkg.in/DataDog/dd-trace-go.v1 v1.62.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect @@ -278,8 +275,8 @@ replace ( cosmossdk.io/store => github.com/InjectiveLabs/cosmos-sdk/store v0.0.0-20240904140803-b4127ecb5410 cosmossdk.io/x/tx => github.com/InjectiveLabs/cosmos-sdk/x/tx v0.0.0-20240904140803-b4127ecb5410 - github.com/cometbft/cometbft => github.com/InjectiveLabs/cometbft v0.38.11-inj-2 - github.com/cosmos/cosmos-sdk => github.com/InjectiveLabs/cosmos-sdk v0.50.9-0.20240904140803-b4127ecb5410 + github.com/cometbft/cometbft => github.com/InjectiveLabs/cometbft v0.38.11-inj-3 + github.com/cosmos/cosmos-sdk => github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241009150651-2a2420946b5a github.com/ethereum/go-ethereum => github.com/InjectiveLabs/go-ethereum v1.9.22-0.20240923100242-5e28e23d353e nhooyr.io/websocket => github.com/coder/websocket v1.8.10 // replaced as instructed here:https://coder.com/blog/websocket ) diff --git a/go.sum b/go.sum index 8a58d44395..fd535b7e28 100644 --- a/go.sum +++ b/go.sum @@ -186,10 +186,10 @@ cosmossdk.io/api v0.7.5 h1:eMPTReoNmGUm8DeiQL9DyM8sYDjEhWzL1+nLbI9DqtQ= cosmossdk.io/api v0.7.5/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38= cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= -cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo= -cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= -cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= -cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= +cosmossdk.io/core v0.11.1 h1:h9WfBey7NAiFfIcUhDVNS503I2P2HdZLebJlUIs8LPA= +cosmossdk.io/core v0.11.1/go.mod h1:OJzxcdC+RPrgGF8NJZR2uoQr56tc7gfBKhiKeDO7hH0= +cosmossdk.io/depinject v1.0.0 h1:dQaTu6+O6askNXO06+jyeUAnF2/ssKwrrszP9t5q050= +cosmossdk.io/depinject v1.0.0/go.mod h1:zxK/h3HgHoA/eJVtiSsoaRaRA2D5U4cJ5thIG4ssbB8= cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= @@ -238,10 +238,10 @@ github.com/DataDog/sketches-go v1.4.2 h1:gppNudE9d19cQ98RYABOetxIhpTCl4m7CnbRZjv github.com/DataDog/sketches-go v1.4.2/go.mod h1:xJIXldczJyyjnbDop7ZZcLxJdV3+7Kra7H1KMgpgkLk= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/InjectiveLabs/cometbft v0.38.11-inj-2 h1:TkmEMtDjwLk0r6X403BT2Gt0ZFB2tdabc1n5kPdK5H4= -github.com/InjectiveLabs/cometbft v0.38.11-inj-2/go.mod h1:WreBJKC7CtZ9cNsb5p6uNpfCBwNWDu+risVb58GTLfY= -github.com/InjectiveLabs/cosmos-sdk v0.50.9-0.20240904140803-b4127ecb5410 h1:GiHnayUM2oAXPWPpkKfMHSRyBZcA0LSzIJQq6hGuUVo= -github.com/InjectiveLabs/cosmos-sdk v0.50.9-0.20240904140803-b4127ecb5410/go.mod h1:igpF5mgbiTmaS3hO/yoIIv7+ICbzMBIowFWSw5aOrKA= +github.com/InjectiveLabs/cometbft v0.38.11-inj-3 h1:82i3DrztJxVs1R45wVTUKZ5+8g+HT5KPexu6mB92jaE= +github.com/InjectiveLabs/cometbft v0.38.11-inj-3/go.mod h1:WreBJKC7CtZ9cNsb5p6uNpfCBwNWDu+risVb58GTLfY= +github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241009150651-2a2420946b5a h1:MnASvPFfkvCBN7UQrmGyD101lJHubbE96gLwZ6TQlWs= +github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241009150651-2a2420946b5a/go.mod h1:VoDw+J+Xx6cQz/VUbXLXwQNm9S75a8zWAvKrXDqD3u4= github.com/InjectiveLabs/cosmos-sdk/client/v2 v2.0.0-20240904140803-b4127ecb5410 h1:gUiyEHPKz+S1vOb3DhVSpUvSreUR7Z/8Hv3OXws/7LA= github.com/InjectiveLabs/cosmos-sdk/client/v2 v2.0.0-20240904140803-b4127ecb5410/go.mod h1:CZcL41HpJPOOayTCO28j8weNBQprG+SRiKX39votypo= github.com/InjectiveLabs/cosmos-sdk/store v0.0.0-20240904140803-b4127ecb5410 h1:6ai8wHEoV48EACjoGpqW8uxuwCcZfYMBU7FEoC0R0lI= @@ -353,8 +353,6 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= -github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= -github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= @@ -858,11 +856,6 @@ github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/knz/go-libedit v1.10.1 h1:0pHpWtx9vcvC0xGZqEQlQdfSQs7WRlAjuPvk3fOZDCo= -github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -1503,7 +1496,6 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1782,8 +1774,8 @@ google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUE google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240709173604-40e1e62336c5 h1:SbSDUWW1PAO24TNpLdeheoYPd7kllICcLU52x6eD4kQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240709173604-40e1e62336c5/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1895,7 +1887,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 4e6dca2958..8640687f61 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -348,7 +348,7 @@ func (k *Keeper) ApplyMessageWithConfig( vmCfg := evm.Config if vmCfg.Tracer != nil { - stateDB.SetTracer(vmCfg.Tracer) + stateDB.SetTracer(k.evmTracer) vmCfg.Tracer.OnTxStart( evm.GetVMContext(), ethtypes.NewTx(ðtypes.LegacyTx{ @@ -362,25 +362,25 @@ func (k *Keeper) ApplyMessageWithConfig( msg.From, ) - if cfg.DebugTrace { - // msg.GasPrice should have been set to effective gas price - senderAddr := sender.Address() - stateDB.SubBalance( - sender.Address(), - uint256.MustFromBig(new(big.Int).Mul(msg.GasPrice, new(big.Int).SetUint64(msg.GasLimit))), - tracing.BalanceChangeUnspecified, - ) - stateDB.SetNonce(senderAddr, stateDB.GetNonce(senderAddr)+1) - } + //if cfg.DebugTrace { + // // msg.GasPrice should have been set to effective gas price + // senderAddr := sender.Address() + // stateDB.SubBalance( + // sender.Address(), + // uint256.MustFromBig(new(big.Int).Mul(msg.GasPrice, new(big.Int).SetUint64(msg.GasLimit))), + // tracing.BalanceChangeUnspecified, + // ) + // stateDB.SetNonce(senderAddr, stateDB.GetNonce(senderAddr)+1) + //} defer func() { - if cfg.DebugTrace { - stateDB.AddBalance( - sender.Address(), - uint256.MustFromBig(new(big.Int).Mul(msg.GasPrice, new(big.Int).SetUint64(leftoverGas))), - tracing.BalanceChangeUnspecified, - ) - } + //if cfg.DebugTrace { + // stateDB.AddBalance( + // sender.Address(), + // uint256.MustFromBig(new(big.Int).Mul(msg.GasPrice, new(big.Int).SetUint64(leftoverGas))), + // tracing.BalanceChangeUnspecified, + // ) + //} traceErr := err if vmErr != nil { diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index 15c1c61228..0ff3663321 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -17,6 +17,7 @@ package statedb import ( "fmt" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "math/big" "sort" @@ -101,7 +102,7 @@ type StateDB struct { nativeEvents sdk.Events // EVM Tracer - evmTracer *tracing.Hooks + evmTracer *cosmostracing.Hooks // handle balances natively evmDenom string @@ -146,7 +147,7 @@ func NewWithParams(ctx sdk.Context, keeper Keeper, txConfig TxConfig, evmDenom s return db } -func (s *StateDB) SetTracer(tracer *tracing.Hooks) { +func (s *StateDB) SetTracer(tracer *cosmostracing.Hooks) { s.evmTracer = tracer } @@ -431,12 +432,30 @@ func (s *StateDB) Transfer(sender, recipient common.Address, amount *big.Int) { coins := sdk.NewCoins(sdk.NewCoin(s.evmDenom, sdkmath.NewIntFromBigIntMut(amount))) senderAddr := sdk.AccAddress(sender.Bytes()) + senderBalance := s.GetBalance(sender) recipientAddr := sdk.AccAddress(recipient.Bytes()) + recipientBalance := s.GetBalance(recipient) if err := s.ExecuteNativeAction(common.Address{}, nil, func(ctx sdk.Context) error { return s.keeper.Transfer(ctx, senderAddr, recipientAddr, coins) }); err != nil { s.err = err } + + if s.err == nil { + // sender + if s.evmTracer != nil && s.evmTracer.OnBalanceChange != nil { + newBalance := new(big.Int) + newBalance.Sub(senderBalance.ToBig(), amount) + s.evmTracer.OnBalanceChange(common.BytesToAddress(sender.Bytes()), senderBalance.ToBig(), newBalance, tracing.BalanceChangeTransfer) + } + + // recipient + if s.evmTracer != nil && s.evmTracer.OnBalanceChange != nil { + newBalance := new(big.Int) + newBalance.Add(recipientBalance.ToBig(), amount) + s.evmTracer.OnBalanceChange(common.BytesToAddress(recipient.Bytes()), recipientBalance.ToBig(), newBalance, tracing.BalanceChangeTransfer) + } + } } // AddBalance adds amount to the account associated with addr. @@ -456,7 +475,7 @@ func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int, reason tr s.err = err } - if s.err != nil { + if s.err == nil { if s.evmTracer != nil && s.evmTracer.OnBalanceChange != nil { newBalance := new(big.Int) newBalance.Add(balance.ToBig(), amount.ToBig()) @@ -482,10 +501,10 @@ func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int, reason tr s.err = err } - if s.err != nil { + if s.err == nil { if s.evmTracer != nil && s.evmTracer.OnBalanceChange != nil { newBalance := new(big.Int) - new(big.Int).Sub(balance.ToBig(), amount.ToBig()) + newBalance.Sub(balance.ToBig(), amount.ToBig()) s.evmTracer.OnBalanceChange(addr, balance.ToBig(), newBalance, reason) } } @@ -500,7 +519,7 @@ func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) { s.err = err } - if s.err != nil { + if s.err == nil { if s.evmTracer != nil && s.evmTracer.OnBalanceChange != nil { s.evmTracer.OnBalanceChange(addr, balance.ToBig(), amount, tracing.BalanceChangeUnspecified) } diff --git a/x/evm/statedb/statedb_test.go b/x/evm/statedb/statedb_test.go index 63621f2431..c81f106df5 100644 --- a/x/evm/statedb/statedb_test.go +++ b/x/evm/statedb/statedb_test.go @@ -2,7 +2,9 @@ package statedb_test import ( "errors" + "fmt" "math/big" + "strings" "testing" "cosmossdk.io/log" @@ -23,6 +25,7 @@ import ( capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/tracing" + ethtracing "github.com/ethereum/go-ethereum/core/tracing" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -45,11 +48,105 @@ var ( emptyTxConfig statedb.TxConfig = statedb.NewEmptyTxConfig(blockHash) ) +type balanceChange struct { + // We use string to avoid big.Int equality issues + old string + new string + reason ethtracing.BalanceChangeReason +} + +func balanceChangesValues(changes []balanceChange) string { + out := make([]string, len(changes)) + for i, change := range changes { + out[i] = fmt.Sprintf("{%q, %q, ethtracing.BalanceChangeReason(%d)}", change.old, change.new, change.reason) + } + + return strings.Join(out, "\n") +} + type StateDBTestSuite struct { suite.Suite } -// TODO: add tests with the Tracer +func (suite *StateDBTestSuite) TestTracer_Balance() { + testCases := []struct { + name string + malleate func(*statedb.StateDB) + expBalance *big.Int + expBalanceChanges int + }{ + { + name: "1 balance change - Add", + malleate: func(db *statedb.StateDB) { + db.AddBalance(address, uint256.NewInt(10), tracing.BalanceChangeUnspecified) + }, + expBalance: big.NewInt(10), + expBalanceChanges: 1, + }, + { + name: "2 balance changes - Add and Sub", + malleate: func(db *statedb.StateDB) { + db.AddBalance(address, uint256.NewInt(10), tracing.BalanceChangeUnspecified) + // get dirty balance + suite.Require().Equal(uint256.NewInt(10), db.GetBalance(address)) + db.SubBalance(address, uint256.NewInt(2), tracing.BalanceChangeUnspecified) + }, + expBalance: big.NewInt(8), + expBalanceChanges: 2, + }, + { + name: "add zero balance", + malleate: func(db *statedb.StateDB) { + db.AddBalance(address, uint256.NewInt(0), tracing.BalanceChangeUnspecified) + }, + expBalance: big.NewInt(0), + expBalanceChanges: 0, + }, + { + name: "sub zero balance", + malleate: func(db *statedb.StateDB) { + db.SubBalance(address, uint256.NewInt(0), tracing.BalanceChangeUnspecified) + }, + expBalance: big.NewInt(0), + expBalanceChanges: 0, + }, + { + name: "transfer", + malleate: func(db *statedb.StateDB) { + db.AddBalance(address, uint256.NewInt(10), tracing.BalanceChangeUnspecified) + db.Transfer(address, address2, big.NewInt(10)) + suite.Require().Equal(uint256.NewInt(10), db.GetBalance(address2)) + }, + expBalance: big.NewInt(0), + expBalanceChanges: 3, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + var balanceChanges []balanceChange + raw, ctx, keeper := setupTestEnv(suite.T()) + t, err := types.NewFirehoseCosmosLiveTracer() + require.NoError(suite.T(), err) + t.OnBalanceChange = func(addr common.Address, prev, new *big.Int, reason ethtracing.BalanceChangeReason) { + balanceChanges = append(balanceChanges, balanceChange{prev.String(), new.String(), reason}) + } + keeper.SetTracer(t) + db := statedb.New(ctx, keeper, emptyTxConfig) + db.SetTracer(t) + tc.malleate(db) + + // check dirty state + suite.Require().Equal(uint256.MustFromBig(tc.expBalance), db.GetBalance(address)) + suite.Require().NoError(db.Commit()) + + ctx, keeper = newTestKeeper(suite.T(), raw) + // check committed balance too + suite.Require().Equal(tc.expBalance, keeper.GetEVMDenomBalance(ctx, address)) + suite.Require().Equal(tc.expBalanceChanges, len(balanceChanges)) + }) + } +} func (suite *StateDBTestSuite) TestAccount() { key1 := common.BigToHash(big.NewInt(1)) diff --git a/x/evm/tracers/firehose.go b/x/evm/tracers/firehose.go index 8989d24ec8..00fd1983cf 100644 --- a/x/evm/tracers/firehose.go +++ b/x/evm/tracers/firehose.go @@ -116,7 +116,7 @@ func NewTracingHooksFromFirehose(tracer *Firehose) *tracing.Hooks { OnOpcode: tracer.OnOpcode, OnFault: tracer.OnOpcodeFault, - OnBalanceChange: tracer.OnBalanceChange, // todo: this should be called when the block is done to calculate the balance changes in terms of rewards to the miner?? + OnBalanceChange: tracer.OnBalanceChange, OnNonceChange: tracer.OnNonceChange, OnCodeChange: tracer.OnCodeChange, OnStorageChange: tracer.OnStorageChange, @@ -501,25 +501,9 @@ func (f *Firehose) OnBlockEnd(err error) { func (f *Firehose) OnCosmosBlockEnd(event cosmostracing.CosmosEndBlockEvent, err error) { firehoseInfo("block ending (err=%s)", errorView(err)) - // TODO: fetch the real parentHash from the event, implement this once the proto definition changes - if f.cosmosBlockHeader.Height == 1 { - f.lastParentBlockHash = []byte("0000000000000000000000000000000000000000000000000000000000000000") - } - - if f.cosmosBlockHeader.Height > 1 { - // move the parentHash to the previous block - f.lastParentBlockHash = f.lastBlockHash - f.block.Header.ParentHash = f.lastParentBlockHash - - f.cosmosBlockHeader.LastBlockID = cosmostypes.BlockID{ - Hash: f.lastParentBlockHash, - // Missing PartSetHeader - } - } - + f.block.Header.LogsBloom = types.BytesToBloom(event.LogsBloom).Bytes() f.block.Header.Hash = f.cosmosBlockHeader.Hash() f.block.Hash = f.block.Header.Hash - f.block.Header.LogsBloom = types.BytesToBloom(event.LogsBloom).Bytes() // todo: find the right size f.block.Size = 10 @@ -1265,7 +1249,7 @@ func (f *Firehose) OnBalanceChange(a common.Address, prev, new *big.Int, reason if *f.applyBackwardCompatibility { // Known Firehose issue: It's possible to burn Ether by sending some ether to a suicided account. In those case, - // at theend of block producing, StateDB finalize the block by burning ether from the account. This is something + // at the end of block producing, StateDB finalize the block by burning ether from the account. This is something // we were not tracking in the old Firehose instrumentation. if reason == tracing.BalanceDecreaseSelfdestructBurn { return @@ -1694,6 +1678,7 @@ func newBlockHeaderFromCosmosChainHeader(h *cosmostypes.Header, coinbase []byte, // the hash is calculated by the end block as we are missing some data // same applies with the parent hash pbHead := &pbeth.BlockHeader{ + ParentHash: h.LastBlockID.Hash, Number: uint64(h.Height), UncleHash: types.EmptyUncleHash.Bytes(), // No uncles in Tendermint Coinbase: coinbase, diff --git a/x/evm/tracers/tracing.go b/x/evm/tracers/tracing.go index c8d19ef146..0e1411bf0d 100644 --- a/x/evm/tracers/tracing.go +++ b/x/evm/tracers/tracing.go @@ -1,6 +1,9 @@ package tracers import ( + "context" + sdk "github.com/cosmos/cosmos-sdk/types" + cosmostracing "github.com/ethereum/go-ethereum/core/tracing" "github.com/evmos/ethermint/x/evm/tracing" ) @@ -10,3 +13,54 @@ import ( // The scheme of the URL is going to be used to determine which tracer to use // by the registry. type BlockchainTracerFactory = func(backwardCompatibility bool) (*tracing.Hooks, error) + +type CtxBlockchainTracerKeyType string + +const CtxBlockchainTracerKey = CtxBlockchainTracerKeyType("evm_and_state_logger") + +func SetCtxBlockchainTracer(ctx context.Context, hooks *cosmostracing.Hooks) context.Context { + return context.WithValue(ctx, CtxBlockchainTracerKey, hooks) +} + +func GetCtxBlockchainTracer(ctx sdk.Context) *cosmostracing.Hooks { + rawVal := ctx.Context().Value(CtxBlockchainTracerKey) + if rawVal == nil { + return nil + } + logger, ok := rawVal.(*cosmostracing.Hooks) + if !ok { + return nil + } + return logger +} + +func GetCtxEthTracingHooks(ctx sdk.Context) *cosmostracing.Hooks { + if logger := GetCtxBlockchainTracer(ctx); logger != nil { + return logger + } + + return nil +} + +type TxTracerHooks struct { + Hooks *cosmostracing.Hooks + + OnTxReset func() + OnTxCommit func() +} + +func (h TxTracerHooks) InjectInContext(ctx sdk.Context) context.Context { + return SetCtxBlockchainTracer(ctx, h.Hooks) +} + +func (h TxTracerHooks) Reset() { + if h.OnTxReset != nil { + h.OnTxReset() + } +} + +func (h TxTracerHooks) Commit() { + if h.OnTxCommit != nil { + h.OnTxCommit() + } +} From 5558c12e22cdf54ec5b68fcf2cdead599c3f7053 Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Wed, 9 Oct 2024 18:34:44 -0400 Subject: [PATCH 16/45] adding balance change setBalance changes --- x/evm/statedb/statedb_test.go | 45 +++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/x/evm/statedb/statedb_test.go b/x/evm/statedb/statedb_test.go index c81f106df5..6687d92558 100644 --- a/x/evm/statedb/statedb_test.go +++ b/x/evm/statedb/statedb_test.go @@ -120,6 +120,51 @@ func (suite *StateDBTestSuite) TestTracer_Balance() { expBalance: big.NewInt(0), expBalanceChanges: 3, }, + { + name: "multiple transfers", + malleate: func(db *statedb.StateDB) { + db.AddBalance(address, uint256.NewInt(10), tracing.BalanceChangeUnspecified) + db.AddBalance(address2, uint256.NewInt(10), tracing.BalanceChangeUnspecified) + db.Transfer(address, address2, big.NewInt(10)) + db.Transfer(address2, address, big.NewInt(5)) + suite.Require().Equal(uint256.NewInt(15), db.GetBalance(address2)) + }, + expBalance: big.NewInt(5), + expBalanceChanges: 6, + }, + { + name: "set balance", + malleate: func(db *statedb.StateDB) { + db.SetBalance(address, uint256.NewInt(10).ToBig()) + suite.Require().Equal(uint256.NewInt(10), db.GetBalance(address)) + }, + expBalance: big.NewInt(10), + expBalanceChanges: 1, + }, + { + name: "multiple set balance", + malleate: func(db *statedb.StateDB) { + db.SetBalance(address, uint256.NewInt(10).ToBig()) + db.SetBalance(address2, uint256.NewInt(10).ToBig()) + suite.Require().Equal(uint256.NewInt(10), db.GetBalance(address)) + suite.Require().Equal(uint256.NewInt(10), db.GetBalance(address2)) + }, + expBalance: big.NewInt(10), + expBalanceChanges: 2, + }, + { + name: "multiple set balance and some transfers", + malleate: func(db *statedb.StateDB) { + db.SetBalance(address, uint256.NewInt(10).ToBig()) + db.SetBalance(address2, uint256.NewInt(10).ToBig()) + db.Transfer(address, address2, big.NewInt(10)) + db.Transfer(address2, address, big.NewInt(5)) + suite.Require().Equal(uint256.NewInt(5), db.GetBalance(address)) + suite.Require().Equal(uint256.NewInt(15), db.GetBalance(address2)) + }, + expBalance: big.NewInt(5), + expBalanceChanges: 6, + }, } for _, tc := range testCases { From 929fec3efc003a9658ee1b981ed9d08189914e2b Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Thu, 10 Oct 2024 11:50:38 -0400 Subject: [PATCH 17/45] Update tracer tests for nonce changes, code changes and storage changes --- x/evm/statedb/statedb.go | 6 +- x/evm/statedb/statedb_test.go | 210 ++++++++++++++++++++++++++++++++++ 2 files changed, 215 insertions(+), 1 deletion(-) diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index 0ff3663321..bf62f02b63 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -569,7 +569,11 @@ func (s *StateDB) SetStorage(addr common.Address, storage Storage) { stateObject := s.getOrNewStateObject(addr) stateObject.SetStorage(storage) - // TODO: should we call a tracer hook here? + if s.evmTracer != nil && s.evmTracer.OnStorageChange != nil { + for key, value := range storage { + s.evmTracer.OnStorageChange(addr, key, s.GetState(addr, key), value) + } + } } // Suicide marks the given account as suicided. diff --git a/x/evm/statedb/statedb_test.go b/x/evm/statedb/statedb_test.go index 6687d92558..f90b41cc5a 100644 --- a/x/evm/statedb/statedb_test.go +++ b/x/evm/statedb/statedb_test.go @@ -302,6 +302,55 @@ func (suite *StateDBTestSuite) TestAccountOverride() { suite.Require().Equal(uint64(0), db.GetNonce(address)) } +func (suite *StateDBTestSuite) TestTracer_Nonce() { + testCases := []struct { + name string + malleate func(*statedb.StateDB) + expNonceChanges int + }{ + { + name: "set nonce to 1", + malleate: func(db *statedb.StateDB) { + db.SetNonce(address, 1) + }, + expNonceChanges: 1, + }, + { + name: "set nonce to 10", + malleate: func(db *statedb.StateDB) { + db.SetNonce(address, 10) + }, + expNonceChanges: 1, + }, + { + name: "multiple set nonces", + malleate: func(db *statedb.StateDB) { + db.SetNonce(address, 1) + db.SetNonce(address, 2) + }, + expNonceChanges: 2, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + var nonceChanges []uint64 + _, ctx, keeper := setupTestEnv(suite.T()) + t, err := types.NewFirehoseCosmosLiveTracer() + require.NoError(suite.T(), err) + t.OnNonceChange = func(addr common.Address, prev, new uint64) { + nonceChanges = append(nonceChanges, new) + } + keeper.SetTracer(t) + db := statedb.New(ctx, keeper, emptyTxConfig) + db.SetTracer(t) + tc.malleate(db) + + suite.Require().Equal(tc.expNonceChanges, len(nonceChanges)) + }) + } +} + func (suite *StateDBTestSuite) TestDBError() { testCases := []struct { name string @@ -469,6 +518,106 @@ func (suite *StateDBTestSuite) TestCode() { } } +type codeChange struct { + addr string + oldCode string + newCode string +} + +func newCodeChange(addr, oldCode, newCode string) codeChange { + return codeChange{ + addr: addr, + oldCode: oldCode, + newCode: newCode, + } +} + +func (suite *StateDBTestSuite) TestTracer_Code() { + code := []byte("hello world") + code2 := []byte("hello world 2") + codeHash := crypto.Keccak256Hash(code) + code2Hash := crypto.Keccak256Hash(code2) + + testCases := []struct { + name string + malleate func(vm.StateDB) + expCode []byte + expCodeHash common.Hash + expCodeChanges int + }{ + { + name: "non-exist account", + malleate: func(vm.StateDB) {}, + expCode: nil, + expCodeHash: common.Hash{}, + expCodeChanges: 0, + }, + { + name: "empty account", + malleate: func(db vm.StateDB) { + db.CreateAccount(address) + }, + expCode: nil, + expCodeHash: common.BytesToHash(emptyCodeHash), + expCodeChanges: 0, + }, + { + name: "set code", + malleate: func(db vm.StateDB) { + db.SetCode(address, code) + }, + expCode: code, + expCodeHash: codeHash, + expCodeChanges: 1, + }, + { + name: "set multiple code", + malleate: func(db vm.StateDB) { + + db.SetCode(address, code) + db.SetCode(address, code2) + }, + expCode: code2, + expCodeHash: code2Hash, + expCodeChanges: 2, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + var codeChanges []codeChange + raw, ctx, keeper := setupTestEnv(suite.T()) + db := statedb.New(ctx, keeper, emptyTxConfig) + t, err := evmtypes.NewFirehoseCosmosLiveTracer() + + t.OnCodeChange = func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) { + codeChanges = append(codeChanges, newCodeChange(addr.String(), string(prevCode), string(code))) + } + + require.NoError(suite.T(), err) + db.SetTracer(t) + tc.malleate(db) + + // check dirty state + suite.Require().Equal(tc.expCode, db.GetCode(address)) + suite.Require().Equal(len(tc.expCode), db.GetCodeSize(address)) + suite.Require().Equal(tc.expCodeHash, db.GetCodeHash(address)) + + suite.Require().NoError(db.Commit()) + + // check the committed state + ctx, keeper = newTestKeeper(suite.T(), raw) + db = statedb.New(ctx, keeper, emptyTxConfig) + suite.Require().Equal(tc.expCode, db.GetCode(address)) + suite.Require().Equal(len(tc.expCode), db.GetCodeSize(address)) + suite.Require().Equal(tc.expCodeHash, db.GetCodeHash(address)) + + // check code changes + suite.Require().Equal(tc.expCodeChanges, len(codeChanges)) + }) + } +} + func (suite *StateDBTestSuite) TestRevertSnapshot() { v1 := common.BigToHash(big.NewInt(1)) v2 := common.BigToHash(big.NewInt(2)) @@ -903,6 +1052,67 @@ func (suite *StateDBTestSuite) TestSetStorage() { suite.Require().Equal(common.Hash{}, stateDB.GetState(contract, common.BigToHash(big.NewInt(2)))) } +type storageChanges struct { + address string + key string + old string + new string +} + +func newStorageChange(addr, key, old, new string) storageChanges { + return storageChanges{ + address: addr, + key: key, + old: old, + new: new, + } +} + +func (suite *StateDBTestSuite) TestTracer_SetStorage() { + contract := common.BigToAddress(big.NewInt(101)) + + var sChanges []storageChanges + + _, ctx, keeper := setupTestEnv(suite.T()) + t, err := types.NewFirehoseCosmosLiveTracer() + require.NoError(suite.T(), err) + t.OnStorageChange = func(addr common.Address, slot common.Hash, prev, new common.Hash) { + sChanges = append(sChanges, newStorageChange(addr.String(), slot.String(), prev.String(), new.String())) + } + keeper.SetTracer(t) + stateDB := statedb.New(ctx, keeper, emptyTxConfig) + stateDB.SetTracer(t) + + stateDB.SetState(contract, common.BigToHash(big.NewInt(0)), common.BigToHash(big.NewInt(0))) + stateDB.SetState(contract, common.BigToHash(big.NewInt(1)), common.BigToHash(big.NewInt(1))) + stateDB.SetState(contract, common.BigToHash(big.NewInt(2)), common.BigToHash(big.NewInt(2))) + suite.Require().NoError(stateDB.Commit()) + suite.Require().Equal(3, len(sChanges)) + + suite.Require().Equal(common.BigToHash(big.NewInt(0)), stateDB.GetState(contract, common.BigToHash(big.NewInt(0)))) + suite.Require().Equal(common.BigToHash(big.NewInt(1)), stateDB.GetState(contract, common.BigToHash(big.NewInt(1)))) + suite.Require().Equal(common.BigToHash(big.NewInt(2)), stateDB.GetState(contract, common.BigToHash(big.NewInt(2)))) + + // single change to the storage + stateDB.SetStorage(contract, map[common.Hash]common.Hash{ + common.BigToHash(big.NewInt(1)): common.BigToHash(big.NewInt(3)), + }) + suite.Require().Equal(4, len(sChanges)) + + suite.Require().Equal(common.Hash{}, stateDB.GetState(contract, common.BigToHash(big.NewInt(0)))) + suite.Require().Equal(common.BigToHash(big.NewInt(3)), stateDB.GetState(contract, common.BigToHash(big.NewInt(1)))) + suite.Require().Equal(common.Hash{}, stateDB.GetState(contract, common.BigToHash(big.NewInt(2)))) + + // multiple changes to the storage + stateDB.SetStorage(contract, map[common.Hash]common.Hash{ + common.BigToHash(big.NewInt(2)): common.BigToHash(big.NewInt(3)), + common.BigToHash(big.NewInt(4)): common.BigToHash(big.NewInt(5)), + common.BigToHash(big.NewInt(6)): common.BigToHash(big.NewInt(7)), + common.BigToHash(big.NewInt(8)): common.BigToHash(big.NewInt(9)), + }) + suite.Require().Equal(8, len(sChanges)) +} + type StateDBWithForEachStorage interface { ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error } From bcb4d47142a6b09e7247e849953a5fd42fdd09b6 Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Thu, 10 Oct 2024 16:23:59 -0400 Subject: [PATCH 18/45] Bumping cometbft and cosmos-sdk versions --- go.mod | 4 ++-- go.sum | 8 ++++---- x/evm/tracers/firehose.go | 9 ++++++++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 09600491ca..32e013e6e2 100644 --- a/go.mod +++ b/go.mod @@ -275,8 +275,8 @@ replace ( cosmossdk.io/store => github.com/InjectiveLabs/cosmos-sdk/store v0.0.0-20240904140803-b4127ecb5410 cosmossdk.io/x/tx => github.com/InjectiveLabs/cosmos-sdk/x/tx v0.0.0-20240904140803-b4127ecb5410 - github.com/cometbft/cometbft => github.com/InjectiveLabs/cometbft v0.38.11-inj-3 - github.com/cosmos/cosmos-sdk => github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241009150651-2a2420946b5a + github.com/cometbft/cometbft => github.com/InjectiveLabs/cometbft v0.38.11-inj-4 + github.com/cosmos/cosmos-sdk => github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241010141128-de2b5199b23e github.com/ethereum/go-ethereum => github.com/InjectiveLabs/go-ethereum v1.9.22-0.20240923100242-5e28e23d353e nhooyr.io/websocket => github.com/coder/websocket v1.8.10 // replaced as instructed here:https://coder.com/blog/websocket ) diff --git a/go.sum b/go.sum index fd535b7e28..0c3e7a7900 100644 --- a/go.sum +++ b/go.sum @@ -238,10 +238,10 @@ github.com/DataDog/sketches-go v1.4.2 h1:gppNudE9d19cQ98RYABOetxIhpTCl4m7CnbRZjv github.com/DataDog/sketches-go v1.4.2/go.mod h1:xJIXldczJyyjnbDop7ZZcLxJdV3+7Kra7H1KMgpgkLk= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/InjectiveLabs/cometbft v0.38.11-inj-3 h1:82i3DrztJxVs1R45wVTUKZ5+8g+HT5KPexu6mB92jaE= -github.com/InjectiveLabs/cometbft v0.38.11-inj-3/go.mod h1:WreBJKC7CtZ9cNsb5p6uNpfCBwNWDu+risVb58GTLfY= -github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241009150651-2a2420946b5a h1:MnASvPFfkvCBN7UQrmGyD101lJHubbE96gLwZ6TQlWs= -github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241009150651-2a2420946b5a/go.mod h1:VoDw+J+Xx6cQz/VUbXLXwQNm9S75a8zWAvKrXDqD3u4= +github.com/InjectiveLabs/cometbft v0.38.11-inj-4 h1:WBL3gGcPEYscqpIH+r1IkytmMhoLt247JtO71AlZ/iQ= +github.com/InjectiveLabs/cometbft v0.38.11-inj-4/go.mod h1:WreBJKC7CtZ9cNsb5p6uNpfCBwNWDu+risVb58GTLfY= +github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241010141128-de2b5199b23e h1:fFy5HkvhhCcNsnkwM2pJf7+TiIWs6nOgCct6FLMI9i4= +github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241010141128-de2b5199b23e/go.mod h1:DAbn+xjBNvDKXf/XThMI/UADLyJi55Jo+ILfJ0NE4tQ= github.com/InjectiveLabs/cosmos-sdk/client/v2 v2.0.0-20240904140803-b4127ecb5410 h1:gUiyEHPKz+S1vOb3DhVSpUvSreUR7Z/8Hv3OXws/7LA= github.com/InjectiveLabs/cosmos-sdk/client/v2 v2.0.0-20240904140803-b4127ecb5410/go.mod h1:CZcL41HpJPOOayTCO28j8weNBQprG+SRiKX39votypo= github.com/InjectiveLabs/cosmos-sdk/store v0.0.0-20240904140803-b4127ecb5410 h1:6ai8wHEoV48EACjoGpqW8uxuwCcZfYMBU7FEoC0R0lI= diff --git a/x/evm/tracers/firehose.go b/x/evm/tracers/firehose.go index 00fd1983cf..148de47a00 100644 --- a/x/evm/tracers/firehose.go +++ b/x/evm/tracers/firehose.go @@ -418,6 +418,8 @@ func (f *Firehose) OnCosmosBlockStart(event cosmostracing.CosmosStartBlockEvent) gasLimit := event.GasLimit baseFee := event.BaseFee + // The block version seems to be 11 on each block, is there a way to get it? + header.Version.Block = 11 f.cosmosBlockHeader = header firehoseInfo("block start (number=%d)", header.Height) @@ -1670,6 +1672,11 @@ func newBlockHeaderFromChainHeader(h *types.Header, td *pbeth.BigInt) *pbeth.Blo func newBlockHeaderFromCosmosChainHeader(h *cosmostypes.Header, coinbase []byte, gasLimit uint64, baseFee *big.Int) *pbeth.BlockHeader { difficulty := firehoseBigIntFromNative(new(big.Int).SetInt64(0)) + parentBlockHash := h.LastBlockID.Hash + if h.Height == 1 { + parentBlockHash = common.Hash{}.Bytes() + } + transactionRoot := types.EmptyRootHash.Bytes() if h.DataHash != nil { transactionRoot = h.DataHash @@ -1678,7 +1685,7 @@ func newBlockHeaderFromCosmosChainHeader(h *cosmostypes.Header, coinbase []byte, // the hash is calculated by the end block as we are missing some data // same applies with the parent hash pbHead := &pbeth.BlockHeader{ - ParentHash: h.LastBlockID.Hash, + ParentHash: parentBlockHash, Number: uint64(h.Height), UncleHash: types.EmptyUncleHash.Bytes(), // No uncles in Tendermint Coinbase: coinbase, From 3f8d55ea1253465df34c22f27348d488c848e528 Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Fri, 11 Oct 2024 10:46:09 -0400 Subject: [PATCH 19/45] Update cometbft dependency with sf fork with changes done to PartSetHeader --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 32e013e6e2..71ab89c6bd 100644 --- a/go.mod +++ b/go.mod @@ -275,7 +275,7 @@ replace ( cosmossdk.io/store => github.com/InjectiveLabs/cosmos-sdk/store v0.0.0-20240904140803-b4127ecb5410 cosmossdk.io/x/tx => github.com/InjectiveLabs/cosmos-sdk/x/tx v0.0.0-20240904140803-b4127ecb5410 - github.com/cometbft/cometbft => github.com/InjectiveLabs/cometbft v0.38.11-inj-4 + github.com/cometbft/cometbft => github.com/streamingfast/cometbft v0.38.11-inj-5 github.com/cosmos/cosmos-sdk => github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241010141128-de2b5199b23e github.com/ethereum/go-ethereum => github.com/InjectiveLabs/go-ethereum v1.9.22-0.20240923100242-5e28e23d353e nhooyr.io/websocket => github.com/coder/websocket v1.8.10 // replaced as instructed here:https://coder.com/blog/websocket diff --git a/go.sum b/go.sum index 0c3e7a7900..e6f406d916 100644 --- a/go.sum +++ b/go.sum @@ -238,8 +238,6 @@ github.com/DataDog/sketches-go v1.4.2 h1:gppNudE9d19cQ98RYABOetxIhpTCl4m7CnbRZjv github.com/DataDog/sketches-go v1.4.2/go.mod h1:xJIXldczJyyjnbDop7ZZcLxJdV3+7Kra7H1KMgpgkLk= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/InjectiveLabs/cometbft v0.38.11-inj-4 h1:WBL3gGcPEYscqpIH+r1IkytmMhoLt247JtO71AlZ/iQ= -github.com/InjectiveLabs/cometbft v0.38.11-inj-4/go.mod h1:WreBJKC7CtZ9cNsb5p6uNpfCBwNWDu+risVb58GTLfY= github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241010141128-de2b5199b23e h1:fFy5HkvhhCcNsnkwM2pJf7+TiIWs6nOgCct6FLMI9i4= github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241010141128-de2b5199b23e/go.mod h1:DAbn+xjBNvDKXf/XThMI/UADLyJi55Jo+ILfJ0NE4tQ= github.com/InjectiveLabs/cosmos-sdk/client/v2 v2.0.0-20240904140803-b4127ecb5410 h1:gUiyEHPKz+S1vOb3DhVSpUvSreUR7Z/8Hv3OXws/7LA= @@ -1121,6 +1119,8 @@ github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9 github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/streamingfast/cometbft v0.38.11-inj-5 h1:6nEaRsiBtihMb3QUhcgATQzCG7V+DKxFGH707hA1Ju8= +github.com/streamingfast/cometbft v0.38.11-inj-5/go.mod h1:WreBJKC7CtZ9cNsb5p6uNpfCBwNWDu+risVb58GTLfY= 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= From 7867d50ebc16d5202293c09593398afa3b9b8890 Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Tue, 15 Oct 2024 19:13:11 -0400 Subject: [PATCH 20/45] Update tracer BeginBlock and EndBlock will fetch the tracer from the sdk.Context, when calling ApplyTransaction, we will fetch the tracer from the keeper.evmTracer --- go.mod | 2 +- go.sum | 4 +- rpc/backend/tx_info.go | 3 +- x/evm/keeper/abci.go | 13 +-- x/evm/keeper/config.go | 22 +++-- x/evm/keeper/grpc_query.go | 5 +- x/evm/keeper/keeper.go | 6 +- x/evm/keeper/state_transition.go | 114 +++++++++-------------- x/evm/keeper/state_transition_test.go | 128 ++++++++++++++++++++++---- x/evm/statedb/statedb.go | 10 +- x/evm/statedb/statedb_test.go | 2 - x/evm/tracers/firehose.go | 38 +++++--- x/evm/tracers/tracing.go | 54 ----------- x/evm/tracing/context.go | 36 ++++++++ x/evm/tracing/hooks.go | 12 ++- 15 files changed, 267 insertions(+), 182 deletions(-) create mode 100644 x/evm/tracing/context.go diff --git a/go.mod b/go.mod index 71ab89c6bd..4972198e46 100644 --- a/go.mod +++ b/go.mod @@ -275,7 +275,7 @@ replace ( cosmossdk.io/store => github.com/InjectiveLabs/cosmos-sdk/store v0.0.0-20240904140803-b4127ecb5410 cosmossdk.io/x/tx => github.com/InjectiveLabs/cosmos-sdk/x/tx v0.0.0-20240904140803-b4127ecb5410 - github.com/cometbft/cometbft => github.com/streamingfast/cometbft v0.38.11-inj-5 + github.com/cometbft/cometbft => github.com/Injectivelabs/cometbft v0.38.11-inj-5 github.com/cosmos/cosmos-sdk => github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241010141128-de2b5199b23e github.com/ethereum/go-ethereum => github.com/InjectiveLabs/go-ethereum v1.9.22-0.20240923100242-5e28e23d353e nhooyr.io/websocket => github.com/coder/websocket v1.8.10 // replaced as instructed here:https://coder.com/blog/websocket diff --git a/go.sum b/go.sum index e6f406d916..9e2e7ceb91 100644 --- a/go.sum +++ b/go.sum @@ -250,6 +250,8 @@ github.com/InjectiveLabs/go-ethereum v1.9.22-0.20240923100242-5e28e23d353e h1:yX github.com/InjectiveLabs/go-ethereum v1.9.22-0.20240923100242-5e28e23d353e/go.mod h1:PHp+ZPjvQGBT2/iU18sro79JX+Ffy8IIqU7lnuVX3oE= github.com/InjectiveLabs/metrics v0.0.10 h1:BoOwXnCtRRIPmq06jcI20pXZYE758eusaCI5jDOoN4U= github.com/InjectiveLabs/metrics v0.0.10/go.mod h1:eYu++0DVUjk/jjV9WgvCo8gQU+16Yoyhp1iu+ghKNME= +github.com/Injectivelabs/cometbft v0.38.11-inj-5 h1:VrfeIvIEhdHwF7KfMZ1KmQRiT0TseSVRmx0NDoQc0Dw= +github.com/Injectivelabs/cometbft v0.38.11-inj-5/go.mod h1:WreBJKC7CtZ9cNsb5p6uNpfCBwNWDu+risVb58GTLfY= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= @@ -1119,8 +1121,6 @@ github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9 github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= -github.com/streamingfast/cometbft v0.38.11-inj-5 h1:6nEaRsiBtihMb3QUhcgATQzCG7V+DKxFGH707hA1Ju8= -github.com/streamingfast/cometbft v0.38.11-inj-5/go.mod h1:WreBJKC7CtZ9cNsb5p6uNpfCBwNWDu+risVb58GTLfY= 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= diff --git a/rpc/backend/tx_info.go b/rpc/backend/tx_info.go index 2e626e2565..814900686c 100644 --- a/rpc/backend/tx_info.go +++ b/rpc/backend/tx_info.go @@ -71,7 +71,8 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*rpctypes.RPCTransac } } } - // if we still unable to find the eth tx index, return error, shouldn't happen. + + // if we are still unable to find the eth tx index, return error, shouldn't happen. if res.EthTxIndex == -1 { return nil, errors.New("can't find index of ethereum tx") } diff --git a/x/evm/keeper/abci.go b/x/evm/keeper/abci.go index da6774aab1..4b046f7ccb 100644 --- a/x/evm/keeper/abci.go +++ b/x/evm/keeper/abci.go @@ -17,6 +17,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" ) // BeginBlock sets the sdk Context and EIP155 chain id to the Keeper. @@ -29,8 +30,9 @@ func (k *Keeper) BeginBlock(ctx sdk.Context) error { return err } - if k.evmTracer != nil && k.evmTracer.OnCosmosBlockStart != nil { - k.evmTracer.OnCosmosBlockStart( + // In the case of BeginBlock hook, we can extract the tracer from the context + if tracer := cosmostracing.GetCtxBlockchainTracer(ctx); tracer != nil && tracer.OnCosmosBlockStart != nil { + tracer.OnCosmosBlockStart( ToCosmosStartBlockEvent( k, ctx, @@ -50,10 +52,9 @@ func (k *Keeper) EndBlock(ctx sdk.Context) error { k.CollectTxBloom(ctx) k.RemoveParamsCache(ctx) - if k.evmTracer != nil && k.evmTracer.OnCosmosBlockEnd != nil { - defer func() { - k.evmTracer.OnCosmosBlockEnd(ToCosmosEndBlockEvent(k, ctx), nil) - }() + // In the case of EndBlock hook, we can extract the tracer from the context + if tracer := cosmostracing.GetCtxBlockchainTracer(ctx); tracer != nil && tracer.OnCosmosBlockEnd != nil { + tracer.OnCosmosBlockEnd(ToCosmosEndBlockEvent(k, ctx), nil) } return nil diff --git a/x/evm/keeper/config.go b/x/evm/keeper/config.go index 61d9abff1d..4226ba73fe 100644 --- a/x/evm/keeper/config.go +++ b/x/evm/keeper/config.go @@ -16,7 +16,7 @@ package keeper import ( - "github.com/ethereum/go-ethereum/eth/tracers" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "math/big" errorsmod "cosmossdk.io/errors" @@ -53,7 +53,7 @@ type EVMBlockConfig struct { type EVMConfig struct { *EVMBlockConfig TxConfig statedb.TxConfig - Tracer *tracers.Tracer + Tracer *cosmostracing.Hooks DebugTrace bool Overrides *rpctypes.StateOverride BlockOverrides *rpctypes.BlockOverrides @@ -131,13 +131,19 @@ func (k *Keeper) EVMConfig(ctx sdk.Context, chainID *big.Int, txHash common.Hash TxConfig: txConfig, } + return cfg, nil +} + +// EVMConfigWithTracer creates the EVMConfig based on current state and returns the evmTracer +func (k *Keeper) EVMConfigWithTracer(ctx sdk.Context, chainID *big.Int, txHash common.Hash) (*EVMConfig, error) { + cfg, err := k.EVMConfig(ctx, chainID, txHash) + + if err != nil { + return nil, err + } + if k.evmTracer != nil { - t := &tracers.Tracer{ - Hooks: k.evmTracer.Hooks, - GetResult: nil, - Stop: nil, - } - cfg.Tracer = t + cfg.Tracer = k.evmTracer } return cfg, nil diff --git a/x/evm/keeper/grpc_query.go b/x/evm/keeper/grpc_query.go index b4eacd911f..5eee851acd 100644 --- a/x/evm/keeper/grpc_query.go +++ b/x/evm/keeper/grpc_query.go @@ -20,6 +20,7 @@ import ( "encoding/json" "errors" "fmt" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "math/big" "time" @@ -719,7 +720,9 @@ func (k *Keeper) prepareTrace( cfg.BlockOverrides = &blockOverrides } - cfg.Tracer = tracer + cfg.Tracer = &cosmostracing.Hooks{ + Hooks: tracer.Hooks, + } cfg.DebugTrace = true res, err := k.ApplyMessageWithConfig(ctx, msg, cfg, commitMessage) if err != nil { diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 4c44d3d60e..bae0832897 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -139,9 +139,9 @@ func (k Keeper) ChainID() *big.Int { return k.eip155ChainID } -func (k *Keeper) InitChainer() { - if k.evmTracer != nil && k.evmTracer.OnBlockchainInit != nil { - k.evmTracer.OnBlockchainInit(evmtypes.DefaultChainConfig().EthereumConfig(k.ChainID())) +func (k *Keeper) InitChainer(ctx sdk.Context) { + if tracer := cosmostracing.GetCtxBlockchainTracer(ctx); tracer != nil && tracer.OnBlockchainInit != nil { + tracer.OnBlockchainInit(evmtypes.DefaultChainConfig().EthereumConfig(k.ChainID())) } } diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 8640687f61..dae7dd2bc4 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -18,6 +18,7 @@ package keeper import ( "bytes" "fmt" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "math/big" "sort" @@ -33,7 +34,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/tracing" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -41,6 +41,8 @@ import ( "github.com/ethereum/go-ethereum/params" ) +var configOverridesErrDesc = "failed to apply state override" + // NewEVM generates a go-ethereum VM from the provided Message fields and the chain parameters // (ChainConfig and module Params). It additionally sets the validator operator address as the // coinbase address to make it available for the COINBASE opcode, even though there is no @@ -73,13 +75,8 @@ func (k *Keeper) NewEVM( txCtx := core.NewEVMTxContext(msg) // Set Config Tracer if it was not already initialized - if k.evmTracer != nil { - t := &tracers.Tracer{ - Hooks: k.evmTracer.Hooks, - GetResult: nil, - Stop: nil, - } - cfg.Tracer = t + if tracer := cosmostracing.GetCtxBlockchainTracer(ctx); tracer != nil { + cfg.Tracer = tracer } vmConfig := k.VMConfig(ctx, cfg) @@ -159,7 +156,7 @@ func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc { } } -// ApplyTransaction runs and attempts to perform a state transition with the given transaction (i.e Message), that will +// ApplyTransaction runs and attempts to perform a state transition with the given transaction (i.e. Message), that will // only be persisted (committed) to the underlying KVStore if the transaction does not fail. // // # Gas tracking @@ -178,7 +175,7 @@ func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc { // For relevant discussion see: https://github.com/cosmos/cosmos-sdk/discussions/9072 func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) (*types.MsgEthereumTxResponse, error) { ethTx := msgEth.AsTransaction() - cfg, err := k.EVMConfig(ctx, k.eip155ChainID, ethTx.Hash()) + cfg, err := k.EVMConfigWithTracer(ctx, k.eip155ChainID, ethTx.Hash()) if err != nil { return nil, errorsmod.Wrap(err, "failed to load evm config") } @@ -196,9 +193,9 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) } // pass true to commit the StateDB - res, err := k.ApplyMessageWithConfig(tmpCtx, msg, cfg, true) - if err != nil { - return nil, errorsmod.Wrap(err, "failed to apply ethereum core message") + res, applyMessageErr := k.ApplyMessageWithConfig(tmpCtx, msg, cfg, true) + if applyMessageErr != nil { + return nil, errorsmod.Wrap(applyMessageErr, "failed to apply ethereum core message") } logs := types.LogsToEthereum(res.Logs) @@ -254,6 +251,19 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) // reset the gas meter for current cosmos transaction k.ResetGasMeterAndConsumeGas(ctx, totalGasUsed) + + defer func() { + // These errors are not vm related, so they should not be passed to the vm tracer + if !errorsmod.IsOf(applyMessageErr, types.ErrCreateDisabled, types.ErrCallDisabled) && (applyMessageErr != nil && applyMessageErr.Error() != configOverridesErrDesc) { + if cfg.Tracer != nil && cfg.Tracer.OnTxEnd != nil { + cfg.Tracer.OnTxEnd( + receipt, + applyMessageErr, + ) + } + } + }() + return res, nil } @@ -337,69 +347,33 @@ func (k *Keeper) ApplyMessageWithConfig( var evm *vm.EVM if cfg.Overrides != nil { if err := cfg.Overrides.Apply(stateDB); err != nil { - return nil, errorsmod.Wrap(err, "failed to apply state override") + return nil, errorsmod.Wrap(err, configOverridesErrDesc) } } + evm = k.NewEVM(ctx, msg, cfg, stateDB) leftoverGas := msg.GasLimit sender := vm.AccountRef(msg.From) - // Allow the tracer captures the tx level events, mainly the gas consumption. - vmCfg := evm.Config + tx := ethtypes.NewTx(ðtypes.LegacyTx{ + Nonce: msg.Nonce, + Gas: msg.GasLimit, + GasPrice: msg.GasPrice, + To: msg.To, + Value: msg.Value, + Data: msg.Data, + }) - if vmCfg.Tracer != nil { - stateDB.SetTracer(k.evmTracer) - vmCfg.Tracer.OnTxStart( + if cfg.Tracer != nil && cfg.Tracer.OnCosmosTxStart != nil { + stateDB.SetTracer(cfg.Tracer) + cfg.Tracer.OnCosmosTxStart( evm.GetVMContext(), - ethtypes.NewTx(ðtypes.LegacyTx{ - Nonce: msg.Nonce, - Gas: msg.GasLimit, - GasPrice: msg.GasPrice, - To: msg.To, - Value: msg.Value, - Data: msg.Data, - }), + tx, + cfg.TxConfig.TxHash, msg.From, ) - - //if cfg.DebugTrace { - // // msg.GasPrice should have been set to effective gas price - // senderAddr := sender.Address() - // stateDB.SubBalance( - // sender.Address(), - // uint256.MustFromBig(new(big.Int).Mul(msg.GasPrice, new(big.Int).SetUint64(msg.GasLimit))), - // tracing.BalanceChangeUnspecified, - // ) - // stateDB.SetNonce(senderAddr, stateDB.GetNonce(senderAddr)+1) - //} - - defer func() { - //if cfg.DebugTrace { - // stateDB.AddBalance( - // sender.Address(), - // uint256.MustFromBig(new(big.Int).Mul(msg.GasPrice, new(big.Int).SetUint64(leftoverGas))), - // tracing.BalanceChangeUnspecified, - // ) - //} - - traceErr := err - if vmErr != nil { - traceErr = vmErr - } - - vmCfg.Tracer.OnTxEnd( - ðtypes.Receipt{ - GasUsed: gasUsed, - }, - traceErr, - ) - }() } - //todo: on each of the exit conditions before the calls to if contractCreation { - // and manually catch them in the defer.vmcfg.tracer.ontxend - // also, I think that we have to manually craft a Call/Create? - rules := cfg.Rules contractCreation := msg.To == nil intrinsicGas, err := k.GetEthIntrinsicGas(msg, rules, contractCreation) @@ -428,6 +402,7 @@ func (k *Keeper) ApplyMessageWithConfig( stateDB.Prepare(rules, msg.From, cfg.CoinBase, msg.To, vm.DefaultActivePrecompiles(rules), msg.AccessList) if contractCreation { + // FIXME: also why do we want to set the nonce in the statedb twice here? // take over the nonce management from evm: // - reset sender's nonce to msg.Nonce() before calling evm. // - increase sender's nonce by one no matter the result. @@ -436,6 +411,12 @@ func (k *Keeper) ApplyMessageWithConfig( stateDB.SetNonce(sender.Address(), msg.Nonce+1) } else { ret, leftoverGas, vmErr = evm.Call(sender, *msg.To, msg.Data, leftoverGas, uint256.MustFromBig(msg.Value)) + + // if they do not want to make the nonce set to the statedb or do we want to manually call the onNonceChange hook + //stateDB.SetNonce(sender.Address(), msg.Nonce+1) + if cfg.Tracer != nil && cfg.Tracer.OnNonceChange != nil { + cfg.Tracer.OnNonceChange(sender.Address(), msg.Nonce, msg.Nonce+1) + } } refundQuotient := params.RefundQuotient @@ -486,11 +467,6 @@ func (k *Keeper) ApplyMessageWithConfig( // reset leftoverGas, to be used by the tracer leftoverGas = msg.GasLimit - gasUsed - if k.evmTracer != nil && k.evmTracer.OnGasChange != nil { - //TODO: add test to make sure the onGasChange is not doubly called - k.evmTracer.OnGasChange(msg.GasLimit, leftoverGas, tracing.GasChangeTxLeftOverReturned) - } - return &types.MsgEthereumTxResponse{ GasUsed: gasUsed, VmError: vmError, diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 8a05eccb4a..d0d9f154e1 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -1,8 +1,10 @@ package keeper_test import ( + "cosmossdk.io/core/comet" "fmt" "github.com/ethereum/go-ethereum/core/tracing" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "math" "math/big" "testing" @@ -639,6 +641,91 @@ func (suite *StateTransitionTestSuite) TestApplyMessage() { suite.Require().False(res.Failed()) } +func (suite *StateTransitionTestSuite) TestApplyMessageTracer() { + expectedGasUsed := params.TxGas + var msg *core.Message + + suite.SetupTest() + suite.Ctx = suite.Ctx.WithCometInfo(NewMockCometInfo()) + suite.Ctx = suite.Ctx.WithConsensusParams(*testutil.DefaultConsensusParams) + + _, err := suite.App.EvmKeeper.EVMConfig(suite.Ctx, big.NewInt(9000), common.Hash{}) + suite.Require().NoError(err) + + keeperParams := suite.App.EvmKeeper.GetParams(suite.Ctx) + chainCfg := keeperParams.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()) + rules := chainCfg.Rules(big.NewInt(suite.Ctx.BlockHeight()), chainCfg.MergeNetsplitBlock != nil, uint64(suite.Ctx.BlockHeader().Time.Unix())) + signer := ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) + tracer := types.NewTracer("", msg, rules) + vmdb := suite.StateDB() + + t, err := types.NewFirehoseCosmosLiveTracer() + require.NoError(suite.T(), err) + suite.Ctx = cosmostracing.SetCtxBlockchainTracer(suite.Ctx, t) + + onCosmosTxStartHookCalled := false + onGasChangedHookCalled := false + onEnterHookCalled := false + onExitHookCalled := false + + startTxHook := t.OnCosmosTxStart + gasChangedHook := t.OnGasChange + enterHook := t.OnEnter + exitHook := t.OnExit + + t.OnCosmosTxStart = func(vm *tracing.VMContext, tx *ethtypes.Transaction, hash common.Hash, from common.Address) { + // call original hook + startTxHook(vm, tx, hash, from) + onCosmosTxStartHookCalled = true + } + t.OnGasChange = func(old, new uint64, reason tracing.GasChangeReason) { + // call original hook + gasChangedHook(old, new, reason) + onGasChangedHookCalled = true + } + t.OnEnter = func(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + // call original hook + enterHook(depth, typ, from, to, input, gas, value) + onEnterHookCalled = true + } + t.OnExit = func(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + // call original hook + exitHook(depth, output, gasUsed, err, reverted) + onExitHookCalled = true + } + + // manually call on blockchain init + t.OnBlockchainInit(chainCfg) + suite.StateDB().SetTracer(t) + + msg, err = newNativeMessage( + vmdb.GetNonce(suite.Address), + suite.Ctx.BlockHeight(), + suite.Address, + chainCfg, + suite.Signer, + signer, + ethtypes.LegacyTxType, + nil, + nil, + ) + suite.Require().NoError(err) + + // manually call begin block + err = suite.App.EvmKeeper.BeginBlock(suite.Ctx) + suite.Require().NoError(err) + res, err := suite.App.EvmKeeper.ApplyMessage(suite.Ctx, msg, tracer, true) + + suite.Require().NoError(err) + suite.Require().Equal(expectedGasUsed, res.GasUsed) + suite.Require().False(res.Failed()) + + suite.Require().True(onCosmosTxStartHookCalled) + suite.Require().True(onGasChangedHookCalled) + suite.Require().True(onEnterHookCalled) + suite.Require().True(onExitHookCalled) +} + func (suite *StateTransitionTestSuite) TestApplyMessageWithConfig() { var ( msg *core.Message @@ -656,24 +743,6 @@ func (suite *StateTransitionTestSuite) TestApplyMessageWithConfig() { malleate func() expErr bool }{ - { - "message applied with firehose tracer", - func() { - msg, err = newNativeMessage( - vmdb.GetNonce(suite.Address), - suite.Ctx.BlockHeight(), - suite.Address, - chainCfg, - suite.Signer, - signer, - ethtypes.AccessListTxType, - nil, - nil, - ) - suite.Require().NoError(err) - }, - false, - }, { "message applied ok", func() { @@ -807,3 +876,26 @@ func (suite *StateTransitionTestSuite) TestGetProposerAddress() { }) } } + +type MockCometInfo struct { +} + +func NewMockCometInfo() *MockCometInfo { + return &MockCometInfo{} +} + +func (c *MockCometInfo) GetEvidence() comet.EvidenceList { + return nil +} + +func (c *MockCometInfo) GetValidatorsHash() []byte { + return []byte{} +} + +func (c *MockCometInfo) GetProposerAddress() []byte { + return []byte{} +} + +func (c *MockCometInfo) GetLastCommit() comet.CommitInfo { + return nil +} diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index bf62f02b63..17ca7495d9 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -123,7 +123,7 @@ func NewWithParams(ctx sdk.Context, keeper Keeper, txConfig TxConfig, evmDenom s cacheMS = parentCacheMS.Clone() commitMS = func() { parentCacheMS.Restore(cacheMS) } } else { - // in unit test, it could be run with a uncached multistore + // in unit test, it could be run with an uncached multistore if cacheMS, ok = ctx.MultiStore().CacheWrap().(cachemulti.Store); !ok { panic("expect the CacheWrap result to be cachemulti.Store") } @@ -148,7 +148,9 @@ func NewWithParams(ctx sdk.Context, keeper Keeper, txConfig TxConfig, evmDenom s } func (s *StateDB) SetTracer(tracer *cosmostracing.Hooks) { - s.evmTracer = tracer + if s.evmTracer == nil { + s.evmTracer = tracer + } } func (s *StateDB) NativeEvents() sdk.Events { @@ -167,6 +169,10 @@ func (s *StateDB) AddLog(log *ethtypes.Log) { log.TxIndex = s.txConfig.TxIndex log.Index = s.txConfig.LogIndex + uint(len(s.logs)) s.logs = append(s.logs, log) + + if s.evmTracer != nil && s.evmTracer.OnLog != nil { + s.evmTracer.OnLog(log) + } } // Logs returns the logs of current transaction. diff --git a/x/evm/statedb/statedb_test.go b/x/evm/statedb/statedb_test.go index f90b41cc5a..905d468684 100644 --- a/x/evm/statedb/statedb_test.go +++ b/x/evm/statedb/statedb_test.go @@ -341,7 +341,6 @@ func (suite *StateDBTestSuite) TestTracer_Nonce() { t.OnNonceChange = func(addr common.Address, prev, new uint64) { nonceChanges = append(nonceChanges, new) } - keeper.SetTracer(t) db := statedb.New(ctx, keeper, emptyTxConfig) db.SetTracer(t) tc.malleate(db) @@ -1079,7 +1078,6 @@ func (suite *StateDBTestSuite) TestTracer_SetStorage() { t.OnStorageChange = func(addr common.Address, slot common.Hash, prev, new common.Hash) { sChanges = append(sChanges, newStorageChange(addr.String(), slot.String(), prev.String(), new.String())) } - keeper.SetTracer(t) stateDB := statedb.New(ctx, keeper, emptyTxConfig) stateDB.SetTracer(t) diff --git a/x/evm/tracers/firehose.go b/x/evm/tracers/firehose.go index 148de47a00..7ae90d20a0 100644 --- a/x/evm/tracers/firehose.go +++ b/x/evm/tracers/firehose.go @@ -136,6 +136,7 @@ func NewCosmosTracingHooksFromFirehose(hooks *tracing.Hooks, firehose *Firehose) OnCosmosBlockStart: firehose.OnCosmosBlockStart, OnCosmosBlockEnd: firehose.OnCosmosBlockEnd, + OnCosmosTxStart: firehose.OnCosmosTxStart, } } @@ -265,6 +266,7 @@ func newFirehose(config *FirehoseConfig) (*Firehose, error) { // Transaction state transactionLogIndex: 0, + transactionIsolated: false, // Call state callStack: NewCallStack(), @@ -303,7 +305,7 @@ func (f *Firehose) newIsolatedTransactionTracer(tracerID string) *Firehose { // Transaction state transactionLogIndex: 0, - transactionIsolated: true, + transactionIsolated: false, // Call state callStack: NewCallStack(), @@ -654,7 +656,23 @@ func (f *Firehose) OnTxStart(evm *tracing.VMContext, tx *types.Transaction, from f.onTxStart(tx, tx.Hash(), from, to) } -// onTxStart is used internally a two places, in the normal "tracer" and in the "OnGenesisBlock", +func (f *Firehose) OnCosmosTxStart(evm *tracing.VMContext, tx *types.Transaction, hash common.Hash, 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())) + + f.ensureInBlockAndNotInTrxAndNotInCall() + + f.evm = evm + var to common.Address + if tx.To() == nil { + to = crypto.CreateAddress(from, evm.StateDB.GetNonce(from)) + } else { + to = *tx.To() + } + + f.onTxStart(tx, hash, from, to) +} + +// onTxStart is used internally two places, in the normal "tracer" and in the "OnGenesisBlock", // we manually pass some override to the `tx` because genesis block has a different way of creating // the transaction that wraps the genesis block. func (f *Firehose) onTxStart(tx *types.Transaction, hash common.Hash, from, to common.Address) { @@ -692,18 +710,12 @@ func (f *Firehose) OnTxEnd(receipt *types.Receipt, err error) { firehoseInfo("trx ending (tracer=%s, isolated=%t, error=%s)", f.tracerID, f.transactionIsolated, errorView(err)) f.ensureInBlockAndInTrx() + // TODO: we do nothing with the error here ??? + // Shouldn't there be something that checks the type of error and sets the status of the transaction accordingly? trxTrace := f.completeTransaction(receipt) - // In this case, we are in some kind of parallel processing and we must simply add the transaction - // to a transient storage (and not in the block directly). Adding it to the block will be done by the - // `OnTxCommit` callback. if f.transactionIsolated { - f.transactionTransient = trxTrace - - // We must not reset transaction here. In the isolated transaction tracer, the transaction is reset - // by the `OnTxReset` callback which comes from outside the tracer. Second, resetting the transaction - // also resets the [f.transactionTransient] field which is the one we want to keep on completion - // of an isolated transaction. + panic("isolated transactions are not supported") } else { f.block.TransactionTraces = append(f.block.TransactionTraces, trxTrace) @@ -739,8 +751,6 @@ func (f *Firehose) completeTransaction(receipt *types.Receipt) *pbeth.Transactio f.transaction.Status = transactionStatusFromChainTxReceipt(receipt.Status) } - // todo: in some blockchains, when there is not received, it means that the transaction is failed, what about here? should it be the same? - // It's possible that the transaction was reverted, but we still have a receipt, in that case, we must // check the root call if rootCall.StatusReverted { @@ -804,7 +814,7 @@ func (f *Firehose) assignOrdinalAndIndexToReceiptLogs() { trx := f.transaction - callLogs := []*pbeth.Log{} + var callLogs []*pbeth.Log for _, call := range trx.Calls { firehoseTrace("checking call (reverted=%t logs=%d)", call.StateReverted, len(call.Logs)) if call.StateReverted { diff --git a/x/evm/tracers/tracing.go b/x/evm/tracers/tracing.go index 0e1411bf0d..c8d19ef146 100644 --- a/x/evm/tracers/tracing.go +++ b/x/evm/tracers/tracing.go @@ -1,9 +1,6 @@ package tracers import ( - "context" - sdk "github.com/cosmos/cosmos-sdk/types" - cosmostracing "github.com/ethereum/go-ethereum/core/tracing" "github.com/evmos/ethermint/x/evm/tracing" ) @@ -13,54 +10,3 @@ import ( // The scheme of the URL is going to be used to determine which tracer to use // by the registry. type BlockchainTracerFactory = func(backwardCompatibility bool) (*tracing.Hooks, error) - -type CtxBlockchainTracerKeyType string - -const CtxBlockchainTracerKey = CtxBlockchainTracerKeyType("evm_and_state_logger") - -func SetCtxBlockchainTracer(ctx context.Context, hooks *cosmostracing.Hooks) context.Context { - return context.WithValue(ctx, CtxBlockchainTracerKey, hooks) -} - -func GetCtxBlockchainTracer(ctx sdk.Context) *cosmostracing.Hooks { - rawVal := ctx.Context().Value(CtxBlockchainTracerKey) - if rawVal == nil { - return nil - } - logger, ok := rawVal.(*cosmostracing.Hooks) - if !ok { - return nil - } - return logger -} - -func GetCtxEthTracingHooks(ctx sdk.Context) *cosmostracing.Hooks { - if logger := GetCtxBlockchainTracer(ctx); logger != nil { - return logger - } - - return nil -} - -type TxTracerHooks struct { - Hooks *cosmostracing.Hooks - - OnTxReset func() - OnTxCommit func() -} - -func (h TxTracerHooks) InjectInContext(ctx sdk.Context) context.Context { - return SetCtxBlockchainTracer(ctx, h.Hooks) -} - -func (h TxTracerHooks) Reset() { - if h.OnTxReset != nil { - h.OnTxReset() - } -} - -func (h TxTracerHooks) Commit() { - if h.OnTxCommit != nil { - h.OnTxCommit() - } -} diff --git a/x/evm/tracing/context.go b/x/evm/tracing/context.go new file mode 100644 index 0000000000..aaaf052ae4 --- /dev/null +++ b/x/evm/tracing/context.go @@ -0,0 +1,36 @@ +package tracing + +import ( + "context" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type CtxBlockchainTracerKeyType string + +const CtxBlockchainTracerKey = CtxBlockchainTracerKeyType("evm_and_state_logger") + +func SetCtxBlockchainTracer(ctx sdk.Context, logger *Hooks) sdk.Context { + return ctx.WithContext(context.WithValue(ctx.Context(), CtxBlockchainTracerKey, logger)) +} + +// GetCtxBlockchainTracer function to get the Cosmos specific [tracing.Hooks] struct +// used to trace EVM blocks and transactions. +func GetCtxBlockchainTracer(ctx sdk.Context) *Hooks { + rawVal := ctx.Context().Value(CtxBlockchainTracerKey) + if rawVal == nil { + return nil + } + logger, ok := rawVal.(*Hooks) + if !ok { + return nil + } + return logger +} + +func GetCtxEthTracingHooks(ctx sdk.Context) *Hooks { + if logger := GetCtxBlockchainTracer(ctx); logger != nil { + return logger + } + + return nil +} diff --git a/x/evm/tracing/hooks.go b/x/evm/tracing/hooks.go index 84ff1e4fd1..36d1412ef8 100644 --- a/x/evm/tracing/hooks.go +++ b/x/evm/tracing/hooks.go @@ -2,6 +2,7 @@ package tracing import ( "github.com/cometbft/cometbft/types" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/tracing" ethtypes "github.com/ethereum/go-ethereum/core/types" "math/big" @@ -10,13 +11,22 @@ import ( type ( OnCosmosBlockStart func(CosmosStartBlockEvent) OnCosmosBlockEnd func(CosmosEndBlockEvent, error) + OnCosmosTxStart func(evm *tracing.VMContext, tx *ethtypes.Transaction, txHash common.Hash, from common.Address) ) type Hooks struct { *tracing.Hooks + // OnCosmosBlockStart is called when a new block is started. OnCosmosBlockStart OnCosmosBlockStart - OnCosmosBlockEnd OnCosmosBlockEnd + + // OnCosmosBlockEnd is called when a block is finished. + OnCosmosBlockEnd OnCosmosBlockEnd + + // OnCosmosTxStart is called when a new transaction is started. + // The transaction hash calculated by the EVM is passed as an argument as it + // is not the same as the one calculated by tx.Hash() + OnCosmosTxStart OnCosmosTxStart } type CosmosStartBlockEvent struct { From b7a2952f9bbbfcf2fdfe231668046dec224c03a9 Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Tue, 15 Oct 2024 21:09:46 -0400 Subject: [PATCH 21/45] Fix on tx end hook call --- x/evm/keeper/state_transition.go | 36 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index dae7dd2bc4..370db8e06d 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -18,7 +18,6 @@ package keeper import ( "bytes" "fmt" - cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "math/big" "sort" @@ -74,11 +73,6 @@ func (k *Keeper) NewEVM( } txCtx := core.NewEVMTxContext(msg) - // Set Config Tracer if it was not already initialized - if tracer := cosmostracing.GetCtxBlockchainTracer(ctx); tracer != nil { - cfg.Tracer = tracer - } - vmConfig := k.VMConfig(ctx, cfg) contracts := make(map[common.Address]vm.PrecompiledContract) active := make([]common.Address, 0) @@ -239,6 +233,24 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) } } + defer func() { + // These errors are not vm related, so they should not be passed to the vm tracer + if errorsmod.IsOf(applyMessageErr, types.ErrCreateDisabled, types.ErrCallDisabled) { + return + } + + if applyMessageErr != nil && applyMessageErr.Error() != configOverridesErrDesc { + return + } + + if cfg.Tracer != nil && cfg.Tracer.OnTxEnd != nil { + cfg.Tracer.OnTxEnd( + receipt, + applyMessageErr, + ) + } + }() + // refund gas in order to match the Ethereum gas consumption instead of the default SDK one. if err = k.RefundGas(ctx, msg, msg.GasLimit-res.GasUsed, cfg.Params.EvmDenom); err != nil { return nil, errorsmod.Wrapf(err, "failed to refund leftover gas to sender %s", msg.From) @@ -252,18 +264,6 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) // reset the gas meter for current cosmos transaction k.ResetGasMeterAndConsumeGas(ctx, totalGasUsed) - defer func() { - // These errors are not vm related, so they should not be passed to the vm tracer - if !errorsmod.IsOf(applyMessageErr, types.ErrCreateDisabled, types.ErrCallDisabled) && (applyMessageErr != nil && applyMessageErr.Error() != configOverridesErrDesc) { - if cfg.Tracer != nil && cfg.Tracer.OnTxEnd != nil { - cfg.Tracer.OnTxEnd( - receipt, - applyMessageErr, - ) - } - } - }() - return res, nil } From 781d9ac24003222311cda1b1213437c45b8923c7 Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Wed, 16 Oct 2024 13:59:51 -0400 Subject: [PATCH 22/45] Fix outdated grpc_query tests --- go.mod | 2 +- go.sum | 4 ++-- x/evm/keeper/grpc_query_test.go | 33 +++++++++------------------ x/evm/keeper/state_transition.go | 24 +++++++++++++------ x/evm/keeper/state_transition_test.go | 18 +++++++-------- x/evm/statedb/statedb.go | 2 +- 6 files changed, 41 insertions(+), 42 deletions(-) diff --git a/go.mod b/go.mod index 4972198e46..c4aaadaeff 100644 --- a/go.mod +++ b/go.mod @@ -276,7 +276,7 @@ replace ( cosmossdk.io/x/tx => github.com/InjectiveLabs/cosmos-sdk/x/tx v0.0.0-20240904140803-b4127ecb5410 github.com/cometbft/cometbft => github.com/Injectivelabs/cometbft v0.38.11-inj-5 - github.com/cosmos/cosmos-sdk => github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241010141128-de2b5199b23e + github.com/cosmos/cosmos-sdk => github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241014111010-224eb95c9eb1 github.com/ethereum/go-ethereum => github.com/InjectiveLabs/go-ethereum v1.9.22-0.20240923100242-5e28e23d353e nhooyr.io/websocket => github.com/coder/websocket v1.8.10 // replaced as instructed here:https://coder.com/blog/websocket ) diff --git a/go.sum b/go.sum index 9e2e7ceb91..3d5d9f2ed5 100644 --- a/go.sum +++ b/go.sum @@ -238,8 +238,8 @@ github.com/DataDog/sketches-go v1.4.2 h1:gppNudE9d19cQ98RYABOetxIhpTCl4m7CnbRZjv github.com/DataDog/sketches-go v1.4.2/go.mod h1:xJIXldczJyyjnbDop7ZZcLxJdV3+7Kra7H1KMgpgkLk= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241010141128-de2b5199b23e h1:fFy5HkvhhCcNsnkwM2pJf7+TiIWs6nOgCct6FLMI9i4= -github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241010141128-de2b5199b23e/go.mod h1:DAbn+xjBNvDKXf/XThMI/UADLyJi55Jo+ILfJ0NE4tQ= +github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241014111010-224eb95c9eb1 h1:j+uBBPJZ2ibYui/pbRHNtfOxPtSNhMmNWVpKlGSLkIQ= +github.com/InjectiveLabs/cosmos-sdk v0.50.10-0.20241014111010-224eb95c9eb1/go.mod h1:tUXtOv0qMZDlPa6OQE+mPRF4RpMMoT39gR/q3+IbK7s= github.com/InjectiveLabs/cosmos-sdk/client/v2 v2.0.0-20240904140803-b4127ecb5410 h1:gUiyEHPKz+S1vOb3DhVSpUvSreUR7Z/8Hv3OXws/7LA= github.com/InjectiveLabs/cosmos-sdk/client/v2 v2.0.0-20240904140803-b4127ecb5410/go.mod h1:CZcL41HpJPOOayTCO28j8weNBQprG+SRiKX39votypo= github.com/InjectiveLabs/cosmos-sdk/store v0.0.0-20240904140803-b4127ecb5410 h1:6ai8wHEoV48EACjoGpqW8uxuwCcZfYMBU7FEoC0R0lI= diff --git a/x/evm/keeper/grpc_query_test.go b/x/evm/keeper/grpc_query_test.go index 0816ad06b6..d075fbd0c5 100644 --- a/x/evm/keeper/grpc_query_test.go +++ b/x/evm/keeper/grpc_query_test.go @@ -862,7 +862,7 @@ func (suite *GRPCServerTestSuiteSuite) TestTraceTx() { predecessors = []*types.MsgEthereumTx{} }, expPass: true, - traceResponse: "{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":", + traceResponse: "{\"gas\":0,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":", }, { msg: "default trace with filtered response", @@ -875,7 +875,7 @@ func (suite *GRPCServerTestSuiteSuite) TestTraceTx() { predecessors = []*types.MsgEthereumTx{} }, expPass: true, - traceResponse: "{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":", + traceResponse: "{\"gas\":0,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":", enableFeemarket: false, }, { @@ -900,7 +900,7 @@ func (suite *GRPCServerTestSuiteSuite) TestTraceTx() { predecessors = []*types.MsgEthereumTx{} }, expPass: true, - traceResponse: "{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":", + traceResponse: "{\"gas\":0,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":", enableFeemarket: true, }, { @@ -934,7 +934,7 @@ func (suite *GRPCServerTestSuiteSuite) TestTraceTx() { predecessors = append(predecessors, firstTx) }, expPass: true, - traceResponse: "{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":", + traceResponse: "{\"gas\":0,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":", enableFeemarket: false, }, { @@ -1005,7 +1005,7 @@ func (suite *GRPCServerTestSuiteSuite) TestTraceTx() { suite.App.EvmKeeper.SetParams(suite.Ctx, params) }, expPass: true, - traceResponse: "{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":", + traceResponse: "{\"gas\":0,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":", }, { msg: "invalid chain id", @@ -1043,16 +1043,10 @@ func (suite *GRPCServerTestSuiteSuite) TestTraceTx() { res, err := suite.EvmQueryClient.TraceTx(suite.Ctx, &traceReq) if tc.expPass { suite.Require().NoError(err) - // if data is too big, slice the result - if len(res.Data) > 150 { - suite.Require().Equal(tc.traceResponse, string(res.Data[:150])) - } else { - suite.Require().Equal(tc.traceResponse, string(res.Data)) - } + suite.Require().Contains(string(res.Data), tc.traceResponse) if traceConfig == nil || traceConfig.Tracer == "" { var result ethlogger.ExecutionResult suite.Require().NoError(json.Unmarshal(res.Data, &result)) - suite.Require().Positive(result.Gas) } } else { suite.Require().Error(err) @@ -1085,7 +1079,7 @@ func (suite *GRPCServerTestSuiteSuite) TestTraceBlock() { traceConfig = nil }, expPass: true, - traceResponse: "[{\"result\":{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PU", + traceResponse: "[{\"result\":{\"gas\":0,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PU", }, { msg: "filtered trace", @@ -1097,7 +1091,7 @@ func (suite *GRPCServerTestSuiteSuite) TestTraceBlock() { } }, expPass: true, - traceResponse: "[{\"result\":{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PU", + traceResponse: "[{\"result\":{\"gas\":0,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PU", }, { msg: "javascript tracer", @@ -1119,7 +1113,7 @@ func (suite *GRPCServerTestSuiteSuite) TestTraceBlock() { } }, expPass: true, - traceResponse: "[{\"result\":{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PU", + traceResponse: "[{\"result\":{\"gas\":0,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PU", enableFeemarket: true, }, { @@ -1152,7 +1146,7 @@ func (suite *GRPCServerTestSuiteSuite) TestTraceBlock() { txs = append([]*types.MsgEthereumTx{}, firstTx, secondTx) }, expPass: true, - traceResponse: "[{\"result\":{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PU", + traceResponse: "[{\"result\":{\"gas\":0,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PU", enableFeemarket: false, }, { @@ -1221,12 +1215,7 @@ func (suite *GRPCServerTestSuiteSuite) TestTraceBlock() { res, err := suite.EvmQueryClient.TraceBlock(suite.Ctx, &traceReq) if tc.expPass { suite.Require().NoError(err) - // if data is too big, slice the result - if len(res.Data) > 150 { - suite.Require().Equal(tc.traceResponse, string(res.Data[:150])) - } else { - suite.Require().Contains(string(res.Data), tc.traceResponse) - } + suite.Require().Contains(string(res.Data), tc.traceResponse) } else { suite.Require().Error(err) } diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 370db8e06d..c80107c30d 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -364,14 +364,24 @@ func (k *Keeper) ApplyMessageWithConfig( Data: msg.Data, }) - if cfg.Tracer != nil && cfg.Tracer.OnCosmosTxStart != nil { + if cfg.Tracer != nil { stateDB.SetTracer(cfg.Tracer) - cfg.Tracer.OnCosmosTxStart( - evm.GetVMContext(), - tx, - cfg.TxConfig.TxHash, - msg.From, - ) + + // If a cosmos tracer is set, the OnCosmosTxStart takes precedence over OnTxStart + if cfg.Tracer.OnCosmosTxStart != nil { + cfg.Tracer.OnCosmosTxStart( + evm.GetVMContext(), + tx, + cfg.TxConfig.TxHash, + msg.From, + ) + } else if cfg.Tracer.OnTxStart != nil { + cfg.Tracer.OnTxStart( + evm.GetVMContext(), + tx, + msg.From, + ) + } } rules := cfg.Rules diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index d0d9f154e1..5660fcbec7 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -641,7 +641,8 @@ func (suite *StateTransitionTestSuite) TestApplyMessage() { suite.Require().False(res.Failed()) } -func (suite *StateTransitionTestSuite) TestApplyMessageTracer() { +// TODO: add TestApplyTransactionWithTracer() +func (suite *StateTransitionTestSuite) TestApplyMessageWithConfigTracer() { expectedGasUsed := params.TxGas var msg *core.Message @@ -649,20 +650,19 @@ func (suite *StateTransitionTestSuite) TestApplyMessageTracer() { suite.Ctx = suite.Ctx.WithCometInfo(NewMockCometInfo()) suite.Ctx = suite.Ctx.WithConsensusParams(*testutil.DefaultConsensusParams) - _, err := suite.App.EvmKeeper.EVMConfig(suite.Ctx, big.NewInt(9000), common.Hash{}) + t, err := types.NewFirehoseCosmosLiveTracer() + require.NoError(suite.T(), err) + suite.Ctx = cosmostracing.SetCtxBlockchainTracer(suite.Ctx, t) + suite.App.EvmKeeper.SetTracer(t) + + cfgWithTracer, err := suite.App.EvmKeeper.EVMConfigWithTracer(suite.Ctx, big.NewInt(9000), common.Hash{}) suite.Require().NoError(err) keeperParams := suite.App.EvmKeeper.GetParams(suite.Ctx) chainCfg := keeperParams.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()) - rules := chainCfg.Rules(big.NewInt(suite.Ctx.BlockHeight()), chainCfg.MergeNetsplitBlock != nil, uint64(suite.Ctx.BlockHeader().Time.Unix())) signer := ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) - tracer := types.NewTracer("", msg, rules) vmdb := suite.StateDB() - t, err := types.NewFirehoseCosmosLiveTracer() - require.NoError(suite.T(), err) - suite.Ctx = cosmostracing.SetCtxBlockchainTracer(suite.Ctx, t) - onCosmosTxStartHookCalled := false onGasChangedHookCalled := false onEnterHookCalled := false @@ -714,7 +714,7 @@ func (suite *StateTransitionTestSuite) TestApplyMessageTracer() { // manually call begin block err = suite.App.EvmKeeper.BeginBlock(suite.Ctx) suite.Require().NoError(err) - res, err := suite.App.EvmKeeper.ApplyMessage(suite.Ctx, msg, tracer, true) + res, err := suite.App.EvmKeeper.ApplyMessageWithConfig(suite.Ctx, msg, cfgWithTracer, true) suite.Require().NoError(err) suite.Require().Equal(expectedGasUsed, res.GasUsed) diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index 17ca7495d9..2458ff3ce5 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -214,7 +214,7 @@ func (s *StateDB) Empty(addr common.Address) bool { // GetBalance retrieves the balance from the given address or 0 if object not found func (s *StateDB) GetBalance(addr common.Address) *uint256.Int { - bal := s.keeper.GetBalance(s.ctx, sdk.AccAddress(addr.Bytes()), s.evmDenom) + bal := s.keeper.GetBalance(s.ctx, addr.Bytes(), s.evmDenom) return uint256.MustFromBig(bal) } From 34b418279bd4a1188f7c8c323ae9fefc64b09b89 Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Thu, 17 Oct 2024 09:35:50 -0400 Subject: [PATCH 23/45] Adding firehose trace in the OnStorageChange call --- x/evm/tracers/firehose.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x/evm/tracers/firehose.go b/x/evm/tracers/firehose.go index 7ae90d20a0..cf2493fe70 100644 --- a/x/evm/tracers/firehose.go +++ b/x/evm/tracers/firehose.go @@ -657,7 +657,7 @@ func (f *Firehose) OnTxStart(evm *tracing.VMContext, tx *types.Transaction, from } func (f *Firehose) OnCosmosTxStart(evm *tracing.VMContext, tx *types.Transaction, hash common.Hash, 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 type=%d gas=%d isolated=%t input=%s)", f.tracerID, hash, tx.Type(), tx.Gas(), f.transactionIsolated, inputView(tx.Data())) f.ensureInBlockAndNotInTrxAndNotInCall() @@ -884,8 +884,8 @@ func (f *Firehose) OnCallEnter(depth int, typ byte, from common.Address, to comm if isRootCall := depth == 0; isRootCall { callType = rootCallType(opCode == vm.CREATE) } else { - // The invokation for vm.SELFDESTRUCT is called while already in another call and is recorded specially - // in the Geth tracer and generates `OnEnter/OnExit` callbacks. However in Firehose, self destruction + // The invocation for vm.SELFDESTRUCT is called while already in another call and is recorded specially + // in the Geth tracer and generates `OnEnter/OnExit` callbacks. However in Firehose, self-destruction // simply sets the call as having called suicided so there is no extra call. // // So we ignore `OnEnter/OnExit` callbacks for `SELFDESTRUCT` opcode, we ignore it here and set @@ -1350,6 +1350,8 @@ func (f *Firehose) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev func (f *Firehose) OnStorageChange(a common.Address, k, prev, new common.Hash) { f.ensureInBlockAndInTrxAndInCall() + firehoseTrace("on storage change (addr=%s, key=%s, prev=%s, new=%s)", a.String(), k.String(), prev.String(), new.String()) + activeCall := f.callStack.Peek() activeCall.StorageChanges = append(activeCall.StorageChanges, &pbeth.StorageChange{ Address: a.Bytes(), From 4e2ce711e797d4f97b7cd367a0e4df23b9122b89 Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Thu, 17 Oct 2024 09:55:40 -0400 Subject: [PATCH 24/45] Adding test for ApplyTransactionWithTracer to check that the hooks are properly called --- x/evm/keeper/state_transition_test.go | 65 ++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 5660fcbec7..6680b20047 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -641,7 +641,70 @@ func (suite *StateTransitionTestSuite) TestApplyMessage() { suite.Require().False(res.Failed()) } -// TODO: add TestApplyTransactionWithTracer() +func (suite *StateTransitionTestSuite) TestApplyTransactionWithTracer() { + expectedGasUsed := params.TxGas + var msg *types.MsgEthereumTx + + suite.SetupTest() + suite.Ctx = suite.Ctx.WithCometInfo(NewMockCometInfo()) + suite.Ctx = suite.Ctx.WithConsensusParams(*testutil.DefaultConsensusParams) + + t, err := types.NewFirehoseCosmosLiveTracer() + require.NoError(suite.T(), err) + suite.Ctx = cosmostracing.SetCtxBlockchainTracer(suite.Ctx, t) + suite.App.EvmKeeper.SetTracer(t) + + keeperParams := suite.App.EvmKeeper.GetParams(suite.Ctx) + chainCfg := keeperParams.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()) + signer := ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) + vmdb := suite.StateDB() + + onCosmosTxStartHookCalled := false + onTxEndHookCalled := false + + startTxHook := t.OnCosmosTxStart + endTxHook := t.OnTxEnd + + t.OnCosmosTxStart = func(vm *tracing.VMContext, tx *ethtypes.Transaction, hash common.Hash, from common.Address) { + // call original hook + startTxHook(vm, tx, hash, from) + onCosmosTxStartHookCalled = true + } + t.OnTxEnd = func(receipt *ethtypes.Receipt, err error) { + // call original hook + endTxHook(receipt, err) + onTxEndHookCalled = true + } + + // manually call on blockchain init + t.OnBlockchainInit(chainCfg) + suite.StateDB().SetTracer(t) + + msg, _, err = newEthMsgTx( + vmdb.GetNonce(suite.Address), + suite.Address, + suite.Signer, + signer, + ethtypes.LegacyTxType, + nil, + nil, + ) + suite.Require().NoError(err) + + // manually call begin block + err = suite.App.EvmKeeper.BeginBlock(suite.Ctx) + suite.Require().NoError(err) + + res, err := suite.App.EvmKeeper.ApplyTransaction(suite.Ctx, msg) + + suite.Require().NoError(err) + suite.Require().Equal(expectedGasUsed, res.GasUsed) + suite.Require().False(res.Failed()) + + suite.Require().True(onCosmosTxStartHookCalled) + suite.Require().True(onTxEndHookCalled) +} + func (suite *StateTransitionTestSuite) TestApplyMessageWithConfigTracer() { expectedGasUsed := params.TxGas var msg *core.Message From bc9cb03565510a05bcc234afc319417feb7375f1 Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Thu, 17 Oct 2024 12:13:57 -0400 Subject: [PATCH 25/45] - address pr comments - move tracer calls to storage changes higher up --- x/evm/handler_test.go | 1 - x/evm/keeper/keeper.go | 4 ++-- x/evm/statedb/statedb.go | 20 +++++++++++++------- x/evm/tracers/firehose.go | 2 +- x/evm/tracers/firehose_test.go | 9 ++++----- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/x/evm/handler_test.go b/x/evm/handler_test.go index af038088a1..e721f6c593 100644 --- a/x/evm/handler_test.go +++ b/x/evm/handler_test.go @@ -136,7 +136,6 @@ func (suite *HandlerTestSuite) TestHandleMsgEthereumTx() { for _, tc := range testCases { suite.Run(tc.msg, func() { - // todo: it seems we are missing a hook on a balance change? or something like that? suite.SetupTest() // reset //nolint tc.malleate() diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index bae0832897..018038ca4a 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -33,7 +33,6 @@ import ( ethermint "github.com/evmos/ethermint/types" "github.com/evmos/ethermint/x/evm/statedb" "github.com/evmos/ethermint/x/evm/types" - evmtypes "github.com/evmos/ethermint/x/evm/types" ) // CustomContractFn defines a custom precompiled contract generator with ctx, rules and returns a precompiled contract. @@ -141,7 +140,7 @@ func (k Keeper) ChainID() *big.Int { func (k *Keeper) InitChainer(ctx sdk.Context) { if tracer := cosmostracing.GetCtxBlockchainTracer(ctx); tracer != nil && tracer.OnBlockchainInit != nil { - tracer.OnBlockchainInit(evmtypes.DefaultChainConfig().EthereumConfig(k.ChainID())) + tracer.OnBlockchainInit(types.DefaultChainConfig().EthereumConfig(k.ChainID())) } } @@ -204,6 +203,7 @@ func (k *Keeper) PostTxProcessing(ctx sdk.Context, msg *core.Message, receipt *e return k.hooks.PostTxProcessing(ctx, msg, receipt) } +// SetTracer should only be called during initialization func (k *Keeper) SetTracer(tracer *cosmostracing.Hooks) { k.evmTracer = tracer } diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index 2458ff3ce5..b3cc167185 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -552,34 +552,40 @@ func (s *StateDB) SetCode(addr common.Address, code []byte) { oldCode := s.GetCode(addr) stateObject.SetCode(crypto.Keccak256Hash(code), code) + var oldCodeHash common.Hash + if oldCode != nil { + oldCodeHash = crypto.Keccak256Hash(oldCode) + } + if s.evmTracer != nil && s.evmTracer.OnCodeChange != nil { - s.evmTracer.OnCodeChange(addr, crypto.Keccak256Hash(oldCode), oldCode, crypto.Keccak256Hash(code), code) + s.evmTracer.OnCodeChange(addr, oldCodeHash, oldCode, crypto.Keccak256Hash(code), code) } } } // SetState sets the contract state. func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { - stateObject := s.getOrNewStateObject(addr) - stateObject.SetState(key, value) - if s.evmTracer != nil && s.evmTracer.OnStorageChange != nil { s.evmTracer.OnStorageChange(addr, key, s.GetState(addr, key), value) } + + stateObject := s.getOrNewStateObject(addr) + stateObject.SetState(key, value) } // SetStorage replaces the entire storage for the specified account with given // storage. This function should only be used for debugging and the mutations // must be discarded afterward. func (s *StateDB) SetStorage(addr common.Address, storage Storage) { - stateObject := s.getOrNewStateObject(addr) - stateObject.SetStorage(storage) - if s.evmTracer != nil && s.evmTracer.OnStorageChange != nil { for key, value := range storage { s.evmTracer.OnStorageChange(addr, key, s.GetState(addr, key), value) } } + + stateObject := s.getOrNewStateObject(addr) + stateObject.SetStorage(storage) + } // Suicide marks the given account as suicided. diff --git a/x/evm/tracers/firehose.go b/x/evm/tracers/firehose.go index cf2493fe70..38054385a2 100644 --- a/x/evm/tracers/firehose.go +++ b/x/evm/tracers/firehose.go @@ -2430,7 +2430,7 @@ func (m Memory) GetPtr(offset, size int64) []byte { // work because the memory is going to be expanded before the operation is actually // executed so the memory will be of the correct size. // - // In this situtation, we must pad with zeroes when the memory is not big enough. + // In this situation, we must pad with zeroes when the memory is not big enough. reminder := m[offset:] return append(reminder, make([]byte, int(size)-len(reminder))...) } diff --git a/x/evm/tracers/firehose_test.go b/x/evm/tracers/firehose_test.go index 6556518352..d4f64f8883 100644 --- a/x/evm/tracers/firehose_test.go +++ b/x/evm/tracers/firehose_test.go @@ -393,12 +393,11 @@ func TestFirehose_reorderIsolatedTransactionsAndOrdinals(t *testing.T) { ordinals := maps.Keys(seenOrdinals) slices.Sort(ordinals) - // All ordinals should be in stricly increasing order - prev := -1 + // All ordinals should be in strictly increasing order + prev := 0 for _, ordinal := range ordinals { - if prev != -1 { - assert.Equal(t, prev+1, int(ordinal), "Ordinal %d is not in sequence", ordinal) - } + assert.Equal(t, prev+1, int(ordinal), "Ordinal %d is not in sequence", ordinal) + prev = int(ordinal) } }) } From 610f7b69b58167e17270e6a41591659f229f375e Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Thu, 17 Oct 2024 15:49:15 -0400 Subject: [PATCH 26/45] Fix issue in tracer not catching correct error in `state_transition.go` update with suggestions from coderabbitai --- x/evm/keeper/abci.go | 2 +- x/evm/keeper/keeper_firehose.go | 5 +++-- x/evm/keeper/state_transition.go | 33 +++++++++++++++++++------------- x/evm/tracers/firehose.go | 11 +++++++---- x/evm/tracers/firehose_test.go | 12 +++++++----- x/evm/tracing/hooks.go | 2 +- 6 files changed, 39 insertions(+), 26 deletions(-) diff --git a/x/evm/keeper/abci.go b/x/evm/keeper/abci.go index 4b046f7ccb..d926772e1e 100644 --- a/x/evm/keeper/abci.go +++ b/x/evm/keeper/abci.go @@ -36,7 +36,7 @@ func (k *Keeper) BeginBlock(ctx sdk.Context) error { ToCosmosStartBlockEvent( k, ctx, - evmBlockConfig.CoinBase.Bytes(), + evmBlockConfig.CoinBase, ctx.BlockHeader(), ), ) diff --git a/x/evm/keeper/keeper_firehose.go b/x/evm/keeper/keeper_firehose.go index 21a99174af..80a8c959e7 100644 --- a/x/evm/keeper/keeper_firehose.go +++ b/x/evm/keeper/keeper_firehose.go @@ -5,6 +5,7 @@ import ( cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" cosmostypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/evmos/ethermint/x/evm/tracing" "github.com/evmos/ethermint/x/evm/types" @@ -23,7 +24,7 @@ func BlocksBloom(k *Keeper, ctx sdk.Context) *big.Int { return bloom } -func ToCosmosStartBlockEvent(k *Keeper, ctx sdk.Context, coinbaseBytes []byte, blockHeader cmtproto.Header) tracing.CosmosStartBlockEvent { +func ToCosmosStartBlockEvent(k *Keeper, ctx sdk.Context, coinbaseAddr common.Address, blockHeader cmtproto.Header) tracing.CosmosStartBlockEvent { // ignore the errors as we are sure that the block header is valid h, _ := cosmostypes.HeaderFromProto(&blockHeader) h.ValidatorsHash = ctx.CometInfo().GetValidatorsHash() @@ -46,7 +47,7 @@ func ToCosmosStartBlockEvent(k *Keeper, ctx sdk.Context, coinbaseBytes []byte, b CosmosHeader: &h, BaseFee: baseFee, GasLimit: gasLimit, - Coinbase: coinbaseBytes, + Coinbase: coinbaseAddr, Finalized: finalizedHeader, } } diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index c80107c30d..1675717a9b 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -17,6 +17,7 @@ package keeper import ( "bytes" + "errors" "fmt" "math/big" "sort" @@ -40,7 +41,7 @@ import ( "github.com/ethereum/go-ethereum/params" ) -var configOverridesErrDesc = "failed to apply state override" +var ErrConfigOverrides = errors.New("failed to apply state override") // NewEVM generates a go-ethereum VM from the provided Message fields and the chain parameters // (ChainConfig and module Params). It additionally sets the validator operator address as the @@ -189,6 +190,20 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) // pass true to commit the StateDB res, applyMessageErr := k.ApplyMessageWithConfig(tmpCtx, msg, cfg, true) if applyMessageErr != nil { + + // Any of these errors will not impact the evm state / execution flow + if errorsmod.IsOf(applyMessageErr, types.ErrCreateDisabled, types.ErrCallDisabled, ErrConfigOverrides) { + return nil, errorsmod.Wrap(applyMessageErr, "failed to apply ethereum core message, issue with create, call or config overrides") + } + + // Call onTxEnd tracer hook with an empty receipt + if cfg.Tracer != nil && cfg.Tracer.OnTxEnd != nil { + cfg.Tracer.OnTxEnd( + nil, + applyMessageErr, + ) + } + return nil, errorsmod.Wrap(applyMessageErr, "failed to apply ethereum core message") } @@ -234,19 +249,10 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) } defer func() { - // These errors are not vm related, so they should not be passed to the vm tracer - if errorsmod.IsOf(applyMessageErr, types.ErrCreateDisabled, types.ErrCallDisabled) { - return - } - - if applyMessageErr != nil && applyMessageErr.Error() != configOverridesErrDesc { - return - } - if cfg.Tracer != nil && cfg.Tracer.OnTxEnd != nil { cfg.Tracer.OnTxEnd( receipt, - applyMessageErr, + errors.New(res.VmError), ) } }() @@ -347,7 +353,7 @@ func (k *Keeper) ApplyMessageWithConfig( var evm *vm.EVM if cfg.Overrides != nil { if err := cfg.Overrides.Apply(stateDB); err != nil { - return nil, errorsmod.Wrap(err, configOverridesErrDesc) + return nil, errorsmod.Wrap(ErrConfigOverrides, err.Error()) } } @@ -412,7 +418,8 @@ func (k *Keeper) ApplyMessageWithConfig( stateDB.Prepare(rules, msg.From, cfg.CoinBase, msg.To, vm.DefaultActivePrecompiles(rules), msg.AccessList) if contractCreation { - // FIXME: also why do we want to set the nonce in the statedb twice here? + // Why do we want to set the nonce in the statedb twice here? + // take over the nonce management from evm: // - reset sender's nonce to msg.Nonce() before calling evm. // - increase sender's nonce by one no matter the result. diff --git a/x/evm/tracers/firehose.go b/x/evm/tracers/firehose.go index 38054385a2..a302974203 100644 --- a/x/evm/tracers/firehose.go +++ b/x/evm/tracers/firehose.go @@ -710,7 +710,6 @@ func (f *Firehose) OnTxEnd(receipt *types.Receipt, err error) { firehoseInfo("trx ending (tracer=%s, isolated=%t, error=%s)", f.tracerID, f.transactionIsolated, errorView(err)) f.ensureInBlockAndInTrx() - // TODO: we do nothing with the error here ??? // Shouldn't there be something that checks the type of error and sets the status of the transaction accordingly? trxTrace := f.completeTransaction(receipt) @@ -1681,7 +1680,7 @@ func newBlockHeaderFromChainHeader(h *types.Header, td *pbeth.BigInt) *pbeth.Blo } // FIXME: Create a unit test that is going to fail as soon as any header is added in -func newBlockHeaderFromCosmosChainHeader(h *cosmostypes.Header, coinbase []byte, gasLimit uint64, baseFee *big.Int) *pbeth.BlockHeader { +func newBlockHeaderFromCosmosChainHeader(h *cosmostypes.Header, coinbase common.Address, gasLimit uint64, baseFee *big.Int) *pbeth.BlockHeader { difficulty := firehoseBigIntFromNative(new(big.Int).SetInt64(0)) parentBlockHash := h.LastBlockID.Hash @@ -1700,7 +1699,7 @@ func newBlockHeaderFromCosmosChainHeader(h *cosmostypes.Header, coinbase []byte, ParentHash: parentBlockHash, Number: uint64(h.Height), UncleHash: types.EmptyUncleHash.Bytes(), // No uncles in Tendermint - Coinbase: coinbase, + Coinbase: coinbase.Bytes(), StateRoot: h.AppHash, TransactionsRoot: transactionRoot, ReceiptRoot: types.EmptyRootHash.Bytes(), @@ -2420,7 +2419,7 @@ func (m Memory) GetPtr(offset, size int64) []byte { return nil } - if len(m) >= (int(offset) + int(size)) { + if offset >= 0 && size >= 0 && int64(len(m)) >= offset+size { return m[offset : offset+size] } @@ -2431,6 +2430,10 @@ func (m Memory) GetPtr(offset, size int64) []byte { // executed so the memory will be of the correct size. // // In this situation, we must pad with zeroes when the memory is not big enough. + if offset >= int64(len(m)) { + return make([]byte, size) + } + reminder := m[offset:] return append(reminder, make([]byte, int(size)-len(reminder))...) } diff --git a/x/evm/tracers/firehose_test.go b/x/evm/tracers/firehose_test.go index d4f64f8883..81d5546b21 100644 --- a/x/evm/tracers/firehose_test.go +++ b/x/evm/tracers/firehose_test.go @@ -36,7 +36,7 @@ func TestFirehoseCallStack_Push(t *testing.T) { actions []actionRunner }{ { - "push/pop emtpy", []actionRunner{ + "push/pop empty", []actionRunner{ push(&pbeth.Call{}), pop(), check(func(t *testing.T, s *CallStack) { @@ -84,7 +84,7 @@ func Test_validateKnownTransactionTypes(t *testing.T) { }{ {"legacy", 0, true, nil}, {"access_list", 1, true, nil}, - {"inexistant", 255, false, nil}, + {"nonexistent", 255, false, nil}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -352,7 +352,7 @@ func TestFirehose_reorderIsolatedTransactionsAndOrdinals(t *testing.T) { goldenUpdate := os.Getenv("GOLDEN_UPDATE") == "true" goldenPath := tt.expectedBlockFile - if !goldenUpdate && !fileExits(t, goldenPath) { + if !goldenUpdate && !fileExists(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()) } @@ -367,7 +367,8 @@ func TestFirehose_reorderIsolatedTransactionsAndOrdinals(t *testing.T) { require.NoError(t, err) expectedBlock := &pbeth.Block{} - protojson.Unmarshal(expected, expectedBlock) + err = protojson.Unmarshal(expected, expectedBlock) + require.NoError(t, err) 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()) @@ -434,7 +435,7 @@ 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 { +func fileExists(t *testing.T, path string) bool { t.Helper() stat, err := os.Stat(path) return err == nil && !stat.IsDir() @@ -484,6 +485,7 @@ func TestMemory_GetPtr(t *testing.T) { {"memory is just a bit too small", Memory([]byte{1, 2, 3}), args{0, 4}, []byte{1, 2, 3, 0}}, {"memory is flushed with request", Memory([]byte{1, 2, 3, 4}), args{0, 4}, []byte{1, 2, 3, 4}}, {"memory is just a bit too big", Memory([]byte{1, 2, 3, 4, 5}), args{0, 4}, []byte{1, 2, 3, 4}}, + {"offset beyond memory length", Memory([]byte{1, 2, 3}), args{5, 2}, []byte{0, 0}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/x/evm/tracing/hooks.go b/x/evm/tracing/hooks.go index 36d1412ef8..af3a042cf6 100644 --- a/x/evm/tracing/hooks.go +++ b/x/evm/tracing/hooks.go @@ -33,7 +33,7 @@ type CosmosStartBlockEvent struct { CosmosHeader *types.Header BaseFee *big.Int GasLimit uint64 - Coinbase []byte + Coinbase common.Address Finalized *ethtypes.Header } From 2eb08d9e9e3c545c36ee68a5d8b4545fef027a3f Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Thu, 17 Oct 2024 16:49:40 -0400 Subject: [PATCH 27/45] removing todo --- x/evm/handler_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/evm/handler_test.go b/x/evm/handler_test.go index e721f6c593..d0e97c5152 100644 --- a/x/evm/handler_test.go +++ b/x/evm/handler_test.go @@ -104,7 +104,7 @@ func (suite *HandlerTestSuite) TestHandleMsgEthereumTx() { true, }, { - "insufficient balance", // todo: shouldn't this be insufficient gas + "insufficient balance", func() { tx = types.NewTxContract(suite.chainID, 0, big.NewInt(100), 0, big.NewInt(10000), nil, nil, nil, nil) suite.signTx(tx) From 81ed849c124b994f5fef4e53caa554fbc274db8d Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Thu, 17 Oct 2024 18:35:03 -0400 Subject: [PATCH 28/45] removing todo comment --- x/evm/genesis.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/x/evm/genesis.go b/x/evm/genesis.go index 1aa6edf16a..db3799d35b 100644 --- a/x/evm/genesis.go +++ b/x/evm/genesis.go @@ -82,8 +82,6 @@ func InitGenesis( } } - // todo: should we call FHTracer.OnGenesisBlock here? - return []abci.ValidatorUpdate{} } From 5d807a1874b31dcec18e43b9d03fb5e428796c4e Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Fri, 18 Oct 2024 14:07:15 -0400 Subject: [PATCH 29/45] Removing global registry --- x/evm/tracers/firehose.go | 1 - x/evm/tracers/registry.go | 8 -------- 2 files changed, 9 deletions(-) diff --git a/x/evm/tracers/firehose.go b/x/evm/tracers/firehose.go index a302974203..1f07055a35 100644 --- a/x/evm/tracers/firehose.go +++ b/x/evm/tracers/firehose.go @@ -68,7 +68,6 @@ func init() { staticFirehoseChainValidationOnInit() tracers.LiveDirectory.Register("firehose", newFirehoseTracer) - GlobalLiveTracerRegistry.Register("firehose", NewCosmosFirehoseTracer) // Those 2 are defined but not used in this branch, they are kept because used in other branches // so it's easier to keep them here and suppress the warning by faking a usage. diff --git a/x/evm/tracers/registry.go b/x/evm/tracers/registry.go index 71731343dd..36d47e129d 100644 --- a/x/evm/tracers/registry.go +++ b/x/evm/tracers/registry.go @@ -1,7 +1,5 @@ package tracers -var GlobalLiveTracerRegistry = NewLiveTracerRegistry() - type LiveTracerRegistry interface { GetFactoryByID(id string) (BlockchainTracerFactory, bool) Register(id string, factory BlockchainTracerFactory) @@ -9,12 +7,6 @@ type LiveTracerRegistry interface { var _ LiveTracerRegistry = (*liveTracerRegistry)(nil) -func NewLiveTracerRegistry() LiveTracerRegistry { - return &liveTracerRegistry{ - tracers: make(map[string]BlockchainTracerFactory), - } -} - type liveTracerRegistry struct { tracers map[string]BlockchainTracerFactory } From 4a1c9947269776e237fe3a8f31756ab83ca6613a Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Mon, 21 Oct 2024 16:40:43 +0200 Subject: [PATCH 30/45] refactor: removce EVMConfigWithTracer --- x/evm/keeper/config.go | 19 +++---------------- x/evm/keeper/state_transition.go | 2 +- x/evm/keeper/state_transition_test.go | 9 +++++---- 3 files changed, 9 insertions(+), 21 deletions(-) diff --git a/x/evm/keeper/config.go b/x/evm/keeper/config.go index 4226ba73fe..3c77efb39f 100644 --- a/x/evm/keeper/config.go +++ b/x/evm/keeper/config.go @@ -16,9 +16,10 @@ package keeper import ( - cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "math/big" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/common" @@ -129,21 +130,7 @@ func (k *Keeper) EVMConfig(ctx sdk.Context, chainID *big.Int, txHash common.Hash cfg := &EVMConfig{ EVMBlockConfig: blockCfg, TxConfig: txConfig, - } - - return cfg, nil -} - -// EVMConfigWithTracer creates the EVMConfig based on current state and returns the evmTracer -func (k *Keeper) EVMConfigWithTracer(ctx sdk.Context, chainID *big.Int, txHash common.Hash) (*EVMConfig, error) { - cfg, err := k.EVMConfig(ctx, chainID, txHash) - - if err != nil { - return nil, err - } - - if k.evmTracer != nil { - cfg.Tracer = k.evmTracer + Tracer: k.evmTracer, } return cfg, nil diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 1675717a9b..89adfb258d 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -170,7 +170,7 @@ func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc { // For relevant discussion see: https://github.com/cosmos/cosmos-sdk/discussions/9072 func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) (*types.MsgEthereumTxResponse, error) { ethTx := msgEth.AsTransaction() - cfg, err := k.EVMConfigWithTracer(ctx, k.eip155ChainID, ethTx.Hash()) + cfg, err := k.EVMConfig(ctx, k.eip155ChainID, ethTx.Hash()) if err != nil { return nil, errorsmod.Wrap(err, "failed to load evm config") } diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 6680b20047..21675b23eb 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -1,14 +1,15 @@ package keeper_test import ( - "cosmossdk.io/core/comet" "fmt" - "github.com/ethereum/go-ethereum/core/tracing" - cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "math" "math/big" "testing" + "cosmossdk.io/core/comet" + "github.com/ethereum/go-ethereum/core/tracing" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" + sdkmath "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" "github.com/cometbft/cometbft/crypto/tmhash" @@ -718,7 +719,7 @@ func (suite *StateTransitionTestSuite) TestApplyMessageWithConfigTracer() { suite.Ctx = cosmostracing.SetCtxBlockchainTracer(suite.Ctx, t) suite.App.EvmKeeper.SetTracer(t) - cfgWithTracer, err := suite.App.EvmKeeper.EVMConfigWithTracer(suite.Ctx, big.NewInt(9000), common.Hash{}) + cfgWithTracer, err := suite.App.EvmKeeper.EVMConfig(suite.Ctx, big.NewInt(9000), common.Hash{}) suite.Require().NoError(err) keeperParams := suite.App.EvmKeeper.GetParams(suite.Ctx) From 4c4cf62001244ff65c766676f1a952ec5f449d99 Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Mon, 21 Oct 2024 17:01:37 +0200 Subject: [PATCH 31/45] refactor: comment choice of tracer in prepareTrace --- x/evm/keeper/grpc_query.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/x/evm/keeper/grpc_query.go b/x/evm/keeper/grpc_query.go index 5eee851acd..9fb97d6aa9 100644 --- a/x/evm/keeper/grpc_query.go +++ b/x/evm/keeper/grpc_query.go @@ -20,10 +20,11 @@ import ( "encoding/json" "errors" "fmt" - cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "math/big" "time" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" + "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers/logger" @@ -645,6 +646,7 @@ func (k *Keeper) prepareTrace( overrides = traceConfig.Overrides.EthereumConfig(cfg.ChainConfig.ChainID) } + // setup default tracer logConfig := logger.Config{ EnableMemory: traceConfig.EnableMemory, DisableStorage: traceConfig.DisableStorage, @@ -654,7 +656,6 @@ func (k *Keeper) prepareTrace( Limit: int(traceConfig.Limit), Overrides: overrides, } - logger := logger.NewStructLogger(&logConfig) tracer = &tracers.Tracer{ Hooks: logger.Hooks(), @@ -662,17 +663,17 @@ func (k *Keeper) prepareTrace( Stop: logger.Stop, } - txIndex, err := ethermint.SafeInt(txConfig.TxIndex) - if err != nil { - return nil, 0, status.Error(codes.Internal, err.Error()) - } - - tCtx := &tracers.Context{ - BlockHash: txConfig.BlockHash, - TxIndex: txIndex, - TxHash: txConfig.TxHash, - } + // override default tracer if traceConfig.Tracer is set if traceConfig.Tracer != "" { + txIndex, err := ethermint.SafeInt(txConfig.TxIndex) + if err != nil { + return nil, 0, status.Error(codes.Internal, err.Error()) + } + tCtx := &tracers.Context{ + BlockHash: txConfig.BlockHash, + TxIndex: txIndex, + TxHash: txConfig.TxHash, + } var cfg json.RawMessage if traceConfig.TracerJsonConfig != "" { cfg = json.RawMessage(traceConfig.TracerJsonConfig) From ba1afe577b895d37dd7b8e5060ff84185092711a Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Mon, 21 Oct 2024 17:09:34 +0200 Subject: [PATCH 32/45] refactor: update comment on NewEVM about RANDAO --- x/evm/keeper/state_transition.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 89adfb258d..16c1779898 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -49,7 +49,7 @@ var ErrConfigOverrides = errors.New("failed to apply state override") // beneficiary of the coinbase transaction (since we're not mining). // // NOTE: the RANDOM opcode is currently not supported since it requires -// RANDOM implementation. See https://github.com/evmos/ethermint/pull/1520#pullrequestreview-1200504697 +// RANDAO implementation. See https://github.com/evmos/ethermint/pull/1520#pullrequestreview-1200504697 // for more information. func (k *Keeper) NewEVM( ctx sdk.Context, From 5d3ad487731ba8de504dceeb9e10bf203e075d04 Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Mon, 21 Oct 2024 17:41:52 +0200 Subject: [PATCH 33/45] chore: add nil check in StateDB.AddLog --- x/evm/statedb/statedb.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index b3cc167185..577ff077c0 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -17,10 +17,11 @@ package statedb import ( "fmt" - cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "math/big" "sort" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" + errorsmod "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" "cosmossdk.io/store/cachemulti" @@ -170,7 +171,7 @@ func (s *StateDB) AddLog(log *ethtypes.Log) { log.Index = s.txConfig.LogIndex + uint(len(s.logs)) s.logs = append(s.logs, log) - if s.evmTracer != nil && s.evmTracer.OnLog != nil { + if log != nil && s.evmTracer != nil && s.evmTracer.OnLog != nil { s.evmTracer.OnLog(log) } } From f865e0088359569605bbb46fd28de32d81494303 Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Tue, 22 Oct 2024 14:43:30 +0200 Subject: [PATCH 34/45] chore: remove duplicate package alias in statedb_test.go --- x/evm/statedb/statedb_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/x/evm/statedb/statedb_test.go b/x/evm/statedb/statedb_test.go index 905d468684..5e62c027e7 100644 --- a/x/evm/statedb/statedb_test.go +++ b/x/evm/statedb/statedb_test.go @@ -33,7 +33,6 @@ import ( ethermint "github.com/evmos/ethermint/types" evmkeeper "github.com/evmos/ethermint/x/evm/keeper" "github.com/evmos/ethermint/x/evm/statedb" - "github.com/evmos/ethermint/x/evm/types" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/holiman/uint256" "github.com/stretchr/testify/require" @@ -171,7 +170,7 @@ func (suite *StateDBTestSuite) TestTracer_Balance() { suite.Run(tc.name, func() { var balanceChanges []balanceChange raw, ctx, keeper := setupTestEnv(suite.T()) - t, err := types.NewFirehoseCosmosLiveTracer() + t, err := evmtypes.NewFirehoseCosmosLiveTracer() require.NoError(suite.T(), err) t.OnBalanceChange = func(addr common.Address, prev, new *big.Int, reason ethtracing.BalanceChangeReason) { balanceChanges = append(balanceChanges, balanceChange{prev.String(), new.String(), reason}) @@ -336,7 +335,7 @@ func (suite *StateDBTestSuite) TestTracer_Nonce() { suite.Run(tc.name, func() { var nonceChanges []uint64 _, ctx, keeper := setupTestEnv(suite.T()) - t, err := types.NewFirehoseCosmosLiveTracer() + t, err := evmtypes.NewFirehoseCosmosLiveTracer() require.NoError(suite.T(), err) t.OnNonceChange = func(addr common.Address, prev, new uint64) { nonceChanges = append(nonceChanges, new) @@ -1073,7 +1072,7 @@ func (suite *StateDBTestSuite) TestTracer_SetStorage() { var sChanges []storageChanges _, ctx, keeper := setupTestEnv(suite.T()) - t, err := types.NewFirehoseCosmosLiveTracer() + t, err := evmtypes.NewFirehoseCosmosLiveTracer() require.NoError(suite.T(), err) t.OnStorageChange = func(addr common.Address, slot common.Hash, prev, new common.Hash) { sChanges = append(sChanges, newStorageChange(addr.String(), slot.String(), prev.String(), new.String())) @@ -1207,7 +1206,7 @@ func setupTestEnv(t *testing.T) (storetypes.MultiStore, sdk.Context, *evmkeeper. ctx, keeper := newTestKeeper(t, cms) require.NoError(t, keeper.SetParams(ctx, evmtypes.Params{ - EvmDenom: types.DefaultEVMDenom, + EvmDenom: evmtypes.DefaultEVMDenom, })) return cms, ctx, keeper } From 6a35fc92009b00d952471a4d81d452e6836848a2 Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Tue, 22 Oct 2024 14:56:13 +0200 Subject: [PATCH 35/45] refactor: delete unused LiveTracerRegistry --- x/evm/tracers/registry.go | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 x/evm/tracers/registry.go diff --git a/x/evm/tracers/registry.go b/x/evm/tracers/registry.go deleted file mode 100644 index 36d47e129d..0000000000 --- a/x/evm/tracers/registry.go +++ /dev/null @@ -1,23 +0,0 @@ -package tracers - -type LiveTracerRegistry interface { - GetFactoryByID(id string) (BlockchainTracerFactory, bool) - Register(id string, factory BlockchainTracerFactory) -} - -var _ LiveTracerRegistry = (*liveTracerRegistry)(nil) - -type liveTracerRegistry struct { - tracers map[string]BlockchainTracerFactory -} - -// Register implements LiveTracerRegistry. -func (r *liveTracerRegistry) Register(id string, factory BlockchainTracerFactory) { - r.tracers[id] = factory -} - -// GetFactoryByID implements LiveTracerRegistry. -func (r *liveTracerRegistry) GetFactoryByID(id string) (BlockchainTracerFactory, bool) { - v, found := r.tracers[id] - return v, found -} From 753bad6aa510d45d1ccb11c5537964ec1369011a Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Tue, 22 Oct 2024 14:57:24 +0200 Subject: [PATCH 36/45] refactor: delete unused BlockchainTraceFactory --- x/evm/tracers/tracing.go | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 x/evm/tracers/tracing.go diff --git a/x/evm/tracers/tracing.go b/x/evm/tracers/tracing.go deleted file mode 100644 index c8d19ef146..0000000000 --- a/x/evm/tracers/tracing.go +++ /dev/null @@ -1,12 +0,0 @@ -package tracers - -import ( - "github.com/evmos/ethermint/x/evm/tracing" -) - -// BlockchainTracerFactory is a function that creates a new [BlockchainTracer]. -// It's going to receive the parsed URL from the `live-evm-tracer` flag. -// -// The scheme of the URL is going to be used to determine which tracer to use -// by the registry. -type BlockchainTracerFactory = func(backwardCompatibility bool) (*tracing.Hooks, error) From 7f52fb5966344e2aa27666f7a7c61578759ed1e8 Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Tue, 22 Oct 2024 15:06:10 +0200 Subject: [PATCH 37/45] refactor: change name of methods that get/set tracing hooks in context --- x/evm/keeper/abci.go | 4 ++-- x/evm/keeper/keeper.go | 5 +++-- x/evm/keeper/state_transition_test.go | 4 ++-- x/evm/tracers/firehose.go | 5 +++-- x/evm/tracing/context.go | 23 +++++++---------------- x/evm/tracing/hooks.go | 17 ++++++++++------- 6 files changed, 27 insertions(+), 31 deletions(-) diff --git a/x/evm/keeper/abci.go b/x/evm/keeper/abci.go index d926772e1e..29b6dd89bb 100644 --- a/x/evm/keeper/abci.go +++ b/x/evm/keeper/abci.go @@ -31,7 +31,7 @@ func (k *Keeper) BeginBlock(ctx sdk.Context) error { } // In the case of BeginBlock hook, we can extract the tracer from the context - if tracer := cosmostracing.GetCtxBlockchainTracer(ctx); tracer != nil && tracer.OnCosmosBlockStart != nil { + if tracer := cosmostracing.GetTracingHooks(ctx); tracer != nil && tracer.OnCosmosBlockStart != nil { tracer.OnCosmosBlockStart( ToCosmosStartBlockEvent( k, @@ -53,7 +53,7 @@ func (k *Keeper) EndBlock(ctx sdk.Context) error { k.RemoveParamsCache(ctx) // In the case of EndBlock hook, we can extract the tracer from the context - if tracer := cosmostracing.GetCtxBlockchainTracer(ctx); tracer != nil && tracer.OnCosmosBlockEnd != nil { + if tracer := cosmostracing.GetTracingHooks(ctx); tracer != nil && tracer.OnCosmosBlockEnd != nil { tracer.OnCosmosBlockEnd(ToCosmosEndBlockEvent(k, ctx), nil) } diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 018038ca4a..67fce78b1a 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -16,9 +16,10 @@ package keeper import ( - cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "math/big" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" + errorsmod "cosmossdk.io/errors" "cosmossdk.io/log" storetypes "cosmossdk.io/store/types" @@ -139,7 +140,7 @@ func (k Keeper) ChainID() *big.Int { } func (k *Keeper) InitChainer(ctx sdk.Context) { - if tracer := cosmostracing.GetCtxBlockchainTracer(ctx); tracer != nil && tracer.OnBlockchainInit != nil { + if tracer := cosmostracing.GetTracingHooks(ctx); tracer != nil && tracer.OnBlockchainInit != nil { tracer.OnBlockchainInit(types.DefaultChainConfig().EthereumConfig(k.ChainID())) } } diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 21675b23eb..7f0399300e 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -652,7 +652,7 @@ func (suite *StateTransitionTestSuite) TestApplyTransactionWithTracer() { t, err := types.NewFirehoseCosmosLiveTracer() require.NoError(suite.T(), err) - suite.Ctx = cosmostracing.SetCtxBlockchainTracer(suite.Ctx, t) + suite.Ctx = cosmostracing.SetTracingHooks(suite.Ctx, t) suite.App.EvmKeeper.SetTracer(t) keeperParams := suite.App.EvmKeeper.GetParams(suite.Ctx) @@ -716,7 +716,7 @@ func (suite *StateTransitionTestSuite) TestApplyMessageWithConfigTracer() { t, err := types.NewFirehoseCosmosLiveTracer() require.NoError(suite.T(), err) - suite.Ctx = cosmostracing.SetCtxBlockchainTracer(suite.Ctx, t) + suite.Ctx = cosmostracing.SetTracingHooks(suite.Ctx, t) suite.App.EvmKeeper.SetTracer(t) cfgWithTracer, err := suite.App.EvmKeeper.EVMConfig(suite.Ctx, big.NewInt(9000), common.Hash{}) diff --git a/x/evm/tracers/firehose.go b/x/evm/tracers/firehose.go index 1f07055a35..b39b2f8361 100644 --- a/x/evm/tracers/firehose.go +++ b/x/evm/tracers/firehose.go @@ -8,8 +8,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/ethereum/go-ethereum/eth/tracers" - cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "io" "math/big" "os" @@ -23,6 +21,9 @@ import ( "sync/atomic" "time" + "github.com/ethereum/go-ethereum/eth/tracers" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" + cosmostypes "github.com/cometbft/cometbft/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" diff --git a/x/evm/tracing/context.go b/x/evm/tracing/context.go index aaaf052ae4..7271696899 100644 --- a/x/evm/tracing/context.go +++ b/x/evm/tracing/context.go @@ -2,21 +2,20 @@ package tracing import ( "context" + sdk "github.com/cosmos/cosmos-sdk/types" ) -type CtxBlockchainTracerKeyType string +type BlockchainTracerKeyType string -const CtxBlockchainTracerKey = CtxBlockchainTracerKeyType("evm_and_state_logger") +const BlockchainTracerKey = BlockchainTracerKeyType("evm_and_state_logger") -func SetCtxBlockchainTracer(ctx sdk.Context, logger *Hooks) sdk.Context { - return ctx.WithContext(context.WithValue(ctx.Context(), CtxBlockchainTracerKey, logger)) +func SetTracingHooks(ctx sdk.Context, hooks *Hooks) sdk.Context { + return ctx.WithContext(context.WithValue(ctx.Context(), BlockchainTracerKey, hooks)) } -// GetCtxBlockchainTracer function to get the Cosmos specific [tracing.Hooks] struct -// used to trace EVM blocks and transactions. -func GetCtxBlockchainTracer(ctx sdk.Context) *Hooks { - rawVal := ctx.Context().Value(CtxBlockchainTracerKey) +func GetTracingHooks(ctx sdk.Context) *Hooks { + rawVal := ctx.Context().Value(BlockchainTracerKey) if rawVal == nil { return nil } @@ -26,11 +25,3 @@ func GetCtxBlockchainTracer(ctx sdk.Context) *Hooks { } return logger } - -func GetCtxEthTracingHooks(ctx sdk.Context) *Hooks { - if logger := GetCtxBlockchainTracer(ctx); logger != nil { - return logger - } - - return nil -} diff --git a/x/evm/tracing/hooks.go b/x/evm/tracing/hooks.go index af3a042cf6..f005cfa41f 100644 --- a/x/evm/tracing/hooks.go +++ b/x/evm/tracing/hooks.go @@ -1,19 +1,16 @@ package tracing import ( + "math/big" + "github.com/cometbft/cometbft/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/tracing" ethtypes "github.com/ethereum/go-ethereum/core/types" - "math/big" -) - -type ( - OnCosmosBlockStart func(CosmosStartBlockEvent) - OnCosmosBlockEnd func(CosmosEndBlockEvent, error) - OnCosmosTxStart func(evm *tracing.VMContext, tx *ethtypes.Transaction, txHash common.Hash, from common.Address) ) +// Hooks defines a Cosmos specific [tracing.Hooks] struct used to trace EVM +// blocks and transactions. type Hooks struct { *tracing.Hooks @@ -29,6 +26,12 @@ type Hooks struct { OnCosmosTxStart OnCosmosTxStart } +type ( + OnCosmosBlockStart func(CosmosStartBlockEvent) + OnCosmosBlockEnd func(CosmosEndBlockEvent, error) + OnCosmosTxStart func(evm *tracing.VMContext, tx *ethtypes.Transaction, txHash common.Hash, from common.Address) +) + type CosmosStartBlockEvent struct { CosmosHeader *types.Header BaseFee *big.Int From aa2d647f2fa87d53152d1823dc6b66b016b85e3c Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Tue, 22 Oct 2024 15:43:52 +0200 Subject: [PATCH 38/45] refactor: fix imports in x/evm/types/tracer.go --- x/evm/types/tracer.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/x/evm/types/tracer.go b/x/evm/types/tracer.go index 87bafd1a68..397347de79 100644 --- a/x/evm/types/tracer.go +++ b/x/evm/types/tracer.go @@ -17,21 +17,19 @@ package types import ( "fmt" - cosmostracing "github.com/evmos/ethermint/x/evm/tracing" "os" - "github.com/ethereum/go-ethereum/eth/tracers" - cosmostracers "github.com/evmos/ethermint/x/evm/tracers" - - _ "github.com/ethereum/go-ethereum/eth/tracers/live" - _ "github.com/evmos/ethermint/x/evm/tracers" - - "github.com/ethereum/go-ethereum/eth/tracers/logger" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/params" + + // importing the tracers package automatically triggers its init method which + // registers the firehose tracer in the LiveDirectory + cosmostracers "github.com/evmos/ethermint/x/evm/tracers" + cosmostracing "github.com/evmos/ethermint/x/evm/tracing" ) const ( From 0803635abaa620b66400ddc03d78d920d78abe74 Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Tue, 22 Oct 2024 15:48:00 +0200 Subject: [PATCH 39/45] refactor: remove unused NewLiveTracer --- x/evm/types/tracer.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/x/evm/types/tracer.go b/x/evm/types/tracer.go index 397347de79..f85ef169ae 100644 --- a/x/evm/types/tracer.go +++ b/x/evm/types/tracer.go @@ -72,17 +72,6 @@ func NewTracer(tracer string, msg *core.Message, rules params.Rules) *tracers.Tr } } -func NewLiveTracer(tracer string) (*tracers.Tracer, error) { - h, err := tracers.LiveDirectory.New(tracer, nil) - if err != nil { - return nil, fmt.Errorf("initializing live tracer %s: %w", tracer, err) - } - - return &tracers.Tracer{ - Hooks: h, - }, nil -} - func NewFirehoseCosmosLiveTracer() (*cosmostracing.Hooks, error) { h, err := cosmostracers.NewCosmosFirehoseTracer(false) if err != nil { From bd3ef4651957aed7bc909a5bdc7266751def5342 Mon Sep 17 00:00:00 2001 From: Eduard Voiculescu Date: Tue, 22 Oct 2024 11:14:24 -0400 Subject: [PATCH 40/45] removing the firehose switch case from the NewTracer method as we are using custom hooks for the firehose --- x/evm/types/tracer.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/x/evm/types/tracer.go b/x/evm/types/tracer.go index f85ef169ae..47ebefb7cd 100644 --- a/x/evm/types/tracer.go +++ b/x/evm/types/tracer.go @@ -60,8 +60,6 @@ func NewTracer(tracer string, msg *core.Message, rules params.Rules) *tracers.Tr hooks = logger.NewMarkdownLogger(logCfg, os.Stdout).Hooks() // TODO: Stderr ? case TracerStruct: hooks = logger.NewStructLogger(logCfg).Hooks() - case Firehose: - hooks, _ = tracers.LiveDirectory.New("firehose", nil) default: // Use noop tracer by default hooks, _ = tracers.LiveDirectory.New("noop", nil) From 7789c561e19c2767bbaf84651ac13580703db5f2 Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Wed, 23 Oct 2024 15:40:16 +0200 Subject: [PATCH 41/45] chore: add tracer in app.go --- app/app.go | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ server/start.go | 4 ++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/app/app.go b/app/app.go index adf6bb23c3..dedc6fcc68 100644 --- a/app/app.go +++ b/app/app.go @@ -136,6 +136,7 @@ import ( "github.com/evmos/ethermint/x/evm" evmkeeper "github.com/evmos/ethermint/x/evm/keeper" v0evmtypes "github.com/evmos/ethermint/x/evm/migrations/v0/types" + evmtracing "github.com/evmos/ethermint/x/evm/tracing" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/evmos/ethermint/x/feemarket" feemarketkeeper "github.com/evmos/ethermint/x/feemarket/keeper" @@ -148,6 +149,7 @@ import ( // Force-load the tracer engines to trigger registration due to Go-Ethereum v1.10.15 changes _ "github.com/ethereum/go-ethereum/eth/tracers/js" _ "github.com/ethereum/go-ethereum/eth/tracers/native" + ethparams "github.com/ethereum/go-ethereum/params" ) func init() { @@ -248,6 +250,8 @@ type EthermintApp struct { // the configurator configurator module.Configurator + + evmTracer *evmtracing.Hooks } // NewEthermintApp returns a reference to a new initialized Ethermint application. @@ -794,6 +798,27 @@ func NewEthermintApp( app.ScopedIBCKeeper = scopedIBCKeeper app.ScopedTransferKeeper = scopedTransferKeeper + tracer := cast.ToString(appOpts.Get(srvflags.EVMTracer)) + + // Currently only the firehose live tracer is supported + if tracer == "firehose" { + liveTracer, err := evmtypes.NewFirehoseCosmosLiveTracer() + if err != nil { + panic(err) + } + app.EvmKeeper.SetTracer(liveTracer) + app.evmTracer = liveTracer + } else if tracer == "access_list" { + panic("access_list tracer is not supported") + } else if tracer != "" { + liveTracer := evmtypes.NewTracer(tracer, nil, ethparams.Rules{}) + t := &evmtracing.Hooks{ + Hooks: liveTracer.Hooks, + } + app.EvmKeeper.SetTracer(t) + app.evmTracer = t + } + return app } @@ -851,11 +876,26 @@ func (app *EthermintApp) PreBlocker(ctx sdk.Context, _ *abci.RequestFinalizeBloc // BeginBlocker updates every begin block func (app *EthermintApp) BeginBlocker(ctx sdk.Context) (sdk.BeginBlock, error) { + if app.evmTracer != nil { + ctx = evmtracing.SetTracingHooks(ctx, app.evmTracer) + } + + // Cosmos chains will only call InitChainer when the chain either starts + // from genesis or is being upgraded and a full state initialization is needed + if app.EvmKeeper != nil && app.EvmKeeper.ChainID() == nil { + app.EvmKeeper.WithChainID(ctx) + app.EvmKeeper.InitChainer(ctx) + } + return app.ModuleManager.BeginBlock(ctx) } // EndBlocker updates every end block func (app *EthermintApp) EndBlocker(ctx sdk.Context) (sdk.EndBlock, error) { + if app.evmTracer != nil { + ctx = evmtracing.SetTracingHooks(ctx, app.evmTracer) + } + return app.ModuleManager.EndBlock(ctx) } @@ -872,6 +912,16 @@ func (app *EthermintApp) InitChainer(ctx sdk.Context, req *abci.RequestInitChain if err := app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap()); err != nil { return nil, err } + + if app.evmTracer != nil { + ctx = evmtracing.SetTracingHooks(ctx, app.evmTracer) + } + + if app.EvmKeeper != nil { + app.EvmKeeper.WithChainID(ctx) + app.EvmKeeper.InitChainer(ctx) + } + return app.ModuleManager.InitGenesis(ctx, app.appCodec, genesisState) } diff --git a/server/start.go b/server/start.go index e1a5854ac6..4c1fece5f1 100644 --- a/server/start.go +++ b/server/start.go @@ -219,8 +219,8 @@ which accepts a path for the resulting pprof file. cmd.Flags().Bool(srvflags.JSONRPCAllowIndexerGap, true, "Allow block gap for the custom tx indexer for json-rpc") cmd.Flags().Bool(srvflags.JSONRPCEnableMetrics, false, "Define if EVM rpc metrics server should be enabled") - cmd.Flags().String(srvflags.EVMTracer, config.DefaultEVMTracer, "the EVM tracer type to collect execution traces from the EVM transaction execution (json|struct|access_list|markdown)") //nolint:lll - cmd.Flags().Uint64(srvflags.EVMMaxTxGasWanted, config.DefaultMaxTxGasWanted, "the gas wanted for each eth tx returned in ante handler in check tx mode") //nolint:lll + cmd.Flags().String(srvflags.EVMTracer, config.DefaultEVMTracer, "the EVM tracer type to collect execution traces from the EVM transaction execution (json|struct|access_list|markdown|firestore)") //nolint:lll + cmd.Flags().Uint64(srvflags.EVMMaxTxGasWanted, config.DefaultMaxTxGasWanted, "the gas wanted for each eth tx returned in ante handler in check tx mode") //nolint:lll cmd.Flags().String(srvflags.TLSCertPath, "", "the cert.pem file path for the server TLS configuration") cmd.Flags().String(srvflags.TLSKeyPath, "", "the key.pem file path for the server TLS configuration") From 7db3bd18c81188809c265a51aa7bc501d91931df Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Wed, 23 Oct 2024 16:19:15 +0200 Subject: [PATCH 42/45] chore: refactor initializeEVM --- app/app.go | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/app.go b/app/app.go index dedc6fcc68..c7cf0e92f6 100644 --- a/app/app.go +++ b/app/app.go @@ -866,6 +866,13 @@ func (app *EthermintApp) setPostHandler() { app.SetPostHandler(postHandler) } +func (app *EthermintApp) initializeEVM(ctx sdk.Context) { + if app.EvmKeeper != nil && app.EvmKeeper.ChainID() == nil { + app.EvmKeeper.WithChainID(ctx) + app.EvmKeeper.InitChainer(ctx) + } +} + // Name returns the name of the App func (app *EthermintApp) Name() string { return app.BaseApp.Name() } @@ -880,12 +887,7 @@ func (app *EthermintApp) BeginBlocker(ctx sdk.Context) (sdk.BeginBlock, error) { ctx = evmtracing.SetTracingHooks(ctx, app.evmTracer) } - // Cosmos chains will only call InitChainer when the chain either starts - // from genesis or is being upgraded and a full state initialization is needed - if app.EvmKeeper != nil && app.EvmKeeper.ChainID() == nil { - app.EvmKeeper.WithChainID(ctx) - app.EvmKeeper.InitChainer(ctx) - } + app.initializeEVM(ctx) return app.ModuleManager.BeginBlock(ctx) } @@ -917,10 +919,7 @@ func (app *EthermintApp) InitChainer(ctx sdk.Context, req *abci.RequestInitChain ctx = evmtracing.SetTracingHooks(ctx, app.evmTracer) } - if app.EvmKeeper != nil { - app.EvmKeeper.WithChainID(ctx) - app.EvmKeeper.InitChainer(ctx) - } + app.initializeEVM(ctx) return app.ModuleManager.InitGenesis(ctx, app.appCodec, genesisState) } From e9421167bd0d7b5e93704c8046e479dca0491491 Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Wed, 23 Oct 2024 16:38:37 +0200 Subject: [PATCH 43/45] chore: define error type for ErrConfigOverrides --- x/evm/keeper/state_transition.go | 6 ++---- x/evm/types/errors.go | 3 +++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 16c1779898..7f0850a079 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -41,8 +41,6 @@ import ( "github.com/ethereum/go-ethereum/params" ) -var ErrConfigOverrides = errors.New("failed to apply state override") - // NewEVM generates a go-ethereum VM from the provided Message fields and the chain parameters // (ChainConfig and module Params). It additionally sets the validator operator address as the // coinbase address to make it available for the COINBASE opcode, even though there is no @@ -192,7 +190,7 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) if applyMessageErr != nil { // Any of these errors will not impact the evm state / execution flow - if errorsmod.IsOf(applyMessageErr, types.ErrCreateDisabled, types.ErrCallDisabled, ErrConfigOverrides) { + if errorsmod.IsOf(applyMessageErr, types.ErrCreateDisabled, types.ErrCallDisabled, types.ErrConfigOverrides) { return nil, errorsmod.Wrap(applyMessageErr, "failed to apply ethereum core message, issue with create, call or config overrides") } @@ -353,7 +351,7 @@ func (k *Keeper) ApplyMessageWithConfig( var evm *vm.EVM if cfg.Overrides != nil { if err := cfg.Overrides.Apply(stateDB); err != nil { - return nil, errorsmod.Wrap(ErrConfigOverrides, err.Error()) + return nil, errorsmod.Wrap(types.ErrConfigOverrides, err.Error()) } } diff --git a/x/evm/types/errors.go b/x/evm/types/errors.go index 87179c50f6..ef69399413 100644 --- a/x/evm/types/errors.go +++ b/x/evm/types/errors.go @@ -49,6 +49,7 @@ const ( codeErrGasOverflow codeErrInvalidAccount codeErrInvalidGasLimit + codeErrConfigOverries ) var ErrPostTxProcessing = errors.New("failed to execute post processing") @@ -116,6 +117,8 @@ var ( // ErrInvalidGasLimit returns an error if gas limit value is invalid ErrInvalidGasLimit = errorsmod.Register(ModuleName, codeErrInvalidGasLimit, "invalid gas limit") + + ErrConfigOverrides = errorsmod.Register(ModuleName, codeErrConfigOverries, "failed to apply state override") ) // NewExecErrorWithReason unpacks the revert return bytes and returns a wrapped error From 5be6f4c4f0d33d29b68767bba0ca3f1b40ee279c Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Wed, 23 Oct 2024 16:46:25 +0200 Subject: [PATCH 44/45] chore: update TestApplyMessage --- x/evm/keeper/state_transition_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 7f0399300e..24cdc7c086 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -619,7 +619,6 @@ func (suite *StateTransitionTestSuite) TestApplyMessage() { chainCfg := keeperParams.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()) rules := chainCfg.Rules(big.NewInt(suite.Ctx.BlockHeight()), chainCfg.MergeNetsplitBlock != nil, uint64(suite.Ctx.BlockHeader().Time.Unix())) signer := ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) - tracer := types.NewTracer("", msg, rules) vmdb := suite.StateDB() msg, err = newNativeMessage( @@ -635,6 +634,8 @@ func (suite *StateTransitionTestSuite) TestApplyMessage() { ) suite.Require().NoError(err) + tracer := types.NewTracer("", msg, rules) + res, err := suite.App.EvmKeeper.ApplyMessage(suite.Ctx, msg, tracer, true) suite.Require().NoError(err) From ffcfd44373b0d312726c591a6b2baaebb972aff8 Mon Sep 17 00:00:00 2001 From: Martin Arrivets Date: Wed, 23 Oct 2024 17:07:21 +0200 Subject: [PATCH 45/45] chore: remove manual call to OnNonceChange after evm.Call --- x/evm/keeper/state_transition.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 7f0850a079..977fe4bb00 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -426,12 +426,6 @@ func (k *Keeper) ApplyMessageWithConfig( stateDB.SetNonce(sender.Address(), msg.Nonce+1) } else { ret, leftoverGas, vmErr = evm.Call(sender, *msg.To, msg.Data, leftoverGas, uint256.MustFromBig(msg.Value)) - - // if they do not want to make the nonce set to the statedb or do we want to manually call the onNonceChange hook - //stateDB.SetNonce(sender.Address(), msg.Nonce+1) - if cfg.Tracer != nil && cfg.Tracer.OnNonceChange != nil { - cfg.Tracer.OnNonceChange(sender.Address(), msg.Nonce, msg.Nonce+1) - } } refundQuotient := params.RefundQuotient