mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 23:26:44 +00:00
added getReadMap and getWriteMap (#473)
This commit is contained in:
parent
f7bd7ca66b
commit
4507b2e057
5 changed files with 250 additions and 10 deletions
|
|
@ -230,7 +230,7 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
|
|||
}
|
||||
|
||||
statedb.AddBalance(task.blockContext.Coinbase, task.result.FeeTipped)
|
||||
output1 := new(big.Int).SetBytes(task.result.senderInitBalance.Bytes())
|
||||
output1 := new(big.Int).SetBytes(task.result.SenderInitBalance.Bytes())
|
||||
output2 := new(big.Int).SetBytes(coinbaseBalance.Bytes())
|
||||
|
||||
// Deprecating transfer log and will be removed in future fork. PLEASE DO NOT USE this transfer log going forward. Parameters won't get updated as expected going forward with EIP1559
|
||||
|
|
@ -242,7 +242,7 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
|
|||
task.blockContext.Coinbase,
|
||||
|
||||
task.result.FeeTipped,
|
||||
task.result.senderInitBalance,
|
||||
task.result.SenderInitBalance,
|
||||
coinbaseBalance,
|
||||
output1.Sub(output1, task.result.FeeTipped),
|
||||
output2.Add(output2, task.result.FeeTipped),
|
||||
|
|
|
|||
|
|
@ -353,6 +353,61 @@ func (sw *StateDB) ApplyMVWriteSet(writes []blockstm.WriteDescriptor) {
|
|||
}
|
||||
}
|
||||
|
||||
type DumpStruct struct {
|
||||
TxIdx int
|
||||
TxInc int
|
||||
VerIdx int
|
||||
VerInc int
|
||||
Path []byte
|
||||
Op string
|
||||
}
|
||||
|
||||
// get readMap Dump of format: "TxIdx, Inc, Path, Read"
|
||||
func (s *StateDB) GetReadMapDump() []DumpStruct {
|
||||
readList := s.MVReadList()
|
||||
res := make([]DumpStruct, 0, len(readList))
|
||||
|
||||
for _, val := range readList {
|
||||
temp := &DumpStruct{
|
||||
TxIdx: s.txIndex,
|
||||
TxInc: s.incarnation,
|
||||
VerIdx: val.V.TxnIndex,
|
||||
VerInc: val.V.Incarnation,
|
||||
Path: val.Path,
|
||||
Op: "Read\n",
|
||||
}
|
||||
res = append(res, *temp)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// get writeMap Dump of format: "TxIdx, Inc, Path, Write"
|
||||
func (s *StateDB) GetWriteMapDump() []DumpStruct {
|
||||
writeList := s.MVReadList()
|
||||
res := make([]DumpStruct, 0, len(writeList))
|
||||
|
||||
for _, val := range writeList {
|
||||
temp := &DumpStruct{
|
||||
TxIdx: s.txIndex,
|
||||
TxInc: s.incarnation,
|
||||
VerIdx: val.V.TxnIndex,
|
||||
VerInc: val.V.Incarnation,
|
||||
Path: val.Path,
|
||||
Op: "Write\n",
|
||||
}
|
||||
res = append(res, *temp)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// add empty MVHashMap to StateDB
|
||||
func (s *StateDB) AddEmptyMVHashMap() {
|
||||
mvh := blockstm.MakeMVHashMap()
|
||||
s.mvHashmap = mvh
|
||||
}
|
||||
|
||||
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
|
||||
// state trie concurrently while the state is mutated so that when we reach the
|
||||
// commit phase, most of the needed data is already hot.
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ type ExecutionResult struct {
|
|||
UsedGas uint64 // Total used gas but include the refunded gas
|
||||
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
|
||||
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
|
||||
senderInitBalance *big.Int
|
||||
SenderInitBalance *big.Int
|
||||
FeeBurnt *big.Int
|
||||
BurntContractAddress common.Address
|
||||
FeeTipped *big.Int
|
||||
|
|
@ -403,7 +403,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
|||
UsedGas: st.gasUsed(),
|
||||
Err: vmerr,
|
||||
ReturnData: ret,
|
||||
senderInitBalance: input1,
|
||||
SenderInitBalance: input1,
|
||||
FeeBurnt: burnAmount,
|
||||
BurntContractAddress: burntContractAddress,
|
||||
FeeTipped: amount,
|
||||
|
|
|
|||
|
|
@ -20,10 +20,13 @@ import (
|
|||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -61,6 +64,10 @@ const (
|
|||
// For non-archive nodes, this limit _will_ be overblown, as disk-backed tries
|
||||
// will only be found every ~15K blocks or so.
|
||||
defaultTracechainMemLimit = common.StorageSize(500 * 1024 * 1024)
|
||||
|
||||
defaultPath = string(".")
|
||||
|
||||
defaultIOFlag = false
|
||||
)
|
||||
|
||||
// Backend interface provides the common API services (that are provided by
|
||||
|
|
@ -170,6 +177,8 @@ type TraceConfig struct {
|
|||
Tracer *string
|
||||
Timeout *string
|
||||
Reexec *uint64
|
||||
Path *string
|
||||
IOFlag *bool
|
||||
}
|
||||
|
||||
// TraceCallConfig is the config for traceCall API. It holds one more
|
||||
|
|
@ -567,18 +576,37 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
|||
if block.NumberU64() == 0 {
|
||||
return nil, errors.New("genesis is not traceable")
|
||||
}
|
||||
|
||||
parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reexec := defaultTraceReexec
|
||||
if config != nil && config.Reexec != nil {
|
||||
reexec = *config.Reexec
|
||||
}
|
||||
|
||||
path := defaultPath
|
||||
if config != nil && config.Path != nil {
|
||||
path = *config.Path
|
||||
}
|
||||
|
||||
ioflag := defaultIOFlag
|
||||
if config != nil && config.IOFlag != nil {
|
||||
ioflag = *config.IOFlag
|
||||
}
|
||||
|
||||
statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create and add empty mvHashMap in statedb as StateAtBlock does not have mvHashmap in it.
|
||||
if ioflag {
|
||||
statedb.AddEmptyMVHashMap()
|
||||
}
|
||||
|
||||
// Execute all the transaction contained within the block concurrently
|
||||
var (
|
||||
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
|
||||
|
|
@ -615,10 +643,31 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
|||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var IOdump string
|
||||
|
||||
var RWstruct []state.DumpStruct
|
||||
|
||||
var london bool
|
||||
|
||||
if ioflag {
|
||||
IOdump = "TransactionIndex, Incarnation, VersionTxIdx, VersionInc, Path, Operation\n"
|
||||
RWstruct = []state.DumpStruct{}
|
||||
}
|
||||
// Feed the transactions into the tracers and return
|
||||
var failed error
|
||||
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||
|
||||
if ioflag {
|
||||
london = api.backend.ChainConfig().IsLondon(block.Number())
|
||||
}
|
||||
|
||||
for i, tx := range txs {
|
||||
if ioflag {
|
||||
// copy of statedb
|
||||
statedb = statedb.Copy()
|
||||
}
|
||||
|
||||
// Send the trace task over for execution
|
||||
jobs <- &txTraceTask{statedb: statedb.Copy(), index: i}
|
||||
|
||||
|
|
@ -626,14 +675,73 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
|||
msg, _ := tx.AsMessage(signer, block.BaseFee())
|
||||
statedb.Prepare(tx.Hash(), i)
|
||||
vmenv := vm.NewEVM(blockCtx, core.NewEVMTxContext(msg), statedb, api.backend.ChainConfig(), vm.Config{})
|
||||
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
|
||||
failed = err
|
||||
break
|
||||
|
||||
if !ioflag {
|
||||
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
|
||||
failed = err
|
||||
break
|
||||
}
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||
} else {
|
||||
coinbaseBalance := statedb.GetBalance(blockCtx.Coinbase)
|
||||
|
||||
result, err := core.ApplyMessageNoFeeBurnOrTip(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
|
||||
|
||||
if err != nil {
|
||||
failed = err
|
||||
break
|
||||
}
|
||||
|
||||
if london {
|
||||
statedb.AddBalance(result.BurntContractAddress, result.FeeBurnt)
|
||||
}
|
||||
|
||||
statedb.AddBalance(blockCtx.Coinbase, result.FeeTipped)
|
||||
output1 := new(big.Int).SetBytes(result.SenderInitBalance.Bytes())
|
||||
output2 := new(big.Int).SetBytes(coinbaseBalance.Bytes())
|
||||
|
||||
// Deprecating transfer log and will be removed in future fork. PLEASE DO NOT USE this transfer log going forward. Parameters won't get updated as expected going forward with EIP1559
|
||||
// add transfer log
|
||||
core.AddFeeTransferLog(
|
||||
statedb,
|
||||
|
||||
msg.From(),
|
||||
blockCtx.Coinbase,
|
||||
|
||||
result.FeeTipped,
|
||||
result.SenderInitBalance,
|
||||
coinbaseBalance,
|
||||
output1.Sub(output1, result.FeeTipped),
|
||||
output2.Add(output2, result.FeeTipped),
|
||||
)
|
||||
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||
statedb.FlushMVWriteSet()
|
||||
|
||||
structRead := statedb.GetReadMapDump()
|
||||
structWrite := statedb.GetWriteMapDump()
|
||||
|
||||
RWstruct = append(RWstruct, structRead...)
|
||||
RWstruct = append(RWstruct, structWrite...)
|
||||
}
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||
}
|
||||
|
||||
if ioflag {
|
||||
for _, val := range RWstruct {
|
||||
IOdump += fmt.Sprintf("%v , %v, %v , %v, ", val.TxIdx, val.TxInc, val.VerIdx, val.VerInc) + hex.EncodeToString(val.Path) + ", " + val.Op
|
||||
}
|
||||
|
||||
// make sure that the file exists and write IOdump
|
||||
err = ioutil.WriteFile(filepath.Join(path, "data.csv"), []byte(fmt.Sprint(IOdump)), 0600)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
close(jobs)
|
||||
pend.Wait()
|
||||
|
||||
|
|
|
|||
|
|
@ -415,6 +415,83 @@ func TestTraceBlock(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestIOdump(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Initialize test accounts
|
||||
accounts := newAccounts(5)
|
||||
genesis := &core.Genesis{Alloc: core.GenesisAlloc{
|
||||
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[3].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[4].addr: {Balance: big.NewInt(params.Ether)},
|
||||
}}
|
||||
genBlocks := 1
|
||||
signer := types.HomesteadSigner{}
|
||||
api := NewAPI(newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
|
||||
// Transfer from account[0] to account[1], account[1] to account[2], account[2] to account[3], account[3] to account[4], account[4] to account[0]
|
||||
// value: 1000 wei
|
||||
// fee: 0 wei
|
||||
|
||||
for j := 0; j < 5; j++ {
|
||||
tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[(j+1)%5].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[j].key)
|
||||
b.AddTx(tx)
|
||||
}
|
||||
}))
|
||||
|
||||
ioflag := new(bool)
|
||||
|
||||
*ioflag = true
|
||||
|
||||
var testSuite = []struct {
|
||||
blockNumber rpc.BlockNumber
|
||||
config *TraceConfig
|
||||
want string
|
||||
expectErr error
|
||||
}{
|
||||
// Trace head block
|
||||
{
|
||||
config: &TraceConfig{
|
||||
IOFlag: ioflag,
|
||||
},
|
||||
blockNumber: rpc.BlockNumber(genBlocks),
|
||||
want: `[{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}},{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}},{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}},{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}},{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}]`,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testSuite {
|
||||
result, err := api.TraceBlockByNumber(context.Background(), tc.blockNumber, tc.config)
|
||||
if tc.expectErr != nil {
|
||||
if err == nil {
|
||||
t.Errorf("test %d, want error %v", i, tc.expectErr)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(err, tc.expectErr) {
|
||||
t.Errorf("test %d: error mismatch, want %v, get %v", i, tc.expectErr, err)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("test %d, want no error, have %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
have, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
t.Errorf("Error in Marshal: %v", err)
|
||||
}
|
||||
|
||||
want := tc.want
|
||||
if string(have) != want {
|
||||
t.Errorf("test %d, result mismatch, have\n%v\n, want\n%v\n", i, string(have), want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTracingWithOverrides(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Initialize test accounts
|
||||
|
|
|
|||
Loading…
Reference in a new issue