added getReadMap and getWriteMap (#473)

This commit is contained in:
Pratik Patil 2022-08-19 14:47:58 +05:30 committed by Jerry
parent f7bd7ca66b
commit 4507b2e057
5 changed files with 250 additions and 10 deletions

View file

@ -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),

View file

@ -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.

View file

@ -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,

View file

@ -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()

View file

@ -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