mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
feat: add pgeth-monitoring plugin
Signed-off-by: Isma <71719097+Doozers@users.noreply.github.com>
This commit is contained in:
parent
a86ba8fc0d
commit
9eef30aa38
7 changed files with 992 additions and 0 deletions
1
plugins/pgeth-monitoring/.gitignore
vendored
Normal file
1
plugins/pgeth-monitoring/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
plugin.so
|
||||||
37
plugins/pgeth-monitoring/README.md
Normal file
37
plugins/pgeth-monitoring/README.md
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# pgeth-monitoring plugin
|
||||||
|
|
||||||
|
Runs custom tracers while simulating all transactions, encode and feed everything to redis with topics making things easy to subscribe.
|
||||||
|
|
||||||
|
```
|
||||||
|
/head/tx/0xb0ba6c81c185bf7652f9339fdd86f35e47aea38a1215aa107f97d26ca5806c62/0x68c4D9E03D7D902053C428Ca2D74b612Db7F583A/0x5954aB967Bc958940b7EB73ee84797Dc8a2AFbb9/C@0x5954aB967Bc958940b7EB73ee84797Dc8a2AFbb9_20a325d0[S@0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D_6352211e,C@0x4d224452801ACEd8B2F0aebE155379bb5D594381_a9059cbb]
|
||||||
|
```
|
||||||
|
|
||||||
|
The topics follow the following format
|
||||||
|
|
||||||
|
```
|
||||||
|
/CHANNEL/tx/TX_HASH/FROM/TO/CALL_TRACES
|
||||||
|
```
|
||||||
|
|
||||||
|
Where the example above call traces can be interpreted as:
|
||||||
|
|
||||||
|
```
|
||||||
|
C@0x5954aB967Bc958940b7EB73ee84797Dc8a2AFbb9_20a325d0[S@0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D_6352211e,C@0x4d224452801ACEd8B2F0aebE155379bb5D594381_a9059cbb]
|
||||||
|
|
||||||
|
C@0x5954aB967Bc958940b7EB73ee84797Dc8a2AFbb9_20a325d0[
|
||||||
|
S@0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D_6352211e,
|
||||||
|
C@0x4d224452801ACEd8B2F0aebE155379bb5D594381_a9059cbb
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
- Initial call (`C`) made to `0x5954aB967Bc958940b7EB73ee84797Dc8a2AFbb9` on function selector `20a325d0`
|
||||||
|
- Static call (`S`) made to `0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D` on function selector `6352211e`
|
||||||
|
- Call (`C`) made to `0x4d224452801ACEd8B2F0aebE155379bb5D594381` on function selector `a9059cbb`
|
||||||
|
|
||||||
|
|
||||||
|
The different call modes are
|
||||||
|
|
||||||
|
- `C`, a regular `call`
|
||||||
|
- `S`, a `staticcall`
|
||||||
|
- `D`, a `delegatecall`
|
||||||
|
|
||||||
|
The message payload will provide complete execution details with inputs, outputs, context and code address for every step
|
||||||
4
plugins/pgeth-monitoring/config.yaml
Normal file
4
plugins/pgeth-monitoring/config.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
name: "monitoring"
|
||||||
|
config:
|
||||||
|
REDIS_ENDPOINT: "localhost:6379"
|
||||||
|
BEACON_ENDPOINT: "localhost:5052"
|
||||||
7
plugins/pgeth-monitoring/dependencies.sh
Executable file
7
plugins/pgeth-monitoring/dependencies.sh
Executable file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
go get github.com/ethereum/go-ethereum/plugins/pgeth-monitoring
|
||||||
|
go get github.com/redis/go-redis/v9@v9.0.3
|
||||||
|
go get github.com/prometheus/client_golang@v1.12.0
|
||||||
|
go get github.com/attestantio/go-eth2-client/http@v0.16.0
|
||||||
|
go mod tidy
|
||||||
511
plugins/pgeth-monitoring/pgeth_monitoring.go
Normal file
511
plugins/pgeth-monitoring/pgeth_monitoring.go
Normal file
|
|
@ -0,0 +1,511 @@
|
||||||
|
package pgeth_monitoring
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
eth2client "github.com/attestantio/go-eth2-client"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
|
||||||
|
"github.com/attestantio/go-eth2-client/http"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/pgeth/toolkit"
|
||||||
|
"github.com/ethereum/go-ethereum/plugins/pgeth-monitoring/pkg/tracer"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Version() {
|
||||||
|
fmt.Println("pgeth-monitoring: v0.0.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// plugin.so entrypoint
|
||||||
|
func Start(pt *toolkit.PluginToolkit, cfg map[string]interface{}, ctx context.Context, errChan chan error) {
|
||||||
|
var redisEndpointRaw interface{}
|
||||||
|
var redisEndpoint string
|
||||||
|
var beaconEndpointRaw interface{}
|
||||||
|
var beaconEndpoint string
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if redisEndpointRaw, ok = cfg["REDIS_ENDPOINT"]; !ok {
|
||||||
|
pt.Logger.Error("missing REDIS_ENDPOINT config var")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if redisEndpoint, ok = redisEndpointRaw.(string); !ok {
|
||||||
|
pt.Logger.Error("invalid REDIS_ENDPOINT value")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if beaconEndpointRaw, ok = cfg["BEACON_ENDPOINT"]; !ok {
|
||||||
|
pt.Logger.Error("missing BEACON_ENDPOINT config var")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if beaconEndpoint, ok = beaconEndpointRaw.(string); !ok {
|
||||||
|
pt.Logger.Error("invalid BEACON_ENDPOINT value")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := http.New(ctx,
|
||||||
|
http.WithAddress(beaconEndpoint),
|
||||||
|
http.WithLogLevel(zerolog.WarnLevel),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
rdb := redis.NewClient(&redis.Options{
|
||||||
|
Addr: redisEndpoint,
|
||||||
|
Password: "",
|
||||||
|
DB: 0,
|
||||||
|
})
|
||||||
|
me := NewMonitoringEngine(pt, rdb, client, beaconEndpoint, errChan)
|
||||||
|
|
||||||
|
me.Start(ctx)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MonitoringEngine is the main plugin struct
|
||||||
|
// It contains the plugin toolkit, the redis client, and the eth2 client
|
||||||
|
type MonitoringEngine struct {
|
||||||
|
ptk *toolkit.PluginToolkit
|
||||||
|
|
||||||
|
backend *eth.EthAPIBackend
|
||||||
|
chainConfig *params.ChainConfig
|
||||||
|
coinbase common.Address
|
||||||
|
state *state.StateDB
|
||||||
|
header *types.Header
|
||||||
|
|
||||||
|
latestBlock *types.Block
|
||||||
|
|
||||||
|
beaconEndpoint string
|
||||||
|
|
||||||
|
errChan chan error
|
||||||
|
|
||||||
|
rdb *redis.Client
|
||||||
|
eth2 eth2client.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnalyzedTransaction is a transaction with its traces
|
||||||
|
// It is used to encode the transaction and its traces in a redis key
|
||||||
|
// Then the entire struct is sent to redis
|
||||||
|
type AnalyzedTransaction struct {
|
||||||
|
Transaction *types.Transaction `json:"transaction"`
|
||||||
|
From common.Address `json:"from"`
|
||||||
|
Receipt *types.Receipt `json:"receipt"`
|
||||||
|
Traces tracer.Action `json:"traces"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CachedBlockSimulation is a block simulation that is cached in memory
|
||||||
|
// It is used to avoid simulating the same block twice for finalized blocks
|
||||||
|
// Two hours after the block is cached, it is removed from memory
|
||||||
|
// When a cache block is found as finalized, it is removed from memory
|
||||||
|
type CachedBlockSimulation struct {
|
||||||
|
Time time.Time
|
||||||
|
AnalyzedTransactions []AnalyzedTransaction
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMonitoringEngine(pt *toolkit.PluginToolkit, rdb *redis.Client, eth2 eth2client.Service, beaconEndpoint string, errChan chan error) *MonitoringEngine {
|
||||||
|
|
||||||
|
return &MonitoringEngine{
|
||||||
|
ptk: pt,
|
||||||
|
backend: pt.Backend.(*eth.EthAPIBackend),
|
||||||
|
chainConfig: pt.Backend.ChainConfig(),
|
||||||
|
beaconEndpoint: beaconEndpoint,
|
||||||
|
errChan: errChan,
|
||||||
|
rdb: rdb,
|
||||||
|
eth2: eth2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (me *MonitoringEngine) Start(ctx context.Context) {
|
||||||
|
me.startHeadListener(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (me *MonitoringEngine) update(ctx context.Context, parent *types.Block) {
|
||||||
|
|
||||||
|
state, _, err := me.backend.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHashWithHash(parent.Hash(), true))
|
||||||
|
if err != nil {
|
||||||
|
me.errChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
me.header = &types.Header{
|
||||||
|
ParentHash: parent.Hash(),
|
||||||
|
Number: new(big.Int).Add(parent.Number(), common.Big1),
|
||||||
|
GasLimit: parent.GasLimit(),
|
||||||
|
Time: parent.Time() + 12,
|
||||||
|
Coinbase: parent.Coinbase(),
|
||||||
|
BaseFee: eip1559.CalcBaseFee(me.chainConfig, parent.Header()),
|
||||||
|
Difficulty: parent.Difficulty(),
|
||||||
|
}
|
||||||
|
me.coinbase = parent.Coinbase()
|
||||||
|
me.state = state
|
||||||
|
}
|
||||||
|
|
||||||
|
func (me *MonitoringEngine) encodeAndBroadcastCallTrace(ctx context.Context, at *AnalyzedTransaction, channel string) {
|
||||||
|
var topic string
|
||||||
|
if at.Transaction.To() == nil {
|
||||||
|
topic = fmt.Sprintf("/%s/tx/%s/%s/null/%s", channel, at.Transaction.Hash(), at.From, encodeActionCalls(at.Traces))
|
||||||
|
} else {
|
||||||
|
topic = fmt.Sprintf("/%s/tx/%s/%s/%s/%s", channel, at.Transaction.Hash(), at.From, at.Transaction.To(), encodeActionCalls(at.Traces))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
jsoned, err := json.Marshal(*at)
|
||||||
|
if err != nil {
|
||||||
|
me.errChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = me.rdb.Publish(ctx, topic, jsoned).Err()
|
||||||
|
if err != nil {
|
||||||
|
me.errChan <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = me.rdb.Expire(ctx, topic, 1*time.Hour).Err()
|
||||||
|
if err != nil {
|
||||||
|
me.errChan <- err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (me *MonitoringEngine) encodeAndBroadcast(ctx context.Context, ats []AnalyzedTransaction, channel string) {
|
||||||
|
for _, analyzedTx := range ats {
|
||||||
|
me.encodeAndBroadcastCallTrace(ctx, &analyzedTx, channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// analyze is the main function of the plugin
|
||||||
|
// It takes a block and returns a list of AnalyzedTransaction
|
||||||
|
// It simulates the block and gets the traces of all the transactions
|
||||||
|
func (me *MonitoringEngine) analyze(ctx context.Context, block *types.Block, scope string) []AnalyzedTransaction {
|
||||||
|
// We retrieve the parent block
|
||||||
|
parentBlk, err := me.backend.BlockByHash(ctx, block.ParentHash())
|
||||||
|
if err != nil {
|
||||||
|
me.errChan <- err
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// We retrieve the state of the parent block
|
||||||
|
state, _, err := me.backend.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHashWithHash(parentBlk.Hash(), true))
|
||||||
|
if err != nil {
|
||||||
|
me.errChan <- err
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// We configure the vm to use our monitoring tracer
|
||||||
|
gp := new(core.GasPool).AddGas(block.Header().GasLimit)
|
||||||
|
mt := tracer.MonitoringTracer{}
|
||||||
|
var vmConfig vm.Config = vm.Config{
|
||||||
|
Tracer: &mt,
|
||||||
|
NoBaseFee: false,
|
||||||
|
EnablePreimageRecording: false,
|
||||||
|
ExtraEips: []int{},
|
||||||
|
}
|
||||||
|
analyzedTransactions := []AnalyzedTransaction{}
|
||||||
|
// We simulate all the transactions of the block
|
||||||
|
for idx, tx := range block.Transactions() {
|
||||||
|
state.SetTxContext(tx.Hash(), idx)
|
||||||
|
receipt, err := core.ApplyTransaction(me.chainConfig, me.backend.Ethereum().BlockChain(), &block.Header().Coinbase, gp, state, block.Header(), tx, &block.Header().GasUsed, vmConfig)
|
||||||
|
if err != nil {
|
||||||
|
me.errChan <- err
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
receipt.EffectiveGasPrice = getEffectiveGasPrice(tx, parentBlk.BaseFee())
|
||||||
|
signer := types.MakeSigner(me.backend.Ethereum().BlockChain().Config(), receipt.BlockNumber, block.Time())
|
||||||
|
from, _ := types.Sender(signer, tx)
|
||||||
|
analyzedTransactions = append(analyzedTransactions, AnalyzedTransaction{
|
||||||
|
Transaction: tx,
|
||||||
|
From: from,
|
||||||
|
Receipt: receipt,
|
||||||
|
Traces: mt.Action,
|
||||||
|
})
|
||||||
|
mt.Clear()
|
||||||
|
}
|
||||||
|
me.ptk.Logger.Info("Simulated txs", "count", len(block.Transactions()), "scope", scope, "number", block.Number())
|
||||||
|
// We encode and broadcast the traces
|
||||||
|
me.encodeAndBroadcast(ctx, analyzedTransactions, scope)
|
||||||
|
me.ptk.Logger.Info("Broadcasted txs", "count", len(block.Transactions()), "scope", scope, "number", block.Number())
|
||||||
|
me.latestBlock = block
|
||||||
|
return analyzedTransactions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (me *MonitoringEngine) analyzePending(ctx context.Context, txs []*types.Transaction) {
|
||||||
|
gp := new(core.GasPool).AddGas(me.header.GasLimit)
|
||||||
|
mt := tracer.MonitoringTracer{}
|
||||||
|
var vmConfig vm.Config = vm.Config{
|
||||||
|
Tracer: &mt,
|
||||||
|
NoBaseFee: false,
|
||||||
|
EnablePreimageRecording: false,
|
||||||
|
ExtraEips: []int{},
|
||||||
|
}
|
||||||
|
analyzedTransactions := []AnalyzedTransaction{}
|
||||||
|
for _, tx := range txs {
|
||||||
|
// we copy the state at the head
|
||||||
|
stateClone := me.state.Copy()
|
||||||
|
stateClone.SetTxContext(tx.Hash(), 0)
|
||||||
|
// we simulate the pending tx on top of it
|
||||||
|
receipt, err := core.ApplyTransaction(me.chainConfig, me.backend.Ethereum().BlockChain(), &me.header.Coinbase, gp, stateClone, me.header, tx, &me.header.GasUsed, vmConfig)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
receipt.EffectiveGasPrice = getEffectiveGasPrice(tx, me.header.BaseFee)
|
||||||
|
signer := types.MakeSigner(me.backend.Ethereum().BlockChain().Config(), receipt.BlockNumber, me.header.Time)
|
||||||
|
from, _ := types.Sender(signer, tx)
|
||||||
|
analyzedTransactions = append(analyzedTransactions, AnalyzedTransaction{
|
||||||
|
Transaction: tx,
|
||||||
|
From: from,
|
||||||
|
Receipt: receipt,
|
||||||
|
Traces: mt.Action,
|
||||||
|
})
|
||||||
|
mt.Clear()
|
||||||
|
}
|
||||||
|
if len(analyzedTransactions) > 0 {
|
||||||
|
// we encode and broadcast the traces under the pending topic
|
||||||
|
me.encodeAndBroadcast(ctx, analyzedTransactions, "pending")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (me *MonitoringEngine) startHeadListener(ctx context.Context) {
|
||||||
|
|
||||||
|
headChan := make(chan core.ChainHeadEvent)
|
||||||
|
headSubscription := me.backend.SubscribeChainHeadEvent(headChan)
|
||||||
|
pendingChan := make(chan core.NewTxsEvent)
|
||||||
|
pendingSubscription := me.backend.SubscribeNewTxsEvent(pendingChan)
|
||||||
|
ticker := time.NewTicker(30 * time.Second)
|
||||||
|
cache := make(map[common.Hash]CachedBlockSimulation)
|
||||||
|
analyzedPendingTxs := 0
|
||||||
|
|
||||||
|
highestFinalized := uint64(0)
|
||||||
|
|
||||||
|
defer headSubscription.Unsubscribe()
|
||||||
|
defer pendingSubscription.Unsubscribe()
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case err := <-headSubscription.Err():
|
||||||
|
if err != nil {
|
||||||
|
me.errChan <- err
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case err := <-pendingSubscription.Err():
|
||||||
|
if err != nil {
|
||||||
|
me.errChan <- err
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case newHead := <-headChan:
|
||||||
|
me.update(ctx, newHead.Block)
|
||||||
|
me.ptk.Logger.Info("Head was updated", "number", newHead.Block.NumberU64(), "hash", newHead.Block.Hash(), "root", newHead.Block.Root())
|
||||||
|
|
||||||
|
analyzedTransactions := me.analyze(ctx, newHead.Block, "head")
|
||||||
|
cache[newHead.Block.Hash()] = CachedBlockSimulation{
|
||||||
|
Time: time.Now(),
|
||||||
|
AnalyzedTransactions: analyzedTransactions,
|
||||||
|
}
|
||||||
|
case newTxs := <-pendingChan:
|
||||||
|
if me.header == nil {
|
||||||
|
me.ptk.Logger.Warn("Skipping pending tx simulation, not ready")
|
||||||
|
} else {
|
||||||
|
me.analyzePending(ctx, newTxs.Txs)
|
||||||
|
analyzedPendingTxs += len(newTxs.Txs)
|
||||||
|
}
|
||||||
|
case <-ticker.C:
|
||||||
|
if analyzedPendingTxs > 0 {
|
||||||
|
me.ptk.Logger.Info("Analyzed pending txs", "count", analyzedPendingTxs, "rate", fmt.Sprintf("%f/s", float64(analyzedPendingTxs)/float64(30)))
|
||||||
|
analyzedPendingTxs = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if me.eth2 == nil {
|
||||||
|
client, err := http.New(ctx,
|
||||||
|
http.WithAddress(me.beaconEndpoint),
|
||||||
|
http.WithLogLevel(zerolog.WarnLevel),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
me.errChan <- err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
me.eth2 = client
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := me.eth2.(eth2client.SignedBeaconBlockProvider).SignedBeaconBlock(ctx, "finalized")
|
||||||
|
if err != nil {
|
||||||
|
me.errChan <- err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
newHighestFinalized := res.Capella.Message.Body.ExecutionPayload.BlockNumber
|
||||||
|
if highestFinalized == 0 {
|
||||||
|
highestFinalized = res.Capella.Message.Body.ExecutionPayload.BlockNumber
|
||||||
|
me.ptk.Logger.Info("Finalized head was updated", "number", highestFinalized)
|
||||||
|
} else if newHighestFinalized > highestFinalized {
|
||||||
|
me.ptk.Logger.Info("Updating finalized head", "from", highestFinalized, "to", newHighestFinalized)
|
||||||
|
breaked := false
|
||||||
|
for i := highestFinalized + 1; i <= newHighestFinalized; i++ {
|
||||||
|
blk, err := me.backend.BlockByNumber(ctx, rpc.BlockNumber(i))
|
||||||
|
if err != nil {
|
||||||
|
me.errChan <- err
|
||||||
|
breaked = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if blk == nil {
|
||||||
|
breaked = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if cachedValue, ok := cache[blk.Hash()]; ok {
|
||||||
|
me.encodeAndBroadcast(ctx, cachedValue.AnalyzedTransactions, "finalized")
|
||||||
|
me.ptk.Logger.Info("Broadcasted txs", "count", len(cachedValue.AnalyzedTransactions), "scope", "finalized", "number", i)
|
||||||
|
delete(cache, blk.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if breaked {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
me.ptk.Logger.Info("Finalized head was updated", "number", newHighestFinalized)
|
||||||
|
highestFinalized = newHighestFinalized
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range cache {
|
||||||
|
if time.Since(v.Time) > 2*time.Hour {
|
||||||
|
delete(cache, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(cache) > 0 {
|
||||||
|
me.ptk.Logger.Info("Cached blocks", "count", len(cache))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEffectiveGasPrice(tx *types.Transaction, baseFee *big.Int) *big.Int {
|
||||||
|
switch tx.Type() {
|
||||||
|
case types.DynamicFeeTxType:
|
||||||
|
if baseFee == nil {
|
||||||
|
return tx.GasFeeCap()
|
||||||
|
}
|
||||||
|
tip := new(big.Int).Sub(tx.GasFeeCap(), baseFee)
|
||||||
|
if tip.Cmp(tx.GasTipCap()) > 0 {
|
||||||
|
tip.Set(tx.GasTipCap())
|
||||||
|
}
|
||||||
|
return tip.Add(tip, baseFee)
|
||||||
|
case types.AccessListTxType:
|
||||||
|
return tx.GasPrice()
|
||||||
|
case types.LegacyTxType:
|
||||||
|
return tx.GasPrice()
|
||||||
|
default:
|
||||||
|
return big.NewInt(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func callTypeToPrefix(c *tracer.Call) string {
|
||||||
|
switch c.Type() {
|
||||||
|
case "call":
|
||||||
|
return "C"
|
||||||
|
case "staticcall":
|
||||||
|
return "S"
|
||||||
|
case "delegatecall":
|
||||||
|
return "D"
|
||||||
|
case "initial_call":
|
||||||
|
return "C"
|
||||||
|
}
|
||||||
|
return "X"
|
||||||
|
}
|
||||||
|
|
||||||
|
func eventTypeToPrefix(e *tracer.Event) string {
|
||||||
|
switch e.LogType {
|
||||||
|
case "log0":
|
||||||
|
fallthrough
|
||||||
|
case "log1":
|
||||||
|
fallthrough
|
||||||
|
case "log2":
|
||||||
|
fallthrough
|
||||||
|
case "log3":
|
||||||
|
fallthrough
|
||||||
|
case "log4":
|
||||||
|
return "L"
|
||||||
|
}
|
||||||
|
return "X"
|
||||||
|
}
|
||||||
|
|
||||||
|
func revertTypeToPrefix(r *tracer.Revert) string {
|
||||||
|
switch r.ErrorType {
|
||||||
|
case "revert":
|
||||||
|
return "R"
|
||||||
|
case "panic":
|
||||||
|
return "P"
|
||||||
|
}
|
||||||
|
return "X"
|
||||||
|
}
|
||||||
|
|
||||||
|
func minInt(a int, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeSelector(c *tracer.Call) string {
|
||||||
|
selector := fmt.Sprintf("%x", c.In[0:minInt(4, len(c.In))])
|
||||||
|
for len(selector) < 8 {
|
||||||
|
selector += "X"
|
||||||
|
}
|
||||||
|
return selector
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeRevertSelector(c *tracer.Revert) string {
|
||||||
|
selector := fmt.Sprintf("%x", c.Data[0:minInt(4, len(c.Data))])
|
||||||
|
for len(selector) < 8 {
|
||||||
|
selector += "X"
|
||||||
|
}
|
||||||
|
return selector
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeActionCalls(a tracer.Action) string {
|
||||||
|
res := ""
|
||||||
|
if c, ok := a.(*tracer.Call); ok {
|
||||||
|
prefix := callTypeToPrefix(c)
|
||||||
|
selector := encodeSelector(c)
|
||||||
|
res = fmt.Sprintf("%s@%s_%s", prefix, c.To.String(), selector)
|
||||||
|
if len(a.Children()) > 0 {
|
||||||
|
chldArr := []string{}
|
||||||
|
for _, chld := range a.Children() {
|
||||||
|
chldRes := encodeActionCalls(chld)
|
||||||
|
if len(chldRes) > 0 {
|
||||||
|
chldArr = append(chldArr, chldRes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(chldArr) > 0 {
|
||||||
|
joinedChldRes := strings.Join(chldArr[:], ",")
|
||||||
|
res = fmt.Sprintf("%s[%s]", res, joinedChldRes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if e, ok := a.(*tracer.Event); ok {
|
||||||
|
concatenatedTopics := ""
|
||||||
|
for _, topic := range e.Topics {
|
||||||
|
concatenatedTopics += topic.String()[2:]
|
||||||
|
}
|
||||||
|
dataLength := len(e.Data)
|
||||||
|
res = fmt.Sprintf("%s@%s_%s_%d", eventTypeToPrefix(e), e.ContextValue.String(), concatenatedTopics, dataLength)
|
||||||
|
}
|
||||||
|
if r, ok := a.(*tracer.Revert); ok {
|
||||||
|
prefix := revertTypeToPrefix(r)
|
||||||
|
selector := encodeRevertSelector(r)
|
||||||
|
res = fmt.Sprintf("%s@%s_%s", prefix, r.ContextValue.String(), selector)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
191
plugins/pgeth-monitoring/pkg/tracer/action.go
Normal file
191
plugins/pgeth-monitoring/pkg/tracer/action.go
Normal file
|
|
@ -0,0 +1,191 @@
|
||||||
|
package tracer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Action interface {
|
||||||
|
Type() string
|
||||||
|
Children() []Action
|
||||||
|
Parent() Action
|
||||||
|
Depth() int
|
||||||
|
Log()
|
||||||
|
Has(string) bool
|
||||||
|
Context() common.Address
|
||||||
|
Code() common.Address
|
||||||
|
|
||||||
|
AddChildren(Action)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Call struct {
|
||||||
|
ParentValue Action `json:"-"`
|
||||||
|
DepthValue int `json:"depth,omitempty"`
|
||||||
|
|
||||||
|
TypeValue string `json:"type,omitempty"`
|
||||||
|
CallType string `json:"callType,omitempty"`
|
||||||
|
ChildrenValue []Action `json:"children,omitempty"`
|
||||||
|
|
||||||
|
ContextValue common.Address `json:"context,omitempty"`
|
||||||
|
CodeValue common.Address `json:"code,omitempty"`
|
||||||
|
ForwardedContext common.Address `json:"forwardedContext,omitempty"`
|
||||||
|
ForwardedCode common.Address `json:"forwardedCode,omitempty"`
|
||||||
|
|
||||||
|
From common.Address `json:"from,omitempty"`
|
||||||
|
To common.Address `json:"to,omitempty"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
In []byte `json:"-"`
|
||||||
|
Out []byte `json:"-"`
|
||||||
|
InHex string `json:"in,omitempty"`
|
||||||
|
OutHex string `json:"out,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Call) Type() string {
|
||||||
|
return c.CallType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Call) Children() []Action {
|
||||||
|
return c.ChildrenValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Call) Context() common.Address {
|
||||||
|
return c.ContextValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Call) Code() common.Address {
|
||||||
|
return c.CodeValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Call) Depth() int {
|
||||||
|
return c.DepthValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Call) Parent() Action {
|
||||||
|
return c.ParentValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Call) AddChildren(a Action) {
|
||||||
|
c.ChildrenValue = append(c.ChildrenValue, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Call) Log() {
|
||||||
|
fmt.Printf("%s- %s %s to %s (%s:%s) (%d,%d) (%d)\n", strings.Repeat(" ", c.DepthValue), c.Type(), c.From.String(), c.To.String(), c.Context().String(), c.Code().String(), len(c.In), len(c.Out), len(c.ChildrenValue))
|
||||||
|
for _, subcall := range c.ChildrenValue {
|
||||||
|
subcall.Log()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Call) Has(typ string) bool {
|
||||||
|
if c.Type() == typ {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, chld := range c.ChildrenValue {
|
||||||
|
if chld.Has(typ) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
ParentValue Action `json:"-"`
|
||||||
|
DepthValue int `json:"depth,omitempty"`
|
||||||
|
|
||||||
|
TypeValue string `json:"type,omitempty"`
|
||||||
|
LogType string `json:"logType,omitempty"`
|
||||||
|
|
||||||
|
ContextValue common.Address `json:"context,omitempty"`
|
||||||
|
CodeValue common.Address `json:"code,omitempty"`
|
||||||
|
|
||||||
|
Data []byte `json:"-"`
|
||||||
|
DataHex string `json:"data,omitempty"`
|
||||||
|
Topics []common.Hash `json:"topics,omitempty"`
|
||||||
|
From common.Address `json:"from,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Event) Type() string {
|
||||||
|
return c.LogType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Event) Children() []Action {
|
||||||
|
return []Action{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Event) Context() common.Address {
|
||||||
|
return c.ContextValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Event) Code() common.Address {
|
||||||
|
return c.CodeValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Event) Depth() int {
|
||||||
|
return c.DepthValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Event) Parent() Action {
|
||||||
|
return c.ParentValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Event) AddChildren(a Action) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Event) Log() {
|
||||||
|
fmt.Printf("%s- %s (%s:%s) \n", strings.Repeat(" ", c.DepthValue), c.Type(), c.Context().String(), c.Code().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Event) Has(typ string) bool {
|
||||||
|
return c.Type() == typ
|
||||||
|
}
|
||||||
|
|
||||||
|
type Revert struct {
|
||||||
|
ParentValue Action `json:"-"`
|
||||||
|
DepthValue int `json:"depth,omitempty"`
|
||||||
|
|
||||||
|
TypeValue string `json:"type,omitempty"`
|
||||||
|
ErrorType string `json:"errorType,omitempty"`
|
||||||
|
|
||||||
|
ContextValue common.Address `json:"context,omitempty"`
|
||||||
|
CodeValue common.Address `json:"code,omitempty"`
|
||||||
|
|
||||||
|
Data []byte `json:"-"`
|
||||||
|
DataHex string `json:"data,omitempty"`
|
||||||
|
From common.Address `json:"from,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Revert) Type() string {
|
||||||
|
return r.ErrorType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Revert) Children() []Action {
|
||||||
|
return []Action{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Revert) Context() common.Address {
|
||||||
|
return r.ContextValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Revert) Code() common.Address {
|
||||||
|
return r.CodeValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Revert) Depth() int {
|
||||||
|
return r.DepthValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Revert) Parent() Action {
|
||||||
|
return r.ParentValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Revert) AddChildren(a Action) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Revert) Log() {
|
||||||
|
fmt.Printf("%s- %s (%s:%s) %x\n", strings.Repeat(" ", r.DepthValue), r.Type(), r.Context().String(), r.Code().String(), r.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Revert) Has(typ string) bool {
|
||||||
|
return r.Type() == typ
|
||||||
|
}
|
||||||
241
plugins/pgeth-monitoring/pkg/tracer/tracer.go
Normal file
241
plugins/pgeth-monitoring/pkg/tracer/tracer.go
Normal file
|
|
@ -0,0 +1,241 @@
|
||||||
|
package tracer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MonitoringTracer struct {
|
||||||
|
Action Action
|
||||||
|
Cursor Action
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonitoringTracer) Clear() {
|
||||||
|
m.Action = nil
|
||||||
|
m.Cursor = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonitoringTracer) CaptureTxStart(gasLimit uint64) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonitoringTracer) CaptureTxEnd(restGas uint64) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonitoringTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||||
|
copyInput := make([]byte, len(input))
|
||||||
|
usedValue := big.NewInt(0)
|
||||||
|
if value != nil {
|
||||||
|
usedValue.Set(value)
|
||||||
|
}
|
||||||
|
copy(copyInput, input)
|
||||||
|
m.Action = &Call{
|
||||||
|
CallType: "initial_call",
|
||||||
|
TypeValue: "call",
|
||||||
|
ChildrenValue: []Action{},
|
||||||
|
ParentValue: nil,
|
||||||
|
DepthValue: 0,
|
||||||
|
|
||||||
|
ContextValue: common.Address{},
|
||||||
|
CodeValue: common.Address{},
|
||||||
|
|
||||||
|
ForwardedContext: to,
|
||||||
|
ForwardedCode: to,
|
||||||
|
|
||||||
|
From: from,
|
||||||
|
To: to,
|
||||||
|
In: copyInput,
|
||||||
|
InHex: "0x" + hex.EncodeToString(copyInput),
|
||||||
|
Value: "0x" + usedValue.Text(16),
|
||||||
|
}
|
||||||
|
m.Cursor = m.Action
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonitoringTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||||
|
copyOutput := make([]byte, len(output))
|
||||||
|
copy(copyOutput, output)
|
||||||
|
m.Cursor.(*Call).Out = copyOutput
|
||||||
|
m.Cursor.(*Call).OutHex = "0x" + hex.EncodeToString(copyOutput)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonitoringTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
|
callType := callOpcodeToString(typ)
|
||||||
|
ctx, code := parentContextAndCode(m.Cursor)
|
||||||
|
forwardedCode := to
|
||||||
|
forwardedContext := to
|
||||||
|
if callType == "delegatecall" {
|
||||||
|
forwardedContext = from
|
||||||
|
}
|
||||||
|
usedValue := big.NewInt(0)
|
||||||
|
if value != nil {
|
||||||
|
usedValue.Set(value)
|
||||||
|
}
|
||||||
|
copyInput := make([]byte, len(input))
|
||||||
|
copy(copyInput, input)
|
||||||
|
call := &Call{
|
||||||
|
CallType: callType,
|
||||||
|
TypeValue: "call",
|
||||||
|
ChildrenValue: []Action{},
|
||||||
|
ParentValue: m.Cursor,
|
||||||
|
DepthValue: m.Cursor.Depth() + 1,
|
||||||
|
|
||||||
|
ForwardedContext: forwardedContext,
|
||||||
|
ForwardedCode: forwardedCode,
|
||||||
|
ContextValue: ctx,
|
||||||
|
CodeValue: code,
|
||||||
|
From: from,
|
||||||
|
To: to,
|
||||||
|
In: copyInput,
|
||||||
|
InHex: "0x" + hex.EncodeToString(copyInput),
|
||||||
|
Value: "0x" + usedValue.Text(16),
|
||||||
|
}
|
||||||
|
m.Cursor.AddChildren(call)
|
||||||
|
m.Cursor = call
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonitoringTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
||||||
|
copyOutput := make([]byte, len(output))
|
||||||
|
copy(copyOutput, output)
|
||||||
|
m.Cursor.(*Call).Out = copyOutput
|
||||||
|
m.Cursor.(*Call).OutHex = "0x" + hex.EncodeToString(copyOutput)
|
||||||
|
m.Cursor = m.Cursor.Parent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonitoringTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||||
|
if op >= 160 && op <= 164 {
|
||||||
|
stack := scope.Stack.Data()
|
||||||
|
stackLen := len(stack)
|
||||||
|
var offset int64 = 0
|
||||||
|
var size int64 = 0
|
||||||
|
if stackLen >= 2 {
|
||||||
|
offset = stack[stackLen-1].ToBig().Int64()
|
||||||
|
size = stack[stackLen-2].ToBig().Int64()
|
||||||
|
}
|
||||||
|
fetchSize := size
|
||||||
|
var data []byte = []byte{}
|
||||||
|
if int64(scope.Memory.Len()) < offset {
|
||||||
|
fetchSize = 0
|
||||||
|
// generate zero array
|
||||||
|
} else if int64(scope.Memory.Len()) < offset+size {
|
||||||
|
fetchSize -= (offset + size) - int64(scope.Memory.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
if fetchSize > 0 {
|
||||||
|
data = scope.Memory.GetCopy(offset, fetchSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fetchSize < size {
|
||||||
|
data = addZeros(data, size-fetchSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
topics := []common.Hash{}
|
||||||
|
for idx := 0; idx < int(op-160); idx++ {
|
||||||
|
if stackLen-3-idx >= 0 {
|
||||||
|
topics = append(topics, stack[stackLen-3-idx].Bytes32())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, code := parentContextAndCode(m.Cursor)
|
||||||
|
|
||||||
|
m.Cursor.AddChildren(&Event{
|
||||||
|
LogType: fmt.Sprintf("log%d", op-160),
|
||||||
|
TypeValue: "event",
|
||||||
|
Data: data,
|
||||||
|
DataHex: "0x" + hex.EncodeToString(data),
|
||||||
|
Topics: topics,
|
||||||
|
From: scope.Contract.Address(),
|
||||||
|
|
||||||
|
ContextValue: ctx,
|
||||||
|
CodeValue: code,
|
||||||
|
ParentValue: m.Cursor,
|
||||||
|
DepthValue: m.Cursor.Depth() + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if op == 253 {
|
||||||
|
errorType := "revert"
|
||||||
|
data := []byte{}
|
||||||
|
stack := scope.Stack.Data()
|
||||||
|
stackLen := len(stack)
|
||||||
|
var offset int64 = 0
|
||||||
|
var size int64 = 0
|
||||||
|
if stackLen >= 2 {
|
||||||
|
offset = stack[stackLen-1].ToBig().Int64()
|
||||||
|
size = stack[stackLen-2].ToBig().Int64()
|
||||||
|
}
|
||||||
|
fetchSize := size
|
||||||
|
if int64(scope.Memory.Len()) < offset {
|
||||||
|
fetchSize = 0
|
||||||
|
// generate zero array
|
||||||
|
} else if int64(scope.Memory.Len()) < offset+size {
|
||||||
|
fetchSize -= (offset + size) - int64(scope.Memory.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
if fetchSize > 0 {
|
||||||
|
data = scope.Memory.GetCopy(offset, fetchSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fetchSize < size {
|
||||||
|
data = addZeros(data, size-fetchSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, code := parentContextAndCode(m.Cursor)
|
||||||
|
|
||||||
|
m.Cursor.AddChildren(&Revert{
|
||||||
|
ErrorType: errorType,
|
||||||
|
TypeValue: "revert",
|
||||||
|
Data: data,
|
||||||
|
DataHex: "0x" + hex.EncodeToString(data),
|
||||||
|
From: scope.Contract.Address(),
|
||||||
|
ContextValue: ctx,
|
||||||
|
CodeValue: code,
|
||||||
|
ParentValue: m.Cursor,
|
||||||
|
DepthValue: m.Cursor.Depth() + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MonitoringTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
||||||
|
if op != 253 {
|
||||||
|
ctx, code := parentContextAndCode(m.Cursor)
|
||||||
|
m.Cursor.AddChildren(&Revert{
|
||||||
|
ErrorType: "panic",
|
||||||
|
TypeValue: "revert",
|
||||||
|
Data: []byte{},
|
||||||
|
DataHex: "0x" + hex.EncodeToString([]byte{}),
|
||||||
|
From: scope.Contract.Address(),
|
||||||
|
ContextValue: ctx,
|
||||||
|
CodeValue: code,
|
||||||
|
ParentValue: m.Cursor,
|
||||||
|
DepthValue: m.Cursor.Depth() + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func callOpcodeToString(c vm.OpCode) string {
|
||||||
|
switch c {
|
||||||
|
case 241:
|
||||||
|
return "call"
|
||||||
|
case 244:
|
||||||
|
return "delegatecall"
|
||||||
|
case 250:
|
||||||
|
return "staticcall"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("unknown %d", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parentContextAndCode(p Action) (common.Address, common.Address) {
|
||||||
|
if p != nil {
|
||||||
|
return p.(*Call).ForwardedContext, p.(*Call).ForwardedCode
|
||||||
|
}
|
||||||
|
return common.Address{}, common.Address{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addZeros(arr []byte, zeros int64) []byte {
|
||||||
|
return append(arr, make([]byte, zeros)...)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue