mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-24 12:37:16 +00:00
272 lines
8.2 KiB
Go
272 lines
8.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"math/big"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/ledgerwatch/turbo-geth/common"
|
|
"github.com/ledgerwatch/turbo-geth/consensus/ethash"
|
|
"github.com/ledgerwatch/turbo-geth/core"
|
|
"github.com/ledgerwatch/turbo-geth/core/state"
|
|
"github.com/ledgerwatch/turbo-geth/core/types"
|
|
"github.com/ledgerwatch/turbo-geth/core/vm"
|
|
"github.com/ledgerwatch/turbo-geth/ethdb"
|
|
"github.com/ledgerwatch/turbo-geth/params"
|
|
)
|
|
|
|
type TxTracer struct {
|
|
gasForSSTORE uint64
|
|
gasForCREATE uint64
|
|
gasForEthSendingCALL uint64
|
|
currentBlock uint64
|
|
sinceAccounts map[common.Address]uint64
|
|
sinceStorage map[common.Address]map[common.Hash]uint64
|
|
created map[common.Address]struct{}
|
|
lastAccessedAccounts map[common.Address]uint64
|
|
lastAccessedStorage map[common.Address]map[common.Hash]uint64
|
|
measureCreate bool
|
|
measureDepth int
|
|
measureCurrentGas uint64
|
|
trace bool
|
|
}
|
|
|
|
func NewTxTracer() *TxTracer {
|
|
return &TxTracer{
|
|
lastAccessedAccounts: make(map[common.Address]uint64),
|
|
lastAccessedStorage: make(map[common.Address]map[common.Hash]uint64),
|
|
}
|
|
}
|
|
|
|
func (tt *TxTracer) ResetCounters() {
|
|
tt.gasForSSTORE = 0
|
|
tt.gasForCREATE = 0
|
|
tt.gasForEthSendingCALL = 0
|
|
tt.sinceAccounts = make(map[common.Address]uint64)
|
|
tt.sinceStorage = make(map[common.Address]map[common.Hash]uint64)
|
|
tt.created = make(map[common.Address]struct{})
|
|
}
|
|
|
|
func (tt *TxTracer) CaptureStart(depth int, from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error {
|
|
tt.queryAccountAccess(to)
|
|
return nil
|
|
}
|
|
|
|
func (tt *TxTracer) markAccountAccess(account common.Address) {
|
|
tt.lastAccessedAccounts[account] = tt.currentBlock
|
|
}
|
|
|
|
func (tt *TxTracer) markStorageAccess(account common.Address, storageKey common.Hash) {
|
|
if m, ok := tt.lastAccessedStorage[account]; ok {
|
|
m[storageKey] = tt.currentBlock
|
|
} else {
|
|
m = make(map[common.Hash]uint64)
|
|
tt.lastAccessedStorage[account] = m
|
|
m[storageKey] = tt.currentBlock
|
|
}
|
|
}
|
|
|
|
func (tt *TxTracer) queryAccountAccess(account common.Address) {
|
|
var q uint64 = tt.currentBlock
|
|
if blockNum, ok := tt.lastAccessedAccounts[account]; ok {
|
|
q = tt.currentBlock - blockNum
|
|
}
|
|
tt.sinceAccounts[account] = q
|
|
}
|
|
|
|
func (tt *TxTracer) queryStorageAccess(account common.Address, storageKey common.Hash) {
|
|
var q uint64 = tt.currentBlock
|
|
if m, ok1 := tt.lastAccessedStorage[account]; ok1 {
|
|
if blockNum, ok2 := m[storageKey]; ok2 {
|
|
q = tt.currentBlock - blockNum
|
|
}
|
|
}
|
|
if m, ok := tt.sinceStorage[account]; ok {
|
|
m[storageKey] = q
|
|
} else {
|
|
m = make(map[common.Hash]uint64)
|
|
tt.sinceStorage[account] = m
|
|
m[storageKey] = q
|
|
}
|
|
}
|
|
|
|
func (tt *TxTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
|
if tt.measureCreate && tt.measureDepth+1 == depth {
|
|
tt.gasForCREATE += (tt.measureCurrentGas - gas)
|
|
}
|
|
tt.measureCreate = false
|
|
switch op {
|
|
case vm.SSTORE:
|
|
if cost <= gas {
|
|
tt.gasForSSTORE += cost
|
|
}
|
|
if stack.Len() > 0 {
|
|
tt.queryStorageAccess(contract.Address(), common.BigToHash(stack.Back(0)))
|
|
tt.markStorageAccess(contract.Address(), common.BigToHash(stack.Back(0)))
|
|
}
|
|
case vm.SLOAD:
|
|
if stack.Len() > 0 {
|
|
tt.markStorageAccess(contract.Address(), common.BigToHash(stack.Back(0)))
|
|
}
|
|
case vm.CREATE, vm.CREATE2:
|
|
tt.measureCurrentGas = gas
|
|
tt.measureDepth = depth
|
|
tt.measureCreate = true
|
|
case vm.CALL, vm.CALLCODE, vm.DELEGATECALL, vm.STATICCALL:
|
|
var callGas uint64
|
|
if stack.Len() > 0 && cost <= gas {
|
|
callGas = stack.Back(0).Uint64()
|
|
if callGas > gas {
|
|
callGas = gas
|
|
}
|
|
tt.gasForEthSendingCALL += (cost - callGas)
|
|
}
|
|
if stack.Len() > 1 {
|
|
tt.queryAccountAccess(common.BigToAddress(stack.Back(1)))
|
|
}
|
|
case vm.SELFDESTRUCT:
|
|
if stack.Len() > 0 {
|
|
tt.queryAccountAccess(common.BigToAddress(stack.Back(0)))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
func (tt *TxTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
|
|
return nil
|
|
}
|
|
func (tt *TxTracer) CaptureEnd(depth int, output []byte, gasUsed uint64, t time.Duration, err error) error {
|
|
return nil
|
|
}
|
|
func (tt *TxTracer) CaptureCreate(creator common.Address, creation common.Address) error {
|
|
tt.markAccountAccess(creation)
|
|
tt.created[creation] = struct{}{}
|
|
return nil
|
|
}
|
|
func (tt *TxTracer) CaptureAccountRead(account common.Address) error {
|
|
tt.markAccountAccess(account)
|
|
return nil
|
|
}
|
|
func (tt *TxTracer) CaptureAccountWrite(account common.Address) error {
|
|
tt.markAccountAccess(account)
|
|
return nil
|
|
}
|
|
|
|
func transaction_stats() {
|
|
sigs := make(chan os.Signal, 1)
|
|
interruptCh := make(chan bool, 1)
|
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
go func() {
|
|
<-sigs
|
|
interruptCh <- true
|
|
}()
|
|
|
|
ethDb, err := ethdb.NewBoltDatabase("/home/akhounov/.ethereum/geth/chaindata")
|
|
//ethDb, err := ethdb.NewBoltDatabase("/Volumes/tb41/turbo-geth/geth/chaindata")
|
|
//ethDb, err := ethdb.NewBoltDatabase("/Users/alexeyakhunov/Library/Ethereum/geth/chaindata")
|
|
check(err)
|
|
defer ethDb.Close()
|
|
f, err := os.OpenFile("txs.csv", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
|
check(err)
|
|
defer f.Close()
|
|
w := bufio.NewWriter(f)
|
|
defer w.Flush()
|
|
fa, err := os.OpenFile("txs_accounts.csv", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
|
check(err)
|
|
defer fa.Close()
|
|
wa := bufio.NewWriter(fa)
|
|
defer wa.Flush()
|
|
fs, err := os.OpenFile("txs_storage.csv", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
|
check(err)
|
|
defer fs.Close()
|
|
ws := bufio.NewWriter(fs)
|
|
defer ws.Flush()
|
|
fc, err := os.OpenFile("txs_created.csv", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
|
check(err)
|
|
defer fc.Close()
|
|
wc := bufio.NewWriter(fc)
|
|
defer wc.Flush()
|
|
tt := NewTxTracer()
|
|
chainConfig := params.MainnetChainConfig
|
|
vmConfig := vm.Config{Tracer: tt, Debug: true}
|
|
bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil)
|
|
check(err)
|
|
blockNum := uint64(*block)
|
|
interrupt := false
|
|
for !interrupt {
|
|
block := bc.GetBlockByNumber(blockNum)
|
|
if block == nil {
|
|
break
|
|
}
|
|
dbstate := state.NewDbState(ethDb, block.NumberU64()-1)
|
|
statedb := state.New(dbstate)
|
|
signer := types.MakeSigner(chainConfig, block.Number())
|
|
for txIdx, tx := range block.Transactions() {
|
|
// Assemble the transaction call message and return if the requested offset
|
|
msg, _ := tx.AsMessage(signer)
|
|
context := core.NewEVMContext(msg, block.Header(), bc, nil)
|
|
// Not yet the searched for transaction, execute on top of the current state
|
|
vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig)
|
|
tt.ResetCounters()
|
|
tt.currentBlock = blockNum
|
|
tt.measureCreate = tx.To() == nil
|
|
tt.measureDepth = 0
|
|
tt.measureCurrentGas = tx.Gas()
|
|
tt.trace = (blockNum == 1279578) && (txIdx == 3)
|
|
if _, usedGas, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
|
panic(fmt.Errorf("tx %x failed: %v", tx.Hash(), err))
|
|
} else {
|
|
var neededGas uint64
|
|
usedGas += statedb.GetRefund()
|
|
if usedGas > tt.gasForSSTORE+tt.gasForCREATE+tt.gasForEthSendingCALL {
|
|
neededGas = usedGas - (tt.gasForSSTORE + tt.gasForCREATE + tt.gasForEthSendingCALL)
|
|
}
|
|
fmt.Fprintf(w, "%d,%d,%d,%d\n", blockNum, txIdx, len(tx.Data()), neededGas)
|
|
//
|
|
for account, since := range tt.sinceAccounts {
|
|
precompile := true
|
|
for i := 0; i < 19; i++ {
|
|
if account[i] != 0 {
|
|
precompile = false
|
|
break
|
|
}
|
|
}
|
|
if !precompile && since != 0 {
|
|
fmt.Fprintf(wa, "%d,%d,%x,%d\n", blockNum, txIdx, account, since)
|
|
}
|
|
}
|
|
//
|
|
for account, m := range tt.sinceStorage {
|
|
for storageKey, since := range m {
|
|
if since != 0 {
|
|
fmt.Fprintf(ws, "%d,%d,%x,%x,%d\n", blockNum, txIdx, account, storageKey, since)
|
|
}
|
|
}
|
|
}
|
|
//
|
|
for account := range tt.created {
|
|
size := len(statedb.GetCode(account))
|
|
if size > 0 {
|
|
fmt.Fprintf(wc, "%d,%d,%x,%d\n", blockNum, txIdx, account, size)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
blockNum++
|
|
if blockNum%1000 == 0 {
|
|
fmt.Printf("Processed %d blocks\n", blockNum)
|
|
}
|
|
// Check for interrupts
|
|
select {
|
|
case interrupt = <-interruptCh:
|
|
fmt.Println("interrupted, please wait for cleanup...")
|
|
default:
|
|
}
|
|
}
|
|
fmt.Printf("Next time specify -block %d\n", blockNum)
|
|
}
|