Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6a32cde
Keep the trace process consistent with the transaction execution process
benbaley Nov 6, 2025
bc83acd
Merge pull request #2338 from benbaley/hotfix_traceblock
benbaley Nov 6, 2025
e3f9732
fix signer bug in api call
benbaley Nov 6, 2025
982a8a3
Merge pull request #2339 from benbaley/hotfix_traceblock
benbaley Nov 6, 2025
72d0498
fix trace EthCompatible
Nov 7, 2025
2dad9f3
Merge pull request #2340 from cheng762/hotfix_traceblock
benbaley Nov 7, 2025
28a76ce
fix trace EthCompatible
Nov 7, 2025
d0fe88a
Merge pull request #2341 from cheng762/hotfix_traceblock
benbaley Nov 7, 2025
667785e
sync ETH #28542
benbaley Nov 12, 2025
ce6b51d
recover comment
benbaley Nov 12, 2025
5497a7b
Merge pull request #2344 from benbaley/hotfix_traceblock
benbaley Nov 12, 2025
704dc64
expand the scope of DereferenceDB locks to avoid concurrent read/writ…
benbaley Nov 12, 2025
0cf25d9
trace接口的statedb不能用BlockChain的database,Dereference会有并发删dirty的操作,readOn…
benbaley Nov 12, 2025
71bc74e
trace接口的statedb不能用BlockChain的database,Dereference会有并发删dirty的操作,readOn…
benbaley Nov 12, 2025
bf13139
Merge pull request #2345 from benbaley/hotfix_traceblock
benbaley Nov 12, 2025
ab8aa44
defer lock.unlock
benbaley Nov 13, 2025
e6c4c56
Merge branch 'hotfix_traceblock' of https://github.com/PlatONnetwork/…
benbaley Nov 13, 2025
39f7440
PlatON-inner transactions tracing are not currently supported
benbaley Nov 13, 2025
cdd714e
rm unused log
benbaley Nov 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions common/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ import (

const (
DefaultAddressHRP = "lat"

EthCompatibleKey ContextKey = "eth_compatible"
)

type ContextKey string

var currentAddressHRP string

func GetAddressHRP() string {
Expand Down
3 changes: 3 additions & 0 deletions core/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,7 @@ var (

// ErrSenderNoEOA is returned if the sender of a transaction is a contract.
ErrSenderNoEOA = errors.New("sender not an eoa")

// ErrPlatONTxNotSupportTracing is returned if tracing an inner tx.
ErrPlatONTxNotSupportTracing = errors.New("PlatON-inner transactions' tracing are not currently supported")
)
54 changes: 43 additions & 11 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,23 @@ func (st *StateTransition) to() common.Address {
return *st.msg.To()
}

func (st *StateTransition) buyGas() error {
func (st *StateTransition) buyGas(isContractIvk bool) error {
mgval := new(big.Int).SetUint64(st.msg.Gas())
mgval = mgval.Mul(mgval, st.gasPrice)
balanceCheck := mgval
if st.gasFeeCap != nil {

// 1.5.0以前的逻辑中preCheck(并行和串行都是)只检查gas * gasPrice,没有value的校验
// 因PlatON的普通转账交易默认走parallel,此处只有合约调交易和api接口调用
// 增加当前版本的判断,以便preCheck保持和parallel的preCheck逻辑一致
if isContractIvk && st.gasFeeCap != nil {
balanceCheck = new(big.Int).SetUint64(st.msg.Gas())
balanceCheck = balanceCheck.Mul(balanceCheck, st.gasFeeCap)
balanceCheck.Add(balanceCheck, st.value)
} else {
// 因为parallel在1.5.0以前的preCheck只验证了gas,没有验证value
// 放过了一些gasLimit*gasPrice+value > balance的交易
// 所以这里对于普通转账类交易(走parallel的)check只能不验证value(实际上如果gasused+value如果不够交易也不会成功)
balanceCheck = mgval
}
if have, want := st.state.GetBalance(st.msg.From()), balanceCheck; have.Cmp(want) < 0 {
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want)
Expand All @@ -242,11 +251,16 @@ func (st *StateTransition) buyGas() error {
st.gas += st.msg.Gas()

st.initialGas = st.msg.Gas()
st.state.SubBalance(st.msg.From(), mgval)

// 普通转账因为走了parallel,没有buyGas和refund
// 所以这里也不扣mgval
if isContractIvk {
st.state.SubBalance(st.msg.From(), mgval)
}
return nil
}

func (st *StateTransition) preCheck() error {
func (st *StateTransition) preCheck(isContractIvk bool) error {
// Only check transactions that are not fake
if !st.msg.IsFake() {
// Make sure this transaction's nonce is correct.
Expand Down Expand Up @@ -293,7 +307,15 @@ func (st *StateTransition) preCheck() error {
}
}
}
return st.buyGas()
return st.buyGas(isContractIvk)
}
func (st *StateTransition) isContractIvk() bool {
address := st.msg.To()
if address == nil { // create a contract
return true
}
rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber)
return st.evm.StateDB.GetCodeSize(*address) > 0 || vm.IsPrecompiledContract(*address, rules, gov.Gte150VersionState(st.evm.StateDB))
}

// TransitionDb will transition the state by applying the current message and
Expand All @@ -310,6 +332,10 @@ func (st *StateTransition) preCheck() error {
// However if any consensus issue encountered, return the error directly with
// nil evm execution result.
func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
// because of the inconsistent logics of serial and parallel,
// tx added a check to determine whether it is a contract invoking.
isContractIvk := st.isContractIvk()

// First check this message satisfies all consensus rules before
// applying the message. The rules include these clauses
//
Expand All @@ -321,7 +347,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
// 6. caller has enough balance to cover asset transfer for **topmost** call

// Check clauses 1-3, buy gas if everything is correct
if err := st.preCheck(); err != nil {
if err := st.preCheck(isContractIvk); err != nil {
return nil, err
}

Expand Down Expand Up @@ -416,12 +442,16 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
}
}

if !isContractIvk {
gasUsed := new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)
st.state.SubBalance(st.msg.From(), gasUsed)
}
if pauli {
// After EIP-3529: refunds are capped to gasUsed / 5
st.refundGas(params.RefundQuotientEIP3529)
st.refundGas(params.RefundQuotientEIP3529, isContractIvk)
} else {
// Before EIP-3529: refunds were capped to gasUsed / 2
st.refundGas(params.RefundQuotient)
st.refundGas(params.RefundQuotient, isContractIvk)
}

effectiveTip := st.gasPrice
Expand All @@ -446,7 +476,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
}, nil
}

func (st *StateTransition) refundGas(refundQuotient uint64) {
func (st *StateTransition) refundGas(refundQuotient uint64, isContractIvk bool) {
// Apply refund counter, capped to a refund quotient
refund := st.gasUsed() / refundQuotient
if refund > st.state.GetRefund() {
Expand All @@ -456,8 +486,10 @@ func (st *StateTransition) refundGas(refundQuotient uint64) {

// Return ETH for remaining gas, exchanged at the original rate.
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
st.state.AddBalance(st.msg.From(), remaining)

// 转账交易走parallel没有预扣gas,这里不用返还
if isContractIvk {
st.state.AddBalance(st.msg.From(), remaining)
}
// Also return remaining gas to the block gas counter so it is
// available for the next transaction.
st.gp.AddGas(st.gas)
Expand Down
5 changes: 3 additions & 2 deletions eth/state_accessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"errors"
"fmt"
"github.com/PlatONnetwork/PlatON-Go/x/gov"
"time"

"github.com/PlatONnetwork/PlatON-Go/common"
Expand Down Expand Up @@ -202,15 +203,15 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
}
// Lookup the statedb of parent block from the live database,
// otherwise regenerate it on the flight.
statedb, release, err := eth.StateAtBlock(ctx, parent, reexec, nil, true, false)
statedb, release, err := eth.StateAtBlock(ctx, parent, reexec, nil, false, false)
if err != nil {
return nil, vm.BlockContext{}, nil, nil, err
}
if txIndex == 0 && len(block.Transactions()) == 0 {
return nil, vm.BlockContext{}, statedb, release, nil
}
// Recompute transactions up to the target index.
signer := types.MakeSigner(eth.blockchain.Config(), block.Number(), true)
signer := types.MakeSigner(eth.blockchain.Config(), block.Number(), gov.Gte150VersionState(statedb))
for idx, tx := range block.Transactions() {
// Assemble the transaction call message and return if the requested offset
msg, _ := tx.AsMessage(signer, block.BaseFee())
Expand Down
40 changes: 28 additions & 12 deletions eth/tracers/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (

"github.com/PlatONnetwork/PlatON-Go/common"
"github.com/PlatONnetwork/PlatON-Go/common/hexutil"
cvm "github.com/PlatONnetwork/PlatON-Go/common/vm"
"github.com/PlatONnetwork/PlatON-Go/consensus"
"github.com/PlatONnetwork/PlatON-Go/core"
"github.com/PlatONnetwork/PlatON-Go/core/rawdb"
Expand Down Expand Up @@ -252,7 +253,7 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf
}
sub := notifier.CreateSubscription()

resCh := api.traceChain(from, to, config, notifier.Closed())
resCh := api.traceChain(ctx, from, to, config, notifier.Closed())
go func() {
for result := range resCh {
notifier.Notify(sub.ID, result)
Expand All @@ -266,7 +267,7 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf
// the end block but excludes the start one. The return value will be one item per
// transaction, dependent on the requested tracer.
// The tracing procedure should be aborted in case the closed signal is received.
func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed <-chan interface{}) chan *blockTraceResult {
func (api *API) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig, closed <-chan interface{}) chan *blockTraceResult {
reexec := defaultTraceReexec
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
Expand All @@ -277,8 +278,8 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
threads = blocks
}
var (
pend = new(sync.WaitGroup)
ctx = context.Background()
pend = new(sync.WaitGroup)
//ctx = context.Background()
taskCh = make(chan *blockTraceTask, threads)
resCh = make(chan *blockTraceTask, threads)
tracker = newStateTracker(maximumPendingTraceStates, start.NumberU64())
Expand All @@ -291,7 +292,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
// Fetch and execute the block trace taskCh
for task := range taskCh {
var (
signer = types.MakeSigner(api.backend.ChainConfig(), task.block.Number(), true)
signer = types.MakeSigner(api.backend.ChainConfig(), task.block.Number(), gov.Gte150VersionState(task.statedb))
blockCtx = core.NewEVMBlockContext(task.block.Header(), api.chainContext(ctx))
)
// Trace all the transactions contained within
Expand Down Expand Up @@ -537,15 +538,15 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, false, false)
if err != nil {
return nil, err
}
defer release()

var (
roots []common.Hash
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), true)
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), gov.Gte150VersionState(statedb))
chainConfig = api.backend.ChainConfig()
vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx))
deleteEmptyObjects = true
Expand Down Expand Up @@ -604,7 +605,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, false, false)
if err != nil {
return nil, err
}
Expand All @@ -623,7 +624,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
txs = block.Transactions()
blockHash = block.Hash()
blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx))
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), false)
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), gov.Gte150VersionState(statedb))
results = make([]*txTraceResult, len(txs))
)
for i, tx := range txs {
Expand Down Expand Up @@ -652,7 +653,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, statedb *state.StateDB, config *TraceConfig) ([]*txTraceResult, error) {
// Execute all the transaction contained within the block concurrently
var (
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), false)
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), gov.Gte150VersionState(statedb))

