Skip to content

Commit

Permalink
Merge pull request #118 from blocknative/TS_nbc
Browse files Browse the repository at this point in the history
Trace cache and perf improvements
  • Loading branch information
tyler-smith authored Sep 20, 2023
2 parents 9366744 + 8060e52 commit 2b8dc12
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 5 deletions.
54 changes: 51 additions & 3 deletions eth/filters/trace_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import (
"encoding/json"
"errors"
"fmt"
"math/big"
"sync"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
Expand All @@ -14,10 +18,10 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/blocknative"
"github.com/ethereum/go-ethereum/eth/tracers/blocknative/cache"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"math/big"
)

var defaultTxTraceOpts = blocknative.TracerOpts{
Expand Down Expand Up @@ -227,21 +231,65 @@ func (api *FilterAPI) NewFullBlocksWithTrace(ctx context.Context, tracerOptsJSON
return rpcSub, nil
}

var (
txTraceLocksMu sync.RWMutex
txTraceLocks = make(map[common.Hash]chan struct{})
txTraceLocksTimeout = 1 * time.Second
)

// traceTx traces a transaction with the given contexts.
func traceTx(message *core.Message, txCtx *tracers.Context, vmctx vm.BlockContext, chainConfig *params.ChainConfig, statedb *state.StateDB, tracerOpts blocknative.TracerOpts) (*blocknative.Trace, error) {
// Check cached trace or an in-progress trace before executing.
trace, ok := cache.GetTrace(txCtx.TxHash, txCtx.BlockHash)
if ok {
return trace, nil
}

// Check if there is an in-progress trace for this transaction.
// If there is then wait for it to finish and return it.
// If there is not then lock the trace and create a new one.
// Don't wait longer than a second for the cache to finish.
txTraceLocksMu.Lock()
if unlockCh, ok := txTraceLocks[txCtx.TxHash]; ok {
timeOut := time.NewTimer(txTraceLocksTimeout)
select {
case <-unlockCh:
if trace, ok := cache.GetTrace(txCtx.TxHash, txCtx.BlockHash); ok {
txTraceLocksMu.Unlock()
return trace, nil
}
case <-timeOut.C:
}
}
unlockCh := make(chan struct{})
txTraceLocks[txCtx.TxHash] = unlockCh
txTraceLocksMu.Unlock()

// No trace in cache or in-progress so create a new one.
tracer, err := blocknative.NewTxnOpCodeTracerWithOpts(tracerOpts)
if err != nil {
return nil, err
}

txContext := core.NewEVMTxContext(message)
vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{Tracer: tracer})
statedb.SetTxContext(txCtx.TxHash, txCtx.TxIndex)

if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit)); err != nil {
return nil, fmt.Errorf("tracing failed: %w", err)
}
return tracer.GetTrace()
trace, err = tracer.GetTrace()
if err != nil {
return nil, err
}

// Cache the result and unlock/delete the trace lock.
cache.PutTrace(txCtx.TxHash, txCtx.BlockHash, trace)
close(unlockCh)
txTraceLocksMu.Lock()
delete(txTraceLocks, txCtx.TxHash)
txTraceLocksMu.Unlock()

return trace, err
}

// traceBlock traces all transactions in a block.
Expand Down
27 changes: 27 additions & 0 deletions eth/tracers/blocknative/cache/trace_cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package cache

import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/eth/tracers/blocknative"
)

var (
traceCacheSize = 10_000
traceCache = lru.NewCache[[64]byte, *blocknative.Trace](traceCacheSize)
)

func PutTrace(blockHash, txHash common.Hash, trace *blocknative.Trace) bool {
return traceCache.Add(traceCacheKey(blockHash, txHash), trace)
}

func GetTrace(blockHash, txHash common.Hash) (*blocknative.Trace, bool) {
return traceCache.Get(traceCacheKey(blockHash, txHash))
}

func traceCacheKey(blockHash, txHash common.Hash) [64]byte {
key := [64]byte{}
copy(key[:32], blockHash[:])
copy(key[32:], txHash[:])
return key
}
7 changes: 5 additions & 2 deletions eth/tracers/blocknative/utils.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package blocknative

import (
"encoding/hex"
"math/big"
"strconv"
"strings"

"github.com/ethereum/go-ethereum/common"
)
Expand All @@ -24,5 +24,8 @@ func uintToHex(n uint64) string {
}

func addrToHex(a common.Address) string {
return strings.ToLower(a.Hex())
var buf [len(a)*2 + 2]byte
copy(buf[:2], "0x")
hex.Encode(buf[2:], a[:])
return string(buf[:])
}

0 comments on commit 2b8dc12

Please sign in to comment.