txs = block.Transactions()
blockHash = block.Hash()
Expand Down Expand Up @@ -747,7 +748,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false)
statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, false, false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -920,7 +921,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
statedb, release, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true, false)
statedb, release, err := api.backend.StateAtBlock(ctx, block, reexec, nil, false, false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -950,6 +951,10 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
// executes the given message in the provided environment. The return value will
// be tracer dependent.
func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
if message.To() != nil && cvm.PrecompiledContractCheckInstance.IsPlatONPrecompiledContract(*message.To()) {
log.Warn("trace tx failed", "error", core.ErrPlatONTxNotSupportTracing)
return json.RawMessage(`{}`), nil
}
var (
tracer Tracer
err error
Expand All @@ -959,6 +964,17 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex
if config == nil {
config = &TraceConfig{}
}

ethcompatible, _ := ctx.Value(common.EthCompatibleKey).(bool)
if ethcompatible {
txctx.EthCompatible = true
if config.Config == nil {
config.Config = new(logger.Config)
}
config.Config.EthCompatible = true
txctx.EthCompatible = true
}

// Default tracer is the struct logger
tracer = logger.NewStructLogger(config.Config)
if config.Tracer != nil {
Expand Down
11 changes: 11 additions & 0 deletions eth/tracers/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (

"github.com/PlatONnetwork/PlatON-Go/common"
"github.com/PlatONnetwork/PlatON-Go/common/hexutil"
json2 "github.com/PlatONnetwork/PlatON-Go/common/json"
"github.com/PlatONnetwork/PlatON-Go/common/math"
"github.com/PlatONnetwork/PlatON-Go/core/types"
"github.com/PlatONnetwork/PlatON-Go/core/vm"
Expand Down Expand Up @@ -57,6 +58,8 @@ type Config struct {
Limit int // maximum length of output, but zero means unlimited
// Chain overrides, can be used to execute a trace using future fork rules
Overrides *params.ChainConfig `json:"overrides,omitempty"`

EthCompatible bool //内部实现,仅为了让返回值适配eth的地址格式
}

//go:generate go run github.com/fjl/gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
Expand Down Expand Up @@ -248,6 +251,14 @@ func (l *StructLogger) GetResult() (json.RawMessage, error) {
if failed && l.err != vm.ErrExecutionReverted {
returnVal = ""
}
if l.cfg.EthCompatible {
return json2.Marshal(&ExecutionResult{
Gas: l.usedGas,
Failed: failed,
ReturnValue: returnVal,
StructLogs: formatLogs(l.StructLogs()),
})
}
return json.Marshal(&ExecutionResult{
Gas: l.usedGas,
Failed: failed,
Expand Down
13 changes: 13 additions & 0 deletions eth/tracers/native/4byte.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"sync/atomic"

"github.com/PlatONnetwork/PlatON-Go/common"
json2 "github.com/PlatONnetwork/PlatON-Go/common/json"
"github.com/PlatONnetwork/PlatON-Go/core/vm"
"github.com/PlatONnetwork/PlatON-Go/eth/tracers"
)
Expand Down Expand Up @@ -51,6 +52,8 @@ type fourByteTracer struct {
interrupt uint32 // Atomic flag to signal execution interruption
reason error // Textual reason for the interruption
activePrecompiles []common.Address // Updated on CaptureStart based on given rules

ethCompatible bool //内部实现,仅为了让返回值适配eth的地址格式
}

// newFourByteTracer returns a native go tracer which collects
Expand All @@ -59,6 +62,9 @@ func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer,
t := &fourByteTracer{
ids: make(map[string]int),
}
if ctx != nil {
t.ethCompatible = ctx.EthCompatible
}
return t, nil
}

Expand Down Expand Up @@ -114,6 +120,13 @@ func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to comm
// GetResult returns the json-encoded nested list of call traces, and any
// error arising from the encoding or forceful termination (via `Stop`).
func (t *fourByteTracer) GetResult() (json.RawMessage, error) {
if t.ethCompatible {
res, err := json2.Marshal(t.ids)
if err != nil {
return nil, err
}
return res, t.reason
}
res, err := json.Marshal(t.ids)
if err != nil {
return nil, err
Expand Down
Loading
Loading