mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 10:50:44 +00:00
Here we add a Go API for running tracing plugins within the main block import process. As an advanced user of geth, you can now create a Go file in eth/tracers/live/, and within that file register your custom tracer implementation. Then recompile geth and select your tracer on the command line. Hooks defined in the tracer will run whenever a block is processed. The hook system is defined in package core/tracing. It uses a struct with callbacks, instead of requiring an interface, for several reasons: - We plan to keep this API stable long-term. The core/tracing hook API does not depend on on deep geth internals. - There are a lot of hooks, and tracers will only need some of them. Using a struct allows you to implement only the hooks you want to actually use. All existing tracers in eth/tracers/native have been rewritten to use the new hook system. This change breaks compatibility with the vm.EVMLogger interface that we used to have. If you are a user of vm.EVMLogger, please migrate to core/tracing, and sorry for breaking your stuff. But we just couldn't have both the old and new tracing APIs coexist in the EVM. --------- Co-authored-by: Sina M <1591639+s1na@users.noreply.github.com> Co-authored-by: Matthieu Vachon <matthieu.o.vachon@gmail.com> Co-authored-by: Delweng <delweng@gmail.com> Co-authored-by: Martin HS <martin@swende.se>
This commit is contained in:
parent
7be9ab1ed9
commit
ad9003c41e
94 changed files with 2995 additions and 1252 deletions
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
|
|
@ -591,7 +592,7 @@ func DoSettleBalance(coinbase common.Address, takerOrder, makerOrder *tradingsta
|
||||||
tradingstate.SetSubRelayerFee(makerOrder.ExchangeAddress, newRelayerMakerFee, common.RelayerFee, statedb)
|
tradingstate.SetSubRelayerFee(makerOrder.ExchangeAddress, newRelayerMakerFee, common.RelayerFee, statedb)
|
||||||
|
|
||||||
masternodeOwner := statedb.GetOwner(coinbase)
|
masternodeOwner := statedb.GetOwner(coinbase)
|
||||||
statedb.AddBalance(masternodeOwner, matchingFee)
|
statedb.AddBalance(masternodeOwner, matchingFee, tracing.BalanceChangeUnspecified)
|
||||||
|
|
||||||
err = tradingstate.SetTokenBalance(takerOrder.UserAddress, newTakerInTotal, settleBalance.Taker.InToken, statedb)
|
err = tradingstate.SetTokenBalance(takerOrder.UserAddress, newTakerInTotal, settleBalance.Taker.InToken, statedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -680,7 +681,7 @@ func (XDCx *XDCX) ProcessCancelOrder(header *types.Header, tradingStateDB *tradi
|
||||||
}
|
}
|
||||||
masternodeOwner := statedb.GetOwner(coinbase)
|
masternodeOwner := statedb.GetOwner(coinbase)
|
||||||
// relayers pay XDC for masternode
|
// relayers pay XDC for masternode
|
||||||
statedb.AddBalance(masternodeOwner, common.RelayerCancelFee)
|
statedb.AddBalance(masternodeOwner, common.RelayerCancelFee, tracing.BalanceChangeUnspecified)
|
||||||
|
|
||||||
relayerOwner := tradingstate.GetRelayerOwner(originOrder.ExchangeAddress, statedb)
|
relayerOwner := tradingstate.GetRelayerOwner(originOrder.ExchangeAddress, statedb)
|
||||||
switch originOrder.Side {
|
switch originOrder.Side {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
@ -139,7 +140,7 @@ func SubRelayerFee(relayer common.Address, fee *big.Int, statedb *state.StateDB)
|
||||||
} else {
|
} else {
|
||||||
balance = new(big.Int).Sub(balance, fee)
|
balance = new(big.Int).Sub(balance, fee)
|
||||||
statedb.SetState(common.RelayerRegistrationSMC, locHashDeposit, common.BigToHash(balance))
|
statedb.SetState(common.RelayerRegistrationSMC, locHashDeposit, common.BigToHash(balance))
|
||||||
statedb.SubBalance(common.RelayerRegistrationSMC, fee)
|
statedb.SubBalance(common.RelayerRegistrationSMC, fee, tracing.BalanceChangeUnspecified)
|
||||||
log.Debug("ApplyXDCXMatchedTransaction settle balance: SubRelayerFee AFTER", "relayer", relayer.String(), "balance", balance)
|
log.Debug("ApplyXDCXMatchedTransaction settle balance: SubRelayerFee AFTER", "relayer", relayer.String(), "balance", balance)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +163,7 @@ func AddTokenBalance(addr common.Address, value *big.Int, token common.Address,
|
||||||
if token == common.XDCNativeAddressBinary {
|
if token == common.XDCNativeAddressBinary {
|
||||||
balance := statedb.GetBalance(addr)
|
balance := statedb.GetBalance(addr)
|
||||||
log.Debug("ApplyXDCXMatchedTransaction settle balance: ADD TOKEN XDC NATIVE BEFORE", "token", common.XDCNativeAddress, "address", addr.String(), "balance", balance, "orderValue", value)
|
log.Debug("ApplyXDCXMatchedTransaction settle balance: ADD TOKEN XDC NATIVE BEFORE", "token", common.XDCNativeAddress, "address", addr.String(), "balance", balance, "orderValue", value)
|
||||||
statedb.AddBalance(addr, value)
|
statedb.AddBalance(addr, value, tracing.BalanceChangeUnspecified)
|
||||||
balance = statedb.GetBalance(addr)
|
balance = statedb.GetBalance(addr)
|
||||||
log.Debug("ApplyXDCXMatchedTransaction settle balance: ADD XDC NATIVE BALANCE AFTER", "token", token.String(), "address", addr.String(), "balance", balance, "orderValue", value)
|
log.Debug("ApplyXDCXMatchedTransaction settle balance: ADD XDC NATIVE BALANCE AFTER", "token", token.String(), "address", addr.String(), "balance", balance, "orderValue", value)
|
||||||
|
|
||||||
|
|
@ -192,7 +193,7 @@ func SubTokenBalance(addr common.Address, value *big.Int, token common.Address,
|
||||||
if balance.Cmp(value) < 0 {
|
if balance.Cmp(value) < 0 {
|
||||||
return errors.Errorf("value %s in token %s not enough , have : %s , want : %s ", addr.String(), token.String(), balance, value)
|
return errors.Errorf("value %s in token %s not enough , have : %s , want : %s ", addr.String(), token.String(), balance, value)
|
||||||
}
|
}
|
||||||
statedb.SubBalance(addr, value)
|
statedb.SubBalance(addr, value, tracing.BalanceChangeUnspecified)
|
||||||
balance = statedb.GetBalance(addr)
|
balance = statedb.GetBalance(addr)
|
||||||
log.Debug("ApplyXDCXMatchedTransaction settle balance: SUB XDC NATIVE BALANCE AFTER", "token", token.String(), "address", addr.String(), "balance", balance, "orderValue", value)
|
log.Debug("ApplyXDCXMatchedTransaction settle balance: SUB XDC NATIVE BALANCE AFTER", "token", token.String(), "address", addr.String(), "balance", balance, "orderValue", value)
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -323,7 +324,7 @@ func GetTokenBalance(addr common.Address, token common.Address, statedb *state.S
|
||||||
func SetTokenBalance(addr common.Address, balance *big.Int, token common.Address, statedb *state.StateDB) error {
|
func SetTokenBalance(addr common.Address, balance *big.Int, token common.Address, statedb *state.StateDB) error {
|
||||||
// XDC native
|
// XDC native
|
||||||
if token == common.XDCNativeAddressBinary {
|
if token == common.XDCNativeAddressBinary {
|
||||||
statedb.SetBalance(addr, balance)
|
statedb.SetBalance(addr, balance, tracing.BalanceChangeUnspecified)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -344,5 +345,5 @@ func SetSubRelayerFee(relayer common.Address, balance *big.Int, fee *big.Int, st
|
||||||
locBigDeposit := new(big.Int).SetUint64(uint64(0)).Add(locBig, RelayerStructMappingSlot["_deposit"])
|
locBigDeposit := new(big.Int).SetUint64(uint64(0)).Add(locBig, RelayerStructMappingSlot["_deposit"])
|
||||||
locHashDeposit := common.BigToHash(locBigDeposit)
|
locHashDeposit := common.BigToHash(locBigDeposit)
|
||||||
statedb.SetState(common.RelayerRegistrationSMC, locHashDeposit, common.BigToHash(balance))
|
statedb.SetState(common.RelayerRegistrationSMC, locHashDeposit, common.BigToHash(balance))
|
||||||
statedb.SubBalance(common.RelayerRegistrationSMC, fee)
|
statedb.SubBalance(common.RelayerRegistrationSMC, fee, tracing.BalanceChangeUnspecified)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
@ -91,7 +92,7 @@ func SubRelayerFee(relayer common.Address, fee *big.Int, statedb *state.StateDB)
|
||||||
} else {
|
} else {
|
||||||
balance = new(big.Int).Sub(balance, fee)
|
balance = new(big.Int).Sub(balance, fee)
|
||||||
statedb.SetState(common.RelayerRegistrationSMC, locHashDeposit, common.BigToHash(balance))
|
statedb.SetState(common.RelayerRegistrationSMC, locHashDeposit, common.BigToHash(balance))
|
||||||
statedb.SubBalance(common.RelayerRegistrationSMC, fee)
|
statedb.SubBalance(common.RelayerRegistrationSMC, fee, tracing.BalanceChangeUnspecified)
|
||||||
log.Debug("ApplyXDCXMatchedTransaction settle balance: SubRelayerFee AFTER", "relayer", relayer.String(), "balance", balance)
|
log.Debug("ApplyXDCXMatchedTransaction settle balance: SubRelayerFee AFTER", "relayer", relayer.String(), "balance", balance)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -114,7 +115,7 @@ func AddTokenBalance(addr common.Address, value *big.Int, token common.Address,
|
||||||
if token == common.XDCNativeAddressBinary {
|
if token == common.XDCNativeAddressBinary {
|
||||||
balance := statedb.GetBalance(addr)
|
balance := statedb.GetBalance(addr)
|
||||||
log.Debug("ApplyXDCXMatchedTransaction settle balance: ADD TOKEN XDC NATIVE BEFORE", "token", common.XDCNativeAddress, "address", addr.String(), "balance", balance, "orderValue", value)
|
log.Debug("ApplyXDCXMatchedTransaction settle balance: ADD TOKEN XDC NATIVE BEFORE", "token", common.XDCNativeAddress, "address", addr.String(), "balance", balance, "orderValue", value)
|
||||||
statedb.AddBalance(addr, value)
|
statedb.AddBalance(addr, value, tracing.BalanceChangeUnspecified)
|
||||||
balance = statedb.GetBalance(addr)
|
balance = statedb.GetBalance(addr)
|
||||||
log.Debug("ApplyXDCXMatchedTransaction settle balance: ADD XDC NATIVE BALANCE AFTER", "token", common.XDCNativeAddress, "address", addr.String(), "balance", balance, "orderValue", value)
|
log.Debug("ApplyXDCXMatchedTransaction settle balance: ADD XDC NATIVE BALANCE AFTER", "token", common.XDCNativeAddress, "address", addr.String(), "balance", balance, "orderValue", value)
|
||||||
|
|
||||||
|
|
@ -144,7 +145,7 @@ func SubTokenBalance(addr common.Address, value *big.Int, token common.Address,
|
||||||
if balance.Cmp(value) < 0 {
|
if balance.Cmp(value) < 0 {
|
||||||
return errors.Errorf("value %s in token %s not enough , have : %s , want : %s ", addr.String(), common.XDCNativeAddress, balance, value)
|
return errors.Errorf("value %s in token %s not enough , have : %s , want : %s ", addr.String(), common.XDCNativeAddress, balance, value)
|
||||||
}
|
}
|
||||||
statedb.SubBalance(addr, value)
|
statedb.SubBalance(addr, value, tracing.BalanceChangeUnspecified)
|
||||||
balance = statedb.GetBalance(addr)
|
balance = statedb.GetBalance(addr)
|
||||||
log.Debug("ApplyXDCXMatchedTransaction settle balance: SUB XDC NATIVE BALANCE AFTER", "token", common.XDCNativeAddress, "address", addr.String(), "balance", balance, "orderValue", value)
|
log.Debug("ApplyXDCXMatchedTransaction settle balance: SUB XDC NATIVE BALANCE AFTER", "token", common.XDCNativeAddress, "address", addr.String(), "balance", balance, "orderValue", value)
|
||||||
|
|
||||||
|
|
@ -276,7 +277,7 @@ func GetTokenBalance(addr common.Address, token common.Address, statedb *state.S
|
||||||
func SetTokenBalance(addr common.Address, balance *big.Int, token common.Address, statedb *state.StateDB) error {
|
func SetTokenBalance(addr common.Address, balance *big.Int, token common.Address, statedb *state.StateDB) error {
|
||||||
// XDC native
|
// XDC native
|
||||||
if token == common.XDCNativeAddressBinary {
|
if token == common.XDCNativeAddressBinary {
|
||||||
statedb.SetBalance(addr, balance)
|
statedb.SetBalance(addr, balance, tracing.BalanceChangeUnspecified)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -297,5 +298,5 @@ func SetSubRelayerFee(relayer common.Address, balance *big.Int, fee *big.Int, st
|
||||||
locBigDeposit := new(big.Int).SetUint64(uint64(0)).Add(locBig, RelayerStructMappingSlot["_deposit"])
|
locBigDeposit := new(big.Int).SetUint64(uint64(0)).Add(locBig, RelayerStructMappingSlot["_deposit"])
|
||||||
locHashDeposit := common.BigToHash(locBigDeposit)
|
locHashDeposit := common.BigToHash(locBigDeposit)
|
||||||
statedb.SetState(common.RelayerRegistrationSMC, locHashDeposit, common.BigToHash(balance))
|
statedb.SetState(common.RelayerRegistrationSMC, locHashDeposit, common.BigToHash(balance))
|
||||||
statedb.SubBalance(common.RelayerRegistrationSMC, fee)
|
statedb.SubBalance(common.RelayerRegistrationSMC, fee, tracing.BalanceChangeUnspecified)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
)
|
)
|
||||||
|
|
@ -670,7 +671,7 @@ func DoSettleBalance(coinbase common.Address, takerOrder, makerOrder *lendingsta
|
||||||
mapBalances[settleBalance.Maker.OutToken][common.LendingLockAddressBinary] = newCollateralTokenLock
|
mapBalances[settleBalance.Maker.OutToken][common.LendingLockAddressBinary] = newCollateralTokenLock
|
||||||
}
|
}
|
||||||
masternodeOwner := statedb.GetOwner(coinbase)
|
masternodeOwner := statedb.GetOwner(coinbase)
|
||||||
statedb.AddBalance(masternodeOwner, matchingFee)
|
statedb.AddBalance(masternodeOwner, matchingFee, tracing.BalanceChangeUnspecified)
|
||||||
for token, balances := range mapBalances {
|
for token, balances := range mapBalances {
|
||||||
for adrr, value := range balances {
|
for adrr, value := range balances {
|
||||||
err := lendingstate.SetTokenBalance(adrr, value, token, statedb)
|
err := lendingstate.SetTokenBalance(adrr, value, token, statedb)
|
||||||
|
|
@ -747,7 +748,7 @@ func (l *Lending) ProcessCancelOrder(header *types.Header, lendingStateDB *lendi
|
||||||
// relayers pay XDC for masternode
|
// relayers pay XDC for masternode
|
||||||
lendingstate.SubRelayerFee(originOrder.Relayer, common.RelayerLendingCancelFee, statedb)
|
lendingstate.SubRelayerFee(originOrder.Relayer, common.RelayerLendingCancelFee, statedb)
|
||||||
masternodeOwner := statedb.GetOwner(coinbase)
|
masternodeOwner := statedb.GetOwner(coinbase)
|
||||||
statedb.AddBalance(masternodeOwner, common.RelayerLendingCancelFee)
|
statedb.AddBalance(masternodeOwner, common.RelayerLendingCancelFee, tracing.BalanceChangeUnspecified)
|
||||||
relayerOwner := lendingstate.GetRelayerOwner(originOrder.Relayer, statedb)
|
relayerOwner := lendingstate.GetRelayerOwner(originOrder.Relayer, statedb)
|
||||||
switch originOrder.Side {
|
switch originOrder.Side {
|
||||||
case lendingstate.Investing:
|
case lendingstate.Investing:
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
|
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
|
|
@ -737,7 +738,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
||||||
|
|
||||||
// Set infinite balance to the fake caller account.
|
// Set infinite balance to the fake caller account.
|
||||||
from := stateDB.GetOrNewStateObject(call.From)
|
from := stateDB.GetOrNewStateObject(call.From)
|
||||||
from.SetBalance(math.MaxBig256)
|
from.SetBalance(math.MaxBig256, tracing.BalanceChangeUnspecified)
|
||||||
|
|
||||||
// Execute the call.
|
// Execute the call.
|
||||||
msg := &core.Message{
|
msg := &core.Message{
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,8 @@ It expects the genesis file or the network name [ mainnet | testnet | devnet ] a
|
||||||
utils.MetricsInfluxDBTokenFlag,
|
utils.MetricsInfluxDBTokenFlag,
|
||||||
utils.MetricsInfluxDBBucketFlag,
|
utils.MetricsInfluxDBBucketFlag,
|
||||||
utils.MetricsInfluxDBOrganizationFlag,
|
utils.MetricsInfluxDBOrganizationFlag,
|
||||||
|
utils.VMTraceFlag,
|
||||||
|
utils.VMTraceConfigFlag,
|
||||||
}, utils.DatabaseFlags),
|
}, utils.DatabaseFlags),
|
||||||
Description: `
|
Description: `
|
||||||
The import command imports blocks from an RLP-encoded form. The form can be one file
|
The import command imports blocks from an RLP-encoded form. The form can be one file
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ import (
|
||||||
|
|
||||||
// Force-load the tracer engines to trigger registration
|
// Force-load the tracer engines to trigger registration
|
||||||
_ "github.com/XinFinOrg/XDPoSChain/eth/tracers/js"
|
_ "github.com/XinFinOrg/XDPoSChain/eth/tracers/js"
|
||||||
|
_ "github.com/XinFinOrg/XDPoSChain/eth/tracers/live"
|
||||||
_ "github.com/XinFinOrg/XDPoSChain/eth/tracers/native"
|
_ "github.com/XinFinOrg/XDPoSChain/eth/tracers/native"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -121,6 +122,8 @@ var (
|
||||||
//utils.VMEnableDebugFlag,
|
//utils.VMEnableDebugFlag,
|
||||||
utils.Enable0xPrefixFlag,
|
utils.Enable0xPrefixFlag,
|
||||||
utils.EnableXDCPrefixFlag,
|
utils.EnableXDCPrefixFlag,
|
||||||
|
utils.VMTraceFlag,
|
||||||
|
utils.VMTraceConfigFlag,
|
||||||
utils.NetworkIdFlag,
|
utils.NetworkIdFlag,
|
||||||
utils.HTTPCORSDomainFlag,
|
utils.HTTPCORSDomainFlag,
|
||||||
utils.AuthListenFlag,
|
utils.AuthListenFlag,
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm/runtime"
|
"github.com/XinFinOrg/XDPoSChain/core/vm/runtime"
|
||||||
|
|
@ -80,7 +81,7 @@ func runCmd(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
tracer vm.EVMLogger
|
tracer *tracing.Hooks
|
||||||
debugLogger *logger.StructLogger
|
debugLogger *logger.StructLogger
|
||||||
statedb *state.StateDB
|
statedb *state.StateDB
|
||||||
chainConfig *params.ChainConfig
|
chainConfig *params.ChainConfig
|
||||||
|
|
@ -91,7 +92,7 @@ func runCmd(ctx *cli.Context) error {
|
||||||
tracer = logger.NewJSONLogger(logconfig, os.Stdout)
|
tracer = logger.NewJSONLogger(logconfig, os.Stdout)
|
||||||
} else if ctx.Bool(DebugFlag.Name) {
|
} else if ctx.Bool(DebugFlag.Name) {
|
||||||
debugLogger = logger.NewStructLogger(logconfig)
|
debugLogger = logger.NewStructLogger(logconfig)
|
||||||
tracer = debugLogger
|
tracer = debugLogger.Hooks()
|
||||||
} else {
|
} else {
|
||||||
debugLogger = logger.NewStructLogger(logconfig)
|
debugLogger = logger.NewStructLogger(logconfig)
|
||||||
}
|
}
|
||||||
|
|
@ -235,9 +236,7 @@ Gas used: %d
|
||||||
|
|
||||||
`, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC, initialGas-leftOverGas)
|
`, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC, initialGas-leftOverGas)
|
||||||
}
|
}
|
||||||
if tracer != nil {
|
if tracer == nil {
|
||||||
tracer.CaptureEnd(ret, initialGas-leftOverGas, err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("%#x\n", ret)
|
fmt.Printf("%#x\n", ret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf(" error: %v\n", err)
|
fmt.Printf(" error: %v\n", err)
|
||||||
|
|
|
||||||
|
|
@ -57,20 +57,13 @@ func stateTestCmd(ctx *cli.Context) error {
|
||||||
EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name),
|
EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name),
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var cfg vm.Config
|
||||||
tracer vm.EVMLogger
|
|
||||||
debugger *logger.StructLogger
|
|
||||||
)
|
|
||||||
switch {
|
switch {
|
||||||
case ctx.Bool(MachineFlag.Name):
|
case ctx.Bool(MachineFlag.Name):
|
||||||
tracer = logger.NewJSONLogger(config, os.Stderr)
|
cfg.Tracer = logger.NewJSONLogger(config, os.Stderr)
|
||||||
|
|
||||||
case ctx.Bool(DebugFlag.Name):
|
case ctx.Bool(DebugFlag.Name):
|
||||||
debugger = logger.NewStructLogger(config)
|
cfg.Tracer = logger.NewStructLogger(config).Hooks()
|
||||||
tracer = debugger
|
|
||||||
|
|
||||||
default:
|
|
||||||
debugger = logger.NewStructLogger(config)
|
|
||||||
}
|
}
|
||||||
// Load the test content from the input file
|
// Load the test content from the input file
|
||||||
src, err := os.ReadFile(ctx.Args().First())
|
src, err := os.ReadFile(ctx.Args().First())
|
||||||
|
|
@ -82,9 +75,6 @@ func stateTestCmd(ctx *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Iterate over all the tests, run them and aggregate the results
|
// Iterate over all the tests, run them and aggregate the results
|
||||||
cfg := vm.Config{
|
|
||||||
Tracer: tracer,
|
|
||||||
}
|
|
||||||
results := make([]StatetestResult, 0, len(tests))
|
results := make([]StatetestResult, 0, len(tests))
|
||||||
for key, test := range tests {
|
for key, test := range tests {
|
||||||
for _, st := range test.Subtests() {
|
for _, st := range test.Subtests() {
|
||||||
|
|
@ -103,16 +93,7 @@ func stateTestCmd(ctx *cli.Context) error {
|
||||||
if ctx.Bool(MachineFlag.Name) && s != nil {
|
if ctx.Bool(MachineFlag.Name) && s != nil {
|
||||||
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%x\"}\n", s.IntermediateRoot(false))
|
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%x\"}\n", s.IntermediateRoot(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, *result)
|
results = append(results, *result)
|
||||||
|
|
||||||
// Print any structured logs collected
|
|
||||||
if ctx.Bool(DebugFlag.Name) {
|
|
||||||
if debugger != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "#### TRACE ####")
|
|
||||||
logger.WriteTrace(os.Stderr, debugger.StructLogs())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out, _ := json.MarshalIndent(results, "", " ")
|
out, _ := json.MarshalIndent(results, "", " ")
|
||||||
|
|
|
||||||
1
cmd/evm/testdata/31/README.md
vendored
Normal file
1
cmd/evm/testdata/31/README.md
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
This test does some EVM execution, and can be used to test the tracers and trace-outputs.
|
||||||
16
cmd/evm/testdata/31/alloc.json
vendored
Normal file
16
cmd/evm/testdata/31/alloc.json
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||||
|
"balance" : "0x016345785d8a0000",
|
||||||
|
"code" : "0x",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0x1111111111111111111111111111111111111111" : {
|
||||||
|
"balance" : "0x1",
|
||||||
|
"code" : "0x604060406040604000",
|
||||||
|
"nonce" : "0x00",
|
||||||
|
"storage" : {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
cmd/evm/testdata/31/env.json
vendored
Normal file
20
cmd/evm/testdata/31/env.json
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
|
||||||
|
"currentNumber" : "0x01",
|
||||||
|
"currentTimestamp" : "0x03e8",
|
||||||
|
"currentGasLimit" : "0x1000000000",
|
||||||
|
"previousHash" : "0xe4e2a30b340bec696242b67584264f878600dce98354ae0b6328740fd4ff18da",
|
||||||
|
"currentDataGasUsed" : "0x2000",
|
||||||
|
"parentTimestamp" : "0x00",
|
||||||
|
"parentDifficulty" : "0x00",
|
||||||
|
"parentUncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"parentBeaconBlockRoot" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||||
|
"currentRandom" : "0x0000000000000000000000000000000000000000000000000000000000020000",
|
||||||
|
"withdrawals" : [
|
||||||
|
],
|
||||||
|
"parentBaseFee" : "0x08",
|
||||||
|
"parentGasUsed" : "0x00",
|
||||||
|
"parentGasLimit" : "0x1000000000",
|
||||||
|
"parentExcessBlobGas" : "0x1000",
|
||||||
|
"parentBlobGasUsed" : "0x2000"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
"hello world"
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{"pc":0,"op":96,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||||
|
{"pc":2,"op":96,"gas":"0x13495","gasCost":"0x3","memSize":0,"stack":["0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||||
|
{"pc":4,"op":96,"gas":"0x13492","gasCost":"0x3","memSize":0,"stack":["0x40","0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||||
|
{"pc":6,"op":96,"gas":"0x1348f","gasCost":"0x3","memSize":0,"stack":["0x40","0x40","0x40"],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||||
|
{"pc":8,"op":0,"gas":"0x1348c","gasCost":"0x0","memSize":0,"stack":["0x40","0x40","0x40","0x40"],"depth":1,"refund":0,"opName":"STOP"}
|
||||||
|
{"output":"","gasUsed":"0xc"}
|
||||||
14
cmd/evm/testdata/31/txs.json
vendored
Normal file
14
cmd/evm/testdata/31/txs.json
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"gas": "0x186a0",
|
||||||
|
"gasPrice": "0x600",
|
||||||
|
"input": "0x",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"to": "0x1111111111111111111111111111111111111111",
|
||||||
|
"value": "0x1",
|
||||||
|
"v" : "0x0",
|
||||||
|
"r" : "0x0",
|
||||||
|
"s" : "0x0",
|
||||||
|
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -19,6 +19,7 @@ package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -375,8 +376,18 @@ var (
|
||||||
Usage: "Record information useful for VM and contract debugging",
|
Usage: "Record information useful for VM and contract debugging",
|
||||||
Category: flags.VMCategory,
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
|
VMTraceFlag = &cli.StringFlag{
|
||||||
|
Name: "vmtrace",
|
||||||
|
Usage: "Name of tracer which should record internal VM operations (costly)",
|
||||||
|
Category: flags.VMCategory,
|
||||||
|
}
|
||||||
|
VMTraceConfigFlag = &cli.StringFlag{
|
||||||
|
Name: "vmtrace-config",
|
||||||
|
Usage: "Tracer configuration (JSON)",
|
||||||
|
Category: flags.VMCategory,
|
||||||
|
}
|
||||||
|
|
||||||
// API options
|
// API options.
|
||||||
RPCGlobalGasCapFlag = &cli.Uint64Flag{
|
RPCGlobalGasCapFlag = &cli.Uint64Flag{
|
||||||
Name: "rpc-gascap",
|
Name: "rpc-gascap",
|
||||||
Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)",
|
Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)",
|
||||||
|
|
@ -1658,6 +1669,19 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
cfg.GasPrice = big.NewInt(1)
|
cfg.GasPrice = big.NewInt(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VM tracing config.
|
||||||
|
if ctx.IsSet(VMTraceFlag.Name) {
|
||||||
|
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
||||||
|
var config string
|
||||||
|
if ctx.IsSet(VMTraceConfigFlag.Name) {
|
||||||
|
config = ctx.String(VMTraceConfigFlag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.VMTrace = name
|
||||||
|
cfg.VMTraceConfig = config
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterEthService adds an Ethereum client to the stack.
|
// RegisterEthService adds an Ethereum client to the stack.
|
||||||
|
|
@ -1836,10 +1860,25 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (chain *core.B
|
||||||
cache.TrieCleanLimit = ctx.Int(CacheFlag.Name) * ctx.Int(CacheGCFlag.Name) / 100
|
cache.TrieCleanLimit = ctx.Int(CacheFlag.Name) * ctx.Int(CacheGCFlag.Name) / 100
|
||||||
}
|
}
|
||||||
vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)}
|
vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)}
|
||||||
|
if ctx.IsSet(VMTraceFlag.Name) {
|
||||||
|
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
||||||
|
var config json.RawMessage
|
||||||
|
if ctx.IsSet(VMTraceConfigFlag.Name) {
|
||||||
|
config = json.RawMessage(ctx.String(VMTraceConfigFlag.Name))
|
||||||
|
}
|
||||||
|
t, err := tracers.LiveDirectory.New(name, config)
|
||||||
|
if err != nil {
|
||||||
|
Fatalf("Failed to create tracer %q: %v", name, err)
|
||||||
|
}
|
||||||
|
vmcfg.Tracer = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Disable transaction indexing/unindexing by default.
|
||||||
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg)
|
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Can't create BlockChain: %v", err)
|
Fatalf("Can't create BlockChain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return chain, chainDb
|
return chain, chainDb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
|
"github.com/XinFinOrg/XDPoSChain/consensus/misc/eip1559"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
"github.com/XinFinOrg/XDPoSChain/trie"
|
"github.com/XinFinOrg/XDPoSChain/trie"
|
||||||
|
|
@ -448,7 +449,7 @@ var (
|
||||||
// AccumulateRewards credits the coinbase of the given block with the mining
|
// AccumulateRewards credits the coinbase of the given block with the mining
|
||||||
// reward. The total reward consists of the static block reward and rewards for
|
// reward. The total reward consists of the static block reward and rewards for
|
||||||
// included uncles. The coinbase of each uncle block is also rewarded.
|
// included uncles. The coinbase of each uncle block is also rewarded.
|
||||||
func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) {
|
func accumulateRewards(config *params.ChainConfig, stateDB *state.StateDB, header *types.Header, uncles []*types.Header) {
|
||||||
// Select the correct block reward based on chain progression
|
// Select the correct block reward based on chain progression
|
||||||
blockReward := FrontierBlockReward
|
blockReward := FrontierBlockReward
|
||||||
if config.IsByzantium(header.Number) {
|
if config.IsByzantium(header.Number) {
|
||||||
|
|
@ -462,10 +463,10 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header
|
||||||
r.Sub(r, header.Number)
|
r.Sub(r, header.Number)
|
||||||
r.Mul(r, blockReward)
|
r.Mul(r, blockReward)
|
||||||
r.Div(r, big8)
|
r.Div(r, big8)
|
||||||
state.AddBalance(uncle.Coinbase, r)
|
stateDB.AddBalance(uncle.Coinbase, r, tracing.BalanceIncreaseRewardMineUncle)
|
||||||
|
|
||||||
r.Div(blockReward, big32)
|
r.Div(blockReward, big32)
|
||||||
reward.Add(reward, r)
|
reward.Add(reward, r)
|
||||||
}
|
}
|
||||||
state.AddBalance(header.Coinbase, reward)
|
stateDB.AddBalance(header.Coinbase, reward, tracing.BalanceIncreaseRewardMineBlock)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
)
|
)
|
||||||
|
|
@ -40,10 +41,11 @@ var (
|
||||||
// ensure it conforms to DAO hard-fork rules.
|
// ensure it conforms to DAO hard-fork rules.
|
||||||
//
|
//
|
||||||
// DAO hard-fork extension to the header validity:
|
// DAO hard-fork extension to the header validity:
|
||||||
// a) if the node is no-fork, do not accept blocks in the [fork, fork+10) range
|
//
|
||||||
// with the fork specific extra-data set
|
// a) if the node is no-fork, do not accept blocks in the [fork, fork+10) range
|
||||||
// b) if the node is pro-fork, require blocks in the specific range to have the
|
// with the fork specific extra-data set
|
||||||
// unique extra-data set.
|
// b) if the node is pro-fork, require blocks in the specific range to have the
|
||||||
|
// unique extra-data set.
|
||||||
func VerifyDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) error {
|
func VerifyDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) error {
|
||||||
// Short circuit validation if the node doesn't care about the DAO fork
|
// Short circuit validation if the node doesn't care about the DAO fork
|
||||||
if config.DAOForkBlock == nil {
|
if config.DAOForkBlock == nil {
|
||||||
|
|
@ -79,7 +81,7 @@ func ApplyDAOHardFork(statedb *state.StateDB) {
|
||||||
|
|
||||||
// Move every DAO account and extra-balance account funds into the refund contract
|
// Move every DAO account and extra-balance account funds into the refund contract
|
||||||
for _, addr := range params.DAODrainList() {
|
for _, addr := range params.DAODrainList() {
|
||||||
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr))
|
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), tracing.BalanceIncreaseDaoContract)
|
||||||
statedb.SetBalance(addr, new(big.Int))
|
statedb.SetBalance(addr, new(big.Int), tracing.BalanceDecreaseDaoAccount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import (
|
||||||
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
|
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
|
|
@ -211,6 +212,7 @@ type BlockChain struct {
|
||||||
prefetcher Prefetcher // Block state prefetcher interface
|
prefetcher Prefetcher // Block state prefetcher interface
|
||||||
processor Processor // Block transaction processor interface
|
processor Processor // Block transaction processor interface
|
||||||
vmConfig vm.Config
|
vmConfig vm.Config
|
||||||
|
logger *tracing.Hooks
|
||||||
|
|
||||||
IPCEndpoint string
|
IPCEndpoint string
|
||||||
Client bind.ContractBackend // Global ipc client instance.
|
Client bind.ContractBackend // Global ipc client instance.
|
||||||
|
|
@ -259,6 +261,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
||||||
downloadingBlock: lru.NewCache[common.Hash, struct{}](blockCacheLimit),
|
downloadingBlock: lru.NewCache[common.Hash, struct{}](blockCacheLimit),
|
||||||
engine: engine,
|
engine: engine,
|
||||||
vmConfig: vmConfig,
|
vmConfig: vmConfig,
|
||||||
|
logger: vmConfig.Tracer,
|
||||||
badBlocks: lru.NewCache[common.Hash, *types.Header](badBlockLimit),
|
badBlocks: lru.NewCache[common.Hash, *types.Header](badBlockLimit),
|
||||||
blocksHashCache: lru.NewCache[uint64, []common.Hash](blocksHashCacheLimit),
|
blocksHashCache: lru.NewCache[uint64, []common.Hash](blocksHashCacheLimit),
|
||||||
resultTrade: lru.NewCache[common.Hash, interface{}](tradingstate.OrderCacheLimit),
|
resultTrade: lru.NewCache[common.Hash, interface{}](tradingstate.OrderCacheLimit),
|
||||||
|
|
@ -306,6 +309,25 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bc.logger != nil && bc.logger.OnBlockchainInit != nil {
|
||||||
|
bc.logger.OnBlockchainInit(chainConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bc.logger != nil && bc.logger.OnGenesisBlock != nil {
|
||||||
|
if block := bc.CurrentBlock(); block.NumberU64() == 0 {
|
||||||
|
alloc, err := getGenesisState(bc.db, block.Hash())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get genesis state: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if alloc == nil {
|
||||||
|
return nil, fmt.Errorf("live blockchain tracer requires genesis alloc to be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Start future block processor.
|
// Start future block processor.
|
||||||
bc.wg.Add(1)
|
bc.wg.Add(1)
|
||||||
go bc.futureBlocksLoop()
|
go bc.futureBlocksLoop()
|
||||||
|
|
@ -1727,7 +1749,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
}
|
}
|
||||||
// Retrieve the parent block and it's state to execute on top
|
// Retrieve the parent block and it's state to execute on top
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
parent := it.previous()
|
parent := it.previous()
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
||||||
|
|
@ -1737,6 +1758,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, events, coalescedLogs, err
|
||||||
}
|
}
|
||||||
|
statedb.SetLogger(bc.logger)
|
||||||
|
|
||||||
// If we have a followup block, run that against the current state to pre-cache
|
// If we have a followup block, run that against the current state to pre-cache
|
||||||
// transactions and probabilistically some of the account/storage trie nodes.
|
// transactions and probabilistically some of the account/storage trie nodes.
|
||||||
|
|
@ -1745,7 +1767,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
if followup, err := it.peek(); followup != nil && err == nil {
|
if followup, err := it.peek(); followup != nil && err == nil {
|
||||||
go func(start time.Time) {
|
go func(start time.Time) {
|
||||||
throwaway, _ := state.New(parent.Root, bc.stateCache)
|
throwaway, _ := state.New(parent.Root, bc.stateCache)
|
||||||
bc.prefetcher.Prefetch(followup, throwaway, bc.vmConfig, &followupInterrupt)
|
// Disable tracing for prefetcher executions.
|
||||||
|
vmCfg := bc.vmConfig
|
||||||
|
vmCfg.Tracer = nil
|
||||||
|
bc.prefetcher.Prefetch(followup, throwaway, vmCfg, &followupInterrupt)
|
||||||
|
|
||||||
blockPrefetchExecuteTimer.Update(time.Since(start))
|
blockPrefetchExecuteTimer.Update(time.Since(start))
|
||||||
if followupInterrupt.Load() {
|
if followupInterrupt.Load() {
|
||||||
|
|
@ -1755,70 +1780,27 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process block using the parent state as reference point.
|
// The traced section of block import.
|
||||||
t0 := time.Now()
|
res, err := bc.processBlock(block, parent, statedb)
|
||||||
isTIPXDCXReceiver := bc.Config().IsTIPXDCXReceiver(block.Number())
|
|
||||||
tradingState, lendingState, err := bc.processTradingAndLendingStates(isTIPXDCXReceiver, block, parent, statedb)
|
|
||||||
if err != nil {
|
|
||||||
bc.reportBlock(block, nil, err)
|
|
||||||
followupInterrupt.Store(true)
|
|
||||||
return it.index, events, coalescedLogs, err
|
|
||||||
}
|
|
||||||
feeCapacity := state.GetTRC21FeeCapacityFromStateWithCache(parent.Root, statedb)
|
|
||||||
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, tradingState, bc.vmConfig, feeCapacity)
|
|
||||||
t1 := time.Now()
|
|
||||||
if err != nil {
|
|
||||||
bc.reportBlock(block, receipts, err)
|
|
||||||
followupInterrupt.Store(true)
|
|
||||||
return it.index, events, coalescedLogs, err
|
|
||||||
}
|
|
||||||
// Validate the state using the default validator
|
|
||||||
err = bc.validator.ValidateState(block, statedb, receipts, usedGas)
|
|
||||||
if err != nil {
|
|
||||||
bc.reportBlock(block, receipts, err)
|
|
||||||
return it.index, events, coalescedLogs, err
|
|
||||||
}
|
|
||||||
t2 := time.Now()
|
|
||||||
proctime := time.Since(start)
|
|
||||||
|
|
||||||
// Write the block to the chain and get the status.
|
|
||||||
status, err := bc.writeBlockWithState(block, receipts, statedb, tradingState, lendingState)
|
|
||||||
t3 := time.Now()
|
|
||||||
followupInterrupt.Store(true)
|
followupInterrupt.Store(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, events, coalescedLogs, err
|
||||||
}
|
}
|
||||||
|
// Report the import stats before returning the various results
|
||||||
|
stats.processed++
|
||||||
|
stats.usedGas += res.usedGas
|
||||||
|
|
||||||
// Update the metrics subsystem with all the measurements
|
switch res.status {
|
||||||
accountReadTimer.Update(statedb.AccountReads)
|
|
||||||
accountHashTimer.Update(statedb.AccountHashes)
|
|
||||||
accountUpdateTimer.Update(statedb.AccountUpdates)
|
|
||||||
accountCommitTimer.Update(statedb.AccountCommits)
|
|
||||||
|
|
||||||
storageReadTimer.Update(statedb.StorageReads)
|
|
||||||
storageHashTimer.Update(statedb.StorageHashes)
|
|
||||||
storageUpdateTimer.Update(statedb.StorageUpdates)
|
|
||||||
storageCommitTimer.Update(statedb.StorageCommits)
|
|
||||||
|
|
||||||
trieAccess := statedb.AccountReads + statedb.AccountHashes + statedb.AccountUpdates + statedb.AccountCommits
|
|
||||||
trieAccess += statedb.StorageReads + statedb.StorageHashes + statedb.StorageUpdates + statedb.StorageCommits
|
|
||||||
|
|
||||||
blockInsertTimer.UpdateSince(start)
|
|
||||||
blockExecutionTimer.Update(t1.Sub(t0) - trieAccess)
|
|
||||||
blockValidationTimer.Update(t2.Sub(t1))
|
|
||||||
blockWriteTimer.Update(t3.Sub(t2))
|
|
||||||
|
|
||||||
switch status {
|
|
||||||
case CanonStatTy:
|
case CanonStatTy:
|
||||||
log.Debug("Inserted new block from downloader", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()),
|
log.Debug("Inserted new block from downloader", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()),
|
||||||
"txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(start)))
|
"txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
|
|
||||||
coalescedLogs = append(coalescedLogs, logs...)
|
coalescedLogs = append(coalescedLogs, res.logs...)
|
||||||
events = append(events, ChainEvent{block, block.Hash(), logs})
|
events = append(events, ChainEvent{block, block.Hash(), res.logs})
|
||||||
lastCanon = block
|
lastCanon = block
|
||||||
|
|
||||||
// Only count canonical blocks for GC processing time
|
// Only count canonical blocks for GC processing time
|
||||||
bc.gcproc += proctime
|
bc.gcproc += res.procTime
|
||||||
bc.UpdateBlocksHashCache(block)
|
bc.UpdateBlocksHashCache(block)
|
||||||
if bc.chainConfig.IsTIPXDCX(block.Number()) && bc.chainConfig.XDPoS != nil && block.NumberU64() > bc.chainConfig.XDPoS.Epoch {
|
if bc.chainConfig.IsTIPXDCX(block.Number()) && bc.chainConfig.XDPoS != nil && block.NumberU64() > bc.chainConfig.XDPoS.Epoch {
|
||||||
bc.logExchangeData(block)
|
bc.logExchangeData(block)
|
||||||
|
|
@ -1830,9 +1812,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
events = append(events, ChainSideEvent{block})
|
events = append(events, ChainSideEvent{block})
|
||||||
bc.UpdateBlocksHashCache(block)
|
bc.UpdateBlocksHashCache(block)
|
||||||
}
|
}
|
||||||
blockInsertTimer.UpdateSince(start)
|
|
||||||
stats.processed++
|
|
||||||
stats.usedGas += usedGas
|
|
||||||
|
|
||||||
dirty, _ := bc.stateCache.TrieDB().Size()
|
dirty, _ := bc.stateCache.TrieDB().Size()
|
||||||
stats.report(chain, it.index, dirty)
|
stats.report(chain, it.index, dirty)
|
||||||
|
|
@ -1873,6 +1852,96 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
return it.index, events, coalescedLogs, nil
|
return it.index, events, coalescedLogs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// blockProcessingResult is a summary of block processing
|
||||||
|
// used for updating the stats.
|
||||||
|
type blockProcessingResult struct {
|
||||||
|
usedGas uint64
|
||||||
|
procTime time.Duration
|
||||||
|
status WriteStatus
|
||||||
|
logs []*types.Log
|
||||||
|
}
|
||||||
|
|
||||||
|
// processBlock executes and validates the given block. If there was no error
|
||||||
|
// it writes the block and associated state to database.
|
||||||
|
func (bc *BlockChain) processBlock(block *types.Block, parent *types.Header, statedb *state.StateDB) (_ *blockProcessingResult, blockEndErr error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
startTime = time.Now()
|
||||||
|
)
|
||||||
|
// TODO(daniel): implement CurrentFinalBlock() and CurrentSafeBlock(), ref PR #29189
|
||||||
|
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
||||||
|
td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
|
||||||
|
bc.logger.OnBlockStart(tracing.BlockEvent{
|
||||||
|
Block: block,
|
||||||
|
TD: td,
|
||||||
|
// Finalized: bc.CurrentFinalBlock(),
|
||||||
|
// Safe: bc.CurrentSafeBlock(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if bc.logger != nil && bc.logger.OnBlockEnd != nil {
|
||||||
|
defer func() {
|
||||||
|
bc.logger.OnBlockEnd(blockEndErr)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process block using the parent state as reference point.
|
||||||
|
pstart := time.Now()
|
||||||
|
isTIPXDCXReceiver := bc.Config().IsTIPXDCXReceiver(block.Number())
|
||||||
|
tradingState, lendingState, err := bc.processTradingAndLendingStates(isTIPXDCXReceiver, block, parent, statedb)
|
||||||
|
if err != nil {
|
||||||
|
bc.reportBlock(block, nil, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
feeCapacity := state.GetTRC21FeeCapacityFromStateWithCache(parent.Root, statedb)
|
||||||
|
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, tradingState, bc.vmConfig, feeCapacity)
|
||||||
|
if err != nil {
|
||||||
|
bc.reportBlock(block, receipts, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ptime := time.Since(pstart)
|
||||||
|
|
||||||
|
vstart := time.Now()
|
||||||
|
// Validate the state using the default validator
|
||||||
|
err = bc.validator.ValidateState(block, statedb, receipts, usedGas)
|
||||||
|
if err != nil {
|
||||||
|
bc.reportBlock(block, receipts, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
vtime := time.Since(vstart)
|
||||||
|
proctime := time.Since(startTime) // processing + validation
|
||||||
|
|
||||||
|
// Update the metrics touched during block processing and validation
|
||||||
|
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
|
||||||
|
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing)
|
||||||
|
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
|
||||||
|
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation)
|
||||||
|
accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation)
|
||||||
|
storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete(in validation)
|
||||||
|
triehash := statedb.AccountHashes + statedb.StorageHashes // The time spent on tries hashing
|
||||||
|
trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update
|
||||||
|
blockExecutionTimer.Update(ptime - (statedb.AccountReads + statedb.StorageReads)) // The time spent on EVM processing
|
||||||
|
blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation
|
||||||
|
|
||||||
|
// Write the block to the chain and get the status.
|
||||||
|
var (
|
||||||
|
wstart = time.Now()
|
||||||
|
status WriteStatus
|
||||||
|
)
|
||||||
|
status, err = bc.writeBlockWithState(block, receipts, statedb, tradingState, lendingState)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Update the metrics touched during block commit
|
||||||
|
accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
|
||||||
|
storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them
|
||||||
|
|
||||||
|
blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits)
|
||||||
|
elapsed := time.Since(startTime) + 1 // prevent zero division
|
||||||
|
blockInsertTimer.Update(elapsed)
|
||||||
|
|
||||||
|
return &blockProcessingResult{usedGas: usedGas, procTime: proctime, status: status, logs: logs}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// insertSidechain is called when an import batch hits upon a pruned ancestor
|
// insertSidechain is called when an import batch hits upon a pruned ancestor
|
||||||
// error, which happens when a sidechain with a sufficiently old fork-block is
|
// error, which happens when a sidechain with a sufficiently old fork-block is
|
||||||
// found.
|
// found.
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
|
|
@ -123,6 +124,6 @@ func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool {
|
||||||
|
|
||||||
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
|
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
|
||||||
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
|
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
|
||||||
db.SubBalance(sender, amount)
|
db.SubBalance(sender, amount, tracing.BalanceChangeTransfer)
|
||||||
db.AddBalance(recipient, amount)
|
db.AddBalance(recipient, amount, tracing.BalanceChangeTransfer)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||||
|
|
@ -66,6 +67,37 @@ type Genesis struct {
|
||||||
BaseFee *big.Int `json:"baseFeePerGas"`
|
BaseFee *big.Int `json:"baseFeePerGas"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc types.GenesisAlloc, err error) {
|
||||||
|
blob := rawdb.ReadGenesisStateSpec(db, blockhash)
|
||||||
|
if len(blob) != 0 {
|
||||||
|
if err := alloc.UnmarshalJSON(blob); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return alloc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Genesis allocation is missing and there are several possibilities:
|
||||||
|
// the node is legacy which doesn't persist the genesis allocation or
|
||||||
|
// the persisted allocation is just lost.
|
||||||
|
// - supported networks(mainnet, testnets), recover with defined allocations
|
||||||
|
// - private network, can't recover
|
||||||
|
var genesis *Genesis
|
||||||
|
switch blockhash {
|
||||||
|
case params.MainnetGenesisHash:
|
||||||
|
genesis = DefaultGenesisBlock()
|
||||||
|
case params.TestnetGenesisHash:
|
||||||
|
genesis = DefaultTestnetGenesisBlock()
|
||||||
|
case params.DevnetGenesisHash:
|
||||||
|
genesis = DefaultDevnetGenesisBlock()
|
||||||
|
}
|
||||||
|
if genesis != nil {
|
||||||
|
return genesis.Alloc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// field type overrides for gencodec
|
// field type overrides for gencodec
|
||||||
type genesisSpecMarshaling struct {
|
type genesisSpecMarshaling struct {
|
||||||
Nonce math.HexOrDecimal64
|
Nonce math.HexOrDecimal64
|
||||||
|
|
@ -186,7 +218,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
||||||
}
|
}
|
||||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(db))
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(db))
|
||||||
for addr, account := range g.Alloc {
|
for addr, account := range g.Alloc {
|
||||||
statedb.AddBalance(addr, account.Balance)
|
statedb.AddBalance(addr, account.Balance, tracing.BalanceIncreaseGenesisBalance)
|
||||||
statedb.SetCode(addr, account.Code)
|
statedb.SetCode(addr, account.Code)
|
||||||
statedb.SetNonce(addr, account.Nonce)
|
statedb.SetNonce(addr, account.Nonce)
|
||||||
for key, value := range account.Storage {
|
for key, value := range account.Storage {
|
||||||
|
|
|
||||||
|
|
@ -83,3 +83,10 @@ func WriteChainConfig(db ethdb.KeyValueWriter, hash common.Hash, cfg *params.Cha
|
||||||
}
|
}
|
||||||
return db.Put(configKey(hash), data)
|
return db.Put(configKey(hash), data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadGenesisStateSpec retrieves the genesis state specification based on the
|
||||||
|
// given genesis (block-)hash.
|
||||||
|
func ReadGenesisStateSpec(db ethdb.KeyValueReader, blockhash common.Hash) []byte {
|
||||||
|
data, _ := db.Get(genesisStateSpecKey(blockhash))
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,9 @@ var (
|
||||||
// used by old db, now only used for conversion
|
// used by old db, now only used for conversion
|
||||||
oldReceiptsPrefix = []byte("receipts-")
|
oldReceiptsPrefix = []byte("receipts-")
|
||||||
|
|
||||||
preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage
|
preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage
|
||||||
configPrefix = []byte("ethereum-config-") // config prefix for the db
|
configPrefix = []byte("ethereum-config-") // config prefix for the db
|
||||||
|
genesisPrefix = []byte("ethereum-genesis-") // genesis state prefix for the db
|
||||||
|
|
||||||
// Chain index prefixes (use `i` + single byte to avoid mixing data types).
|
// Chain index prefixes (use `i` + single byte to avoid mixing data types).
|
||||||
BloomBitsIndexPrefix = []byte("iB") // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress
|
BloomBitsIndexPrefix = []byte("iB") // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress
|
||||||
|
|
@ -176,3 +177,8 @@ func IsCodeKey(key []byte) (bool, []byte) {
|
||||||
func configKey(hash common.Hash) []byte {
|
func configKey(hash common.Hash) []byte {
|
||||||
return append(configPrefix, hash.Bytes()...)
|
return append(configPrefix, hash.Bytes()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// genesisStateSpecKey = genesisPrefix + hash
|
||||||
|
func genesisStateSpecKey(hash common.Hash) []byte {
|
||||||
|
return append(genesisPrefix, hash.Bytes()...)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
|
|
@ -233,6 +234,9 @@ func (s *stateObject) SetState(db Database, key, value common.Hash) {
|
||||||
key: key,
|
key: key,
|
||||||
prevalue: prev,
|
prevalue: prev,
|
||||||
})
|
})
|
||||||
|
if s.db.logger != nil && s.db.logger.OnStorageChange != nil {
|
||||||
|
s.db.logger.OnStorageChange(s.address, key, prev, value)
|
||||||
|
}
|
||||||
s.setState(key, value)
|
s.setState(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -340,7 +344,7 @@ func (s *stateObject) commitTrie(db Database) (*trie.NodeSet, error) {
|
||||||
|
|
||||||
// AddBalance adds amount to s's balance.
|
// AddBalance adds amount to s's balance.
|
||||||
// It is used to add funds to the destination account of a transfer.
|
// It is used to add funds to the destination account of a transfer.
|
||||||
func (s *stateObject) AddBalance(amount *big.Int) {
|
func (s *stateObject) AddBalance(amount *big.Int, reason tracing.BalanceChangeReason) {
|
||||||
// EIP161: We must check emptiness for the objects such that the account
|
// EIP161: We must check emptiness for the objects such that the account
|
||||||
// clearing (0,0,0 objects) can take effect.
|
// clearing (0,0,0 objects) can take effect.
|
||||||
if amount.Sign() == 0 {
|
if amount.Sign() == 0 {
|
||||||
|
|
@ -349,23 +353,26 @@ func (s *stateObject) AddBalance(amount *big.Int) {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.SetBalance(new(big.Int).Add(s.Balance(), amount))
|
s.SetBalance(new(big.Int).Add(s.Balance(), amount), reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubBalance removes amount from s's balance.
|
// SubBalance removes amount from s's balance.
|
||||||
// It is used to remove funds from the origin account of a transfer.
|
// It is used to remove funds from the origin account of a transfer.
|
||||||
func (s *stateObject) SubBalance(amount *big.Int) {
|
func (s *stateObject) SubBalance(amount *big.Int, reason tracing.BalanceChangeReason) {
|
||||||
if amount.Sign() == 0 {
|
if amount.Sign() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.SetBalance(new(big.Int).Sub(s.Balance(), amount))
|
s.SetBalance(new(big.Int).Sub(s.Balance(), amount), reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) SetBalance(amount *big.Int) {
|
func (s *stateObject) SetBalance(amount *big.Int, reason tracing.BalanceChangeReason) {
|
||||||
s.db.journal.append(balanceChange{
|
s.db.journal.append(balanceChange{
|
||||||
account: &s.address,
|
account: &s.address,
|
||||||
prev: new(big.Int).Set(s.data.Balance),
|
prev: new(big.Int).Set(s.data.Balance),
|
||||||
})
|
})
|
||||||
|
if s.db.logger != nil && s.db.logger.OnBalanceChange != nil {
|
||||||
|
s.db.logger.OnBalanceChange(s.address, s.Balance(), amount, reason)
|
||||||
|
}
|
||||||
s.setBalance(amount)
|
s.setBalance(amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -437,6 +444,9 @@ func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
|
||||||
prevhash: s.CodeHash(),
|
prevhash: s.CodeHash(),
|
||||||
prevcode: prevcode,
|
prevcode: prevcode,
|
||||||
})
|
})
|
||||||
|
if s.db.logger != nil && s.db.logger.OnCodeChange != nil {
|
||||||
|
s.db.logger.OnCodeChange(s.address, common.BytesToHash(s.CodeHash()), prevcode, codeHash, code)
|
||||||
|
}
|
||||||
s.setCode(codeHash, code)
|
s.setCode(codeHash, code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -451,6 +461,9 @@ func (s *stateObject) SetNonce(nonce uint64) {
|
||||||
account: &s.address,
|
account: &s.address,
|
||||||
prev: s.data.Nonce,
|
prev: s.data.Nonce,
|
||||||
})
|
})
|
||||||
|
if s.db.logger != nil && s.db.logger.OnNonceChange != nil {
|
||||||
|
s.db.logger.OnNonceChange(s.address, s.data.Nonce, nonce)
|
||||||
|
}
|
||||||
s.setNonce(nonce)
|
s.setNonce(nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||||
|
|
@ -48,11 +49,11 @@ func TestDump(t *testing.T) {
|
||||||
|
|
||||||
// generate a few entries
|
// generate a few entries
|
||||||
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
|
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
|
||||||
obj1.AddBalance(big.NewInt(22))
|
obj1.AddBalance(big.NewInt(22), tracing.BalanceChangeUnspecified)
|
||||||
obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02}))
|
obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02}))
|
||||||
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
|
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
|
||||||
obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02}))
|
obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02}))
|
||||||
obj3.SetBalance(big.NewInt(44))
|
obj3.SetBalance(big.NewInt(44), tracing.BalanceChangeUnspecified)
|
||||||
|
|
||||||
// write some of them to the trie
|
// write some of them to the trie
|
||||||
s.state.updateStateObject(obj1)
|
s.state.updateStateObject(obj1)
|
||||||
|
|
@ -100,13 +101,13 @@ func TestIterativeDump(t *testing.T) {
|
||||||
|
|
||||||
// generate a few entries
|
// generate a few entries
|
||||||
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
|
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
|
||||||
obj1.AddBalance(big.NewInt(22))
|
obj1.AddBalance(big.NewInt(22), tracing.BalanceChangeUnspecified)
|
||||||
obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02}))
|
obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02}))
|
||||||
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
|
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
|
||||||
obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02}))
|
obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02}))
|
||||||
obj3.SetBalance(big.NewInt(44))
|
obj3.SetBalance(big.NewInt(44), tracing.BalanceChangeUnspecified)
|
||||||
obj4 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x00}))
|
obj4 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x00}))
|
||||||
obj4.AddBalance(big.NewInt(1337))
|
obj4.AddBalance(big.NewInt(1337), tracing.BalanceChangeUnspecified)
|
||||||
|
|
||||||
// write some of them to the trie
|
// write some of them to the trie
|
||||||
s.state.updateStateObject(obj1)
|
s.state.updateStateObject(obj1)
|
||||||
|
|
@ -202,7 +203,7 @@ func TestSnapshot2(t *testing.T) {
|
||||||
|
|
||||||
// db, trie are already non-empty values
|
// db, trie are already non-empty values
|
||||||
so0 := state.getStateObject(stateobjaddr0)
|
so0 := state.getStateObject(stateobjaddr0)
|
||||||
so0.SetBalance(big.NewInt(42))
|
so0.SetBalance(big.NewInt(42), tracing.BalanceChangeUnspecified)
|
||||||
so0.SetNonce(43)
|
so0.SetNonce(43)
|
||||||
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
|
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
|
||||||
so0.selfDestructed = false
|
so0.selfDestructed = false
|
||||||
|
|
@ -214,7 +215,7 @@ func TestSnapshot2(t *testing.T) {
|
||||||
|
|
||||||
// and one with deleted == true
|
// and one with deleted == true
|
||||||
so1 := state.getStateObject(stateobjaddr1)
|
so1 := state.getStateObject(stateobjaddr1)
|
||||||
so1.SetBalance(big.NewInt(52))
|
so1.SetBalance(big.NewInt(52), tracing.BalanceChangeUnspecified)
|
||||||
so1.SetNonce(53)
|
so1.SetNonce(53)
|
||||||
so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'})
|
so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'})
|
||||||
so1.selfDestructed = true
|
so1.selfDestructed = true
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
|
|
@ -45,8 +46,9 @@ type revision struct {
|
||||||
// * Contracts
|
// * Contracts
|
||||||
// * Accounts
|
// * Accounts
|
||||||
type StateDB struct {
|
type StateDB struct {
|
||||||
db Database
|
db Database
|
||||||
trie Trie
|
trie Trie
|
||||||
|
logger *tracing.Hooks
|
||||||
|
|
||||||
// This map holds 'live' objects, which will get modified while processing a state transition.
|
// This map holds 'live' objects, which will get modified while processing a state transition.
|
||||||
stateObjects map[common.Address]*stateObject
|
stateObjects map[common.Address]*stateObject
|
||||||
|
|
@ -130,6 +132,11 @@ func New(root common.Hash, db Database) (*StateDB, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLogger sets the logger for account update hooks.
|
||||||
|
func (s *StateDB) SetLogger(l *tracing.Hooks) {
|
||||||
|
s.logger = l
|
||||||
|
}
|
||||||
|
|
||||||
// setError remembers the first non-nil error it is called with.
|
// setError remembers the first non-nil error it is called with.
|
||||||
func (s *StateDB) setError(err error) {
|
func (s *StateDB) setError(err error) {
|
||||||
if s.dbErr == nil {
|
if s.dbErr == nil {
|
||||||
|
|
@ -169,6 +176,9 @@ func (s *StateDB) AddLog(log *types.Log) {
|
||||||
log.TxHash = s.thash
|
log.TxHash = s.thash
|
||||||
log.TxIndex = uint(s.txIndex)
|
log.TxIndex = uint(s.txIndex)
|
||||||
log.Index = s.logSize
|
log.Index = s.logSize
|
||||||
|
if s.logger != nil && s.logger.OnLog != nil {
|
||||||
|
s.logger.OnLog(log)
|
||||||
|
}
|
||||||
s.logs[s.thash] = append(s.logs[s.thash], log)
|
s.logs[s.thash] = append(s.logs[s.thash], log)
|
||||||
s.logSize++
|
s.logSize++
|
||||||
}
|
}
|
||||||
|
|
@ -366,25 +376,25 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// AddBalance adds amount to the account associated with addr.
|
// AddBalance adds amount to the account associated with addr.
|
||||||
func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) {
|
func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) {
|
||||||
stateObject := s.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
stateObject.AddBalance(amount)
|
stateObject.AddBalance(amount, reason)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubBalance subtracts amount from the account associated with addr.
|
// SubBalance subtracts amount from the account associated with addr.
|
||||||
func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) {
|
func (s *StateDB) SubBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) {
|
||||||
stateObject := s.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
stateObject.SubBalance(amount)
|
stateObject.SubBalance(amount, reason)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) {
|
func (s *StateDB) SetBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) {
|
||||||
stateObject := s.GetOrNewStateObject(addr)
|
stateObject := s.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
stateObject.SetBalance(amount)
|
stateObject.SetBalance(amount, reason)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -434,13 +444,20 @@ func (s *StateDB) SelfDestruct(addr common.Address) {
|
||||||
if stateObject == nil {
|
if stateObject == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var (
|
||||||
|
prev = new(big.Int).Set(stateObject.Balance())
|
||||||
|
n = new(big.Int)
|
||||||
|
)
|
||||||
s.journal.append(selfDestructChange{
|
s.journal.append(selfDestructChange{
|
||||||
account: &addr,
|
account: &addr,
|
||||||
prev: stateObject.selfDestructed,
|
prev: stateObject.selfDestructed,
|
||||||
prevbalance: new(big.Int).Set(stateObject.Balance()),
|
prevbalance: prev,
|
||||||
})
|
})
|
||||||
|
if s.logger != nil && s.logger.OnBalanceChange != nil && prev.Sign() > 0 {
|
||||||
|
s.logger.OnBalanceChange(addr, prev, n, tracing.BalanceDecreaseSelfdestruct)
|
||||||
|
}
|
||||||
stateObject.markSelfdestructed()
|
stateObject.markSelfdestructed()
|
||||||
stateObject.data.Balance = new(big.Int)
|
stateObject.data.Balance = n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) Selfdestruct6780(addr common.Address) {
|
func (s *StateDB) Selfdestruct6780(addr common.Address) {
|
||||||
|
|
@ -763,6 +780,11 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
||||||
// We need to maintain account deletions explicitly (will remain
|
// We need to maintain account deletions explicitly (will remain
|
||||||
// set indefinitely).
|
// set indefinitely).
|
||||||
s.stateObjectsDestruct[obj.address] = struct{}{}
|
s.stateObjectsDestruct[obj.address] = struct{}{}
|
||||||
|
|
||||||
|
// If ether was sent to account post-selfdestruct it is burnt.
|
||||||
|
if bal := obj.Balance(); s.logger != nil && s.logger.OnBalanceChange != nil && obj.selfDestructed && bal.Sign() != 0 {
|
||||||
|
s.logger.OnBalanceChange(obj.address, bal, new(big.Int), tracing.BalanceDecreaseSelfdestructBurn)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
obj.finalise()
|
obj.finalise()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -44,7 +45,7 @@ func TestUpdateLeaks(t *testing.T) {
|
||||||
// Update it with some accounts
|
// Update it with some accounts
|
||||||
for i := byte(0); i < 255; i++ {
|
for i := byte(0); i < 255; i++ {
|
||||||
addr := common.BytesToAddress([]byte{i})
|
addr := common.BytesToAddress([]byte{i})
|
||||||
state.AddBalance(addr, big.NewInt(int64(11*i)))
|
state.AddBalance(addr, big.NewInt(int64(11*i)), tracing.BalanceChangeUnspecified)
|
||||||
state.SetNonce(addr, uint64(42*i))
|
state.SetNonce(addr, uint64(42*i))
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
|
state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
|
||||||
|
|
@ -77,7 +78,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||||
finalState, _ := New(types.EmptyRootHash, NewDatabase(finalDb))
|
finalState, _ := New(types.EmptyRootHash, NewDatabase(finalDb))
|
||||||
|
|
||||||
modify := func(state *StateDB, addr common.Address, i, tweak byte) {
|
modify := func(state *StateDB, addr common.Address, i, tweak byte) {
|
||||||
state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak)))
|
state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak)), tracing.BalanceChangeUnspecified)
|
||||||
state.SetNonce(addr, uint64(42*i+tweak))
|
state.SetNonce(addr, uint64(42*i+tweak))
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{})
|
state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{})
|
||||||
|
|
@ -154,7 +155,7 @@ func TestCopy(t *testing.T) {
|
||||||
|
|
||||||
for i := byte(0); i < 255; i++ {
|
for i := byte(0); i < 255; i++ {
|
||||||
obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||||
obj.AddBalance(big.NewInt(int64(i)))
|
obj.AddBalance(big.NewInt(int64(i)), tracing.BalanceChangeUnspecified)
|
||||||
orig.updateStateObject(obj)
|
orig.updateStateObject(obj)
|
||||||
}
|
}
|
||||||
orig.Finalise(false)
|
orig.Finalise(false)
|
||||||
|
|
@ -171,9 +172,9 @@ func TestCopy(t *testing.T) {
|
||||||
copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||||
ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||||
|
|
||||||
origObj.AddBalance(big.NewInt(2 * int64(i)))
|
origObj.AddBalance(big.NewInt(2*int64(i)), tracing.BalanceChangeUnspecified)
|
||||||
copyObj.AddBalance(big.NewInt(3 * int64(i)))
|
copyObj.AddBalance(big.NewInt(3*int64(i)), tracing.BalanceChangeUnspecified)
|
||||||
ccopyObj.AddBalance(big.NewInt(4 * int64(i)))
|
ccopyObj.AddBalance(big.NewInt(4*int64(i)), tracing.BalanceChangeUnspecified)
|
||||||
|
|
||||||
orig.updateStateObject(origObj)
|
orig.updateStateObject(origObj)
|
||||||
copy.updateStateObject(copyObj)
|
copy.updateStateObject(copyObj)
|
||||||
|
|
@ -253,14 +254,14 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
||||||
{
|
{
|
||||||
name: "SetBalance",
|
name: "SetBalance",
|
||||||
fn: func(a testAction, s *StateDB) {
|
fn: func(a testAction, s *StateDB) {
|
||||||
s.SetBalance(addr, big.NewInt(a.args[0]))
|
s.SetBalance(addr, big.NewInt(a.args[0]), tracing.BalanceChangeUnspecified)
|
||||||
},
|
},
|
||||||
args: make([]int64, 1),
|
args: make([]int64, 1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "AddBalance",
|
name: "AddBalance",
|
||||||
fn: func(a testAction, s *StateDB) {
|
fn: func(a testAction, s *StateDB) {
|
||||||
s.AddBalance(addr, big.NewInt(a.args[0]))
|
s.AddBalance(addr, big.NewInt(a.args[0]), tracing.BalanceChangeUnspecified)
|
||||||
},
|
},
|
||||||
args: make([]int64, 1),
|
args: make([]int64, 1),
|
||||||
},
|
},
|
||||||
|
|
@ -487,7 +488,7 @@ func TestTouchDelete(t *testing.T) {
|
||||||
s.state.Reset(root)
|
s.state.Reset(root)
|
||||||
|
|
||||||
snapshot := s.state.Snapshot()
|
snapshot := s.state.Snapshot()
|
||||||
s.state.AddBalance(common.Address{}, new(big.Int))
|
s.state.AddBalance(common.Address{}, new(big.Int), tracing.BalanceChangeUnspecified)
|
||||||
|
|
||||||
if len(s.state.journal.dirties) != 1 {
|
if len(s.state.journal.dirties) != 1 {
|
||||||
t.Fatal("expected one dirty state object")
|
t.Fatal("expected one dirty state object")
|
||||||
|
|
@ -719,7 +720,7 @@ func TestDeleteCreateRevert(t *testing.T) {
|
||||||
state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
|
|
||||||
addr := common.BytesToAddress([]byte("so"))
|
addr := common.BytesToAddress([]byte("so"))
|
||||||
state.SetBalance(addr, big.NewInt(1))
|
state.SetBalance(addr, big.NewInt(1), tracing.BalanceChangeUnspecified)
|
||||||
|
|
||||||
root, _ := state.Commit(false)
|
root, _ := state.Commit(false)
|
||||||
state.Reset(root)
|
state.Reset(root)
|
||||||
|
|
@ -729,7 +730,7 @@ func TestDeleteCreateRevert(t *testing.T) {
|
||||||
state.Finalise(true)
|
state.Finalise(true)
|
||||||
|
|
||||||
id := state.Snapshot()
|
id := state.Snapshot()
|
||||||
state.SetBalance(addr, big.NewInt(2))
|
state.SetBalance(addr, big.NewInt(2), tracing.BalanceChangeUnspecified)
|
||||||
state.RevertToSnapshot(id)
|
state.RevertToSnapshot(id)
|
||||||
|
|
||||||
// Commit the entire state and make sure we don't crash and have the correct state
|
// Commit the entire state and make sure we don't crash and have the correct state
|
||||||
|
|
@ -746,7 +747,7 @@ func TestDeleteCreateRevert(t *testing.T) {
|
||||||
func TestCopyOfCopy(t *testing.T) {
|
func TestCopyOfCopy(t *testing.T) {
|
||||||
state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()))
|
state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
addr := common.HexToAddress("aaaa")
|
addr := common.HexToAddress("aaaa")
|
||||||
state.SetBalance(addr, big.NewInt(42))
|
state.SetBalance(addr, big.NewInt(42), tracing.BalanceChangeUnspecified)
|
||||||
|
|
||||||
if got := state.Copy().GetBalance(addr).Uint64(); got != 42 {
|
if got := state.Copy().GetBalance(addr).Uint64(); got != 42 {
|
||||||
t.Fatalf("1st copy fail, expected 42, got %v", got)
|
t.Fatalf("1st copy fail, expected 42, got %v", got)
|
||||||
|
|
@ -768,9 +769,9 @@ func TestCopyCommitCopy(t *testing.T) {
|
||||||
skey := common.HexToHash("aaa")
|
skey := common.HexToHash("aaa")
|
||||||
sval := common.HexToHash("bbb")
|
sval := common.HexToHash("bbb")
|
||||||
|
|
||||||
state.SetBalance(addr, big.NewInt(42)) // Change the account trie
|
state.SetBalance(addr, big.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
|
||||||
state.SetCode(addr, []byte("hello")) // Change an external metadata
|
state.SetCode(addr, []byte("hello")) // Change an external metadata
|
||||||
state.SetState(addr, skey, sval) // Change the storage trie
|
state.SetState(addr, skey, sval) // Change the storage trie
|
||||||
|
|
||||||
if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
|
if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
|
||||||
t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
|
t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
|
||||||
|
|
@ -840,9 +841,9 @@ func TestCopyCopyCommitCopy(t *testing.T) {
|
||||||
skey := common.HexToHash("aaa")
|
skey := common.HexToHash("aaa")
|
||||||
sval := common.HexToHash("bbb")
|
sval := common.HexToHash("bbb")
|
||||||
|
|
||||||
state.SetBalance(addr, big.NewInt(42)) // Change the account trie
|
state.SetBalance(addr, big.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
|
||||||
state.SetCode(addr, []byte("hello")) // Change an external metadata
|
state.SetCode(addr, []byte("hello")) // Change an external metadata
|
||||||
state.SetState(addr, skey, sval) // Change the storage trie
|
state.SetState(addr, skey, sval) // Change the storage trie
|
||||||
|
|
||||||
if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
|
if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
|
||||||
t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
|
t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||||
|
|
@ -50,8 +51,8 @@ func makeTestState() (Database, common.Hash, []*testAccount) {
|
||||||
obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||||
acc := &testAccount{address: common.BytesToAddress([]byte{i})}
|
acc := &testAccount{address: common.BytesToAddress([]byte{i})}
|
||||||
|
|
||||||
obj.AddBalance(big.NewInt(int64(11 * i)))
|
obj.AddBalance(big.NewInt(11*int64(i)), tracing.BalanceChangeUnspecified)
|
||||||
acc.balance = big.NewInt(int64(11 * i))
|
acc.balance = big.NewInt(11 * int64(i))
|
||||||
|
|
||||||
obj.SetNonce(uint64(42 * i))
|
obj.SetNonce(uint64(42 * i))
|
||||||
acc.nonce = uint64(42 * i)
|
acc.nonce = uint64(42 * i)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/lru"
|
"github.com/XinFinOrg/XDPoSChain/common/lru"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -146,5 +147,5 @@ func UpdateTRC21Fee(statedb *StateDB, newBalance map[common.Address]*big.Int, to
|
||||||
balanceKey := GetLocMappingAtKey(token.Hash(), slotTokensState)
|
balanceKey := GetLocMappingAtKey(token.Hash(), slotTokensState)
|
||||||
statedb.SetState(common.TRC21IssuerSMC, common.BigToHash(balanceKey), common.BigToHash(value))
|
statedb.SetState(common.TRC21IssuerSMC, common.BigToHash(balanceKey), common.BigToHash(value))
|
||||||
}
|
}
|
||||||
statedb.SubBalance(common.TRC21IssuerSMC, totalFeeUsed)
|
statedb.SubBalance(common.TRC21IssuerSMC, totalFeeUsed, tracing.BalanceChangeUnspecified)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -29,6 +28,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
|
|
@ -66,7 +66,7 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen
|
||||||
// Process returns the receipts and logs accumulated during the process and
|
// Process returns the receipts and logs accumulated during the process and
|
||||||
// returns the amount of gas that was used in the process. If any of the
|
// returns the amount of gas that was used in the process. If any of the
|
||||||
// transactions failed to execute due to insufficient gas it will return an error.
|
// transactions failed to execute due to insufficient gas it will return an error.
|
||||||
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tradingState *tradingstate.TradingStateDB, cfg vm.Config, balanceFee map[common.Address]*big.Int) (types.Receipts, []*types.Log, uint64, error) {
|
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tradingState *tradingstate.TradingStateDB, cfg vm.Config, tokensFee map[common.Address]*big.Int) (types.Receipts, []*types.Log, uint64, error) {
|
||||||
var (
|
var (
|
||||||
receipts types.Receipts
|
receipts types.Receipts
|
||||||
usedGas = new(uint64)
|
usedGas = new(uint64)
|
||||||
|
|
@ -89,6 +89,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra
|
||||||
totalFeeUsed := big.NewInt(0)
|
totalFeeUsed := big.NewInt(0)
|
||||||
blockContext := NewEVMBlockContext(header, p.bc, nil)
|
blockContext := NewEVMBlockContext(header, p.bc, nil)
|
||||||
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg)
|
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg)
|
||||||
|
signer := types.MakeSigner(p.config, blockNumber)
|
||||||
coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil)
|
coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil)
|
||||||
// Iterate over and process the individual transactions
|
// Iterate over and process the individual transactions
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
|
|
@ -117,8 +118,20 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra
|
||||||
return nil, nil, 0, err
|
return nil, nil, 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var balanceFee *big.Int
|
||||||
|
if tx.To() != nil {
|
||||||
|
if value, ok := tokensFee[*tx.To()]; ok {
|
||||||
|
balanceFee = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg, err := TransactionToMessage(tx, signer, balanceFee, blockNumber, header.BaseFee)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(tx.Hash(), i)
|
||||||
receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, header.BaseFee, blockHash, tx, usedGas, vmenv)
|
|
||||||
|
receipt, gas, err, tokenFeeUsed := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, balanceFee, coinbaseOwner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
}
|
}
|
||||||
|
|
@ -126,8 +139,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra
|
||||||
allLogs = append(allLogs, receipt.Logs...)
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
if tokenFeeUsed {
|
if tokenFeeUsed {
|
||||||
fee := common.GetGasFee(block.Header().Number.Uint64(), gas)
|
fee := common.GetGasFee(block.Header().Number.Uint64(), gas)
|
||||||
balanceFee[*tx.To()] = new(big.Int).Sub(balanceFee[*tx.To()], fee)
|
tokensFee[*tx.To()] = new(big.Int).Sub(tokensFee[*tx.To()], fee)
|
||||||
balanceUpdated[*tx.To()] = balanceFee[*tx.To()]
|
balanceUpdated[*tx.To()] = tokensFee[*tx.To()]
|
||||||
totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee)
|
totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -137,7 +150,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra
|
||||||
return receipts, allLogs, *usedGas, nil
|
return receipts, allLogs, *usedGas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, statedb *state.StateDB, tradingState *tradingstate.TradingStateDB, cfg vm.Config, balanceFee map[common.Address]*big.Int) (types.Receipts, []*types.Log, uint64, error) {
|
func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, statedb *state.StateDB, tradingState *tradingstate.TradingStateDB, cfg vm.Config, tokensFee map[common.Address]*big.Int) (types.Receipts, []*types.Log, uint64, error) {
|
||||||
block := cBlock.block
|
block := cBlock.block
|
||||||
var (
|
var (
|
||||||
receipts types.Receipts
|
receipts types.Receipts
|
||||||
|
|
@ -168,6 +181,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
|
||||||
}
|
}
|
||||||
blockContext := NewEVMBlockContext(header, p.bc, nil)
|
blockContext := NewEVMBlockContext(header, p.bc, nil)
|
||||||
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg)
|
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, tradingState, p.config, cfg)
|
||||||
|
signer := types.MakeSigner(p.config, blockNumber)
|
||||||
coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil)
|
coinbaseOwner := getCoinbaseOwner(p.bc, statedb, header, nil)
|
||||||
// Iterate over and process the individual transactions
|
// Iterate over and process the individual transactions
|
||||||
receipts = make([]*types.Receipt, block.Transactions().Len())
|
receipts = make([]*types.Receipt, block.Transactions().Len())
|
||||||
|
|
@ -197,8 +211,18 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
|
||||||
return nil, nil, 0, err
|
return nil, nil, 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var balanceFee *big.Int
|
||||||
|
if tx.To() != nil {
|
||||||
|
if value, ok := tokensFee[*tx.To()]; ok {
|
||||||
|
balanceFee = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg, err := TransactionToMessage(tx, signer, balanceFee, blockNumber, header.BaseFee)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(tx.Hash(), i)
|
||||||
receipt, gas, err, tokenFeeUsed := applyTransaction(p.config, balanceFee, gp, statedb, coinbaseOwner, blockNumber, header.BaseFee, blockHash, tx, usedGas, vmenv)
|
receipt, gas, err, tokenFeeUsed := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, balanceFee, coinbaseOwner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, err
|
return nil, nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
@ -209,8 +233,8 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
|
||||||
allLogs = append(allLogs, receipt.Logs...)
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
if tokenFeeUsed {
|
if tokenFeeUsed {
|
||||||
fee := common.GetGasFee(block.Header().Number.Uint64(), gas)
|
fee := common.GetGasFee(block.Header().Number.Uint64(), gas)
|
||||||
balanceFee[*tx.To()] = new(big.Int).Sub(balanceFee[*tx.To()], fee)
|
tokensFee[*tx.To()] = new(big.Int).Sub(tokensFee[*tx.To()], fee)
|
||||||
balanceUpdated[*tx.To()] = balanceFee[*tx.To()]
|
balanceUpdated[*tx.To()] = tokensFee[*tx.To()]
|
||||||
totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee)
|
totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -220,7 +244,10 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
|
||||||
return receipts, allLogs, *usedGas, nil
|
return receipts, allLogs, *usedGas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, gp *GasPool, statedb *state.StateDB, coinbaseOwner common.Address, blockNumber, baseFee *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, uint64, error, bool) {
|
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
|
||||||
|
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
||||||
|
// this method takes an already created EVM instance as input.
|
||||||
|
func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, balanceFee *big.Int, coinbaseOwner common.Address) (receipt *types.Receipt, gasUsed uint64, err error, tokenFeeUsed bool) {
|
||||||
to := tx.To()
|
to := tx.To()
|
||||||
if to != nil {
|
if to != nil {
|
||||||
if *to == common.BlockSignersBinary && config.IsTIPSigning(blockNumber) {
|
if *to == common.BlockSignersBinary && config.IsTIPSigning(blockNumber) {
|
||||||
|
|
@ -240,17 +267,14 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
||||||
return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas)
|
return ApplyEmptyTransaction(config, statedb, blockNumber, blockHash, tx, usedGas)
|
||||||
}
|
}
|
||||||
|
|
||||||
var balanceFee *big.Int
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil {
|
||||||
if to != nil {
|
evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
if value, ok := tokensFee[*to]; ok {
|
if evm.Config.Tracer.OnTxEnd != nil {
|
||||||
balanceFee = value
|
defer func() {
|
||||||
|
evm.Config.Tracer.OnTxEnd(receipt, err)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
msg, err := TransactionToMessage(tx, types.MakeSigner(config, blockNumber), balanceFee, blockNumber, baseFee)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new context to be used in the EVM environment
|
// Create a new context to be used in the EVM environment
|
||||||
txContext := NewEVMTxContext(msg)
|
txContext := NewEVMTxContext(msg)
|
||||||
|
|
||||||
|
|
@ -395,7 +419,7 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
||||||
hBalance.SetString(bal+"000000000000000000", 10)
|
hBalance.SetString(bal+"000000000000000000", 10)
|
||||||
log.Info("address", addr, "with_balance", bal, "XDC")
|
log.Info("address", addr, "with_balance", bal, "XDC")
|
||||||
addrBin := common.HexToAddress(addr)
|
addrBin := common.HexToAddress(addr)
|
||||||
statedb.SetBalance(addrBin, hBalance)
|
statedb.SetBalance(addrBin, hBalance, tracing.BalanceChangeUnspecified)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -403,7 +427,6 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
||||||
|
|
||||||
// Apply the transaction to the current state (included in the env)
|
// Apply the transaction to the current state (included in the env)
|
||||||
result, err := ApplyMessage(evm, msg, gp, coinbaseOwner)
|
result, err := ApplyMessage(evm, msg, gp, coinbaseOwner)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err, false
|
return nil, 0, err, false
|
||||||
}
|
}
|
||||||
|
|
@ -419,7 +442,7 @@ func applyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*
|
||||||
|
|
||||||
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
||||||
// by the tx.
|
// by the tx.
|
||||||
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas}
|
receipt = &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas}
|
||||||
if result.Failed() {
|
if result.Failed() {
|
||||||
receipt.Status = types.ReceiptStatusFailed
|
receipt.Status = types.ReceiptStatusFailed
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -461,11 +484,23 @@ func getCoinbaseOwner(bc *BlockChain, statedb *state.StateDB, header *types.Head
|
||||||
// for the transaction, gas used and an error if the transaction failed,
|
// for the transaction, gas used and an error if the transaction failed,
|
||||||
// indicating the block was invalid.
|
// indicating the block was invalid.
|
||||||
func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error, bool) {
|
func ApplyTransaction(config *params.ChainConfig, tokensFee map[common.Address]*big.Int, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, XDCxState *tradingstate.TradingStateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error, bool) {
|
||||||
|
var balanceFee *big.Int
|
||||||
|
if tx.To() != nil {
|
||||||
|
if value, ok := tokensFee[*tx.To()]; ok {
|
||||||
|
balanceFee = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create a new context to be used in the EVM environment
|
// Create a new context to be used in the EVM environment
|
||||||
blockContext := NewEVMBlockContext(header, bc, author)
|
blockContext := NewEVMBlockContext(header, bc, author)
|
||||||
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, XDCxState, config, cfg)
|
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, XDCxState, config, cfg)
|
||||||
|
signer := types.MakeSigner(config, header.Number)
|
||||||
|
msg, err := TransactionToMessage(tx, signer, balanceFee, header.Number, header.BaseFee)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err, false
|
||||||
|
}
|
||||||
coinbaseOwner := getCoinbaseOwner(bc, statedb, header, author)
|
coinbaseOwner := getCoinbaseOwner(bc, statedb, header, author)
|
||||||
return applyTransaction(config, tokensFee, gp, statedb, coinbaseOwner, header.Number, header.BaseFee, header.Hash(), tx, usedGas, vmenv)
|
return ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv, balanceFee, coinbaseOwner)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error, bool) {
|
func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error, bool) {
|
||||||
|
|
|
||||||
|
|
@ -233,7 +233,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||||
txs: []*types.Transaction{
|
txs: []*types.Transaction{
|
||||||
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
|
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
|
||||||
},
|
},
|
||||||
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: transaction type not supported",
|
want: "transaction type not supported",
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
block := GenerateBadBlock(t, genesis, ethash.NewFaker(), tt.txs, gspec.Config)
|
block := GenerateBadBlock(t, genesis, ethash.NewFaker(), tt.txs, gspec.Config)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
|
@ -265,11 +266,15 @@ func (st *StateTransition) buyGas() error {
|
||||||
if err := st.gp.SubGas(st.msg.GasLimit); err != nil {
|
if err := st.gp.SubGas(st.msg.GasLimit); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil {
|
||||||
|
st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, tracing.GasChangeTxInitialBalance)
|
||||||
|
}
|
||||||
st.gasRemaining += st.msg.GasLimit
|
st.gasRemaining += st.msg.GasLimit
|
||||||
|
|
||||||
st.initialGas = st.msg.GasLimit
|
st.initialGas = st.msg.GasLimit
|
||||||
if st.msg.BalanceTokenFee == nil {
|
if st.msg.BalanceTokenFee == nil {
|
||||||
st.state.SubBalance(st.msg.From, mgval)
|
st.state.SubBalance(st.msg.From, mgval, tracing.BalanceDecreaseGasBuy)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -354,13 +359,6 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if tracer := st.evm.Config.Tracer; tracer != nil {
|
|
||||||
st.evm.Config.Tracer.CaptureTxStart(st.initialGas)
|
|
||||||
defer func() {
|
|
||||||
st.evm.Config.Tracer.CaptureTxEnd(st.gasRemaining)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
msg = st.msg
|
msg = st.msg
|
||||||
sender = vm.AccountRef(st.from())
|
sender = vm.AccountRef(st.from())
|
||||||
|
|
@ -376,6 +374,9 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult,
|
||||||
if st.gasRemaining < gas {
|
if st.gasRemaining < gas {
|
||||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas)
|
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas)
|
||||||
}
|
}
|
||||||
|
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
|
||||||
|
t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, tracing.GasChangeTxIntrinsicGas)
|
||||||
|
}
|
||||||
st.gasRemaining -= gas
|
st.gasRemaining -= gas
|
||||||
|
|
||||||
// Check whether the init code size has been exceeded.
|
// Check whether the init code size has been exceeded.
|
||||||
|
|
@ -418,7 +419,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult,
|
||||||
|
|
||||||
if st.evm.Context.BlockNumber.Cmp(common.TIPTRC21Fee) > 0 {
|
if st.evm.Context.BlockNumber.Cmp(common.TIPTRC21Fee) > 0 {
|
||||||
if (owner != common.Address{}) {
|
if (owner != common.Address{}) {
|
||||||
st.state.AddBalance(owner, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), msg.GasPrice))
|
st.state.AddBalance(owner, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), msg.GasPrice), tracing.BalanceIncreaseRewardTransactionFee)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
effectiveTip := msg.GasPrice
|
effectiveTip := msg.GasPrice
|
||||||
|
|
@ -428,7 +429,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (*ExecutionResult,
|
||||||
effectiveTip = msg.GasTipCap
|
effectiveTip = msg.GasTipCap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip))
|
st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip), tracing.BalanceIncreaseRewardTransactionFee)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ExecutionResult{
|
return &ExecutionResult{
|
||||||
|
|
@ -444,13 +445,23 @@ func (st *StateTransition) refundGas(refundQuotient uint64) {
|
||||||
if refund > st.state.GetRefund() {
|
if refund > st.state.GetRefund() {
|
||||||
refund = st.state.GetRefund()
|
refund = st.state.GetRefund()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && refund > 0 {
|
||||||
|
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, tracing.GasChangeTxRefunds)
|
||||||
|
}
|
||||||
|
|
||||||
st.gasRemaining += refund
|
st.gasRemaining += refund
|
||||||
|
|
||||||
if st.msg.BalanceTokenFee == nil {
|
if st.msg.BalanceTokenFee == nil {
|
||||||
// Return ETH for remaining gas, exchanged at the original rate.
|
// Return ETH for remaining gas, exchanged at the original rate.
|
||||||
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice)
|
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice)
|
||||||
st.state.AddBalance(st.from(), remaining)
|
st.state.AddBalance(st.from(), remaining, tracing.BalanceIncreaseGasReturn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 {
|
||||||
|
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, tracing.GasChangeTxLeftOverReturned)
|
||||||
|
}
|
||||||
|
|
||||||
// Also return remaining gas to the block gas counter so it is
|
// Also return remaining gas to the block gas counter so it is
|
||||||
// available for the next transaction.
|
// available for the next transaction.
|
||||||
st.gp.AddGas(st.gasRemaining)
|
st.gp.AddGas(st.gasRemaining)
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
"github.com/XinFinOrg/XDPoSChain/contracts/XDCx/contract"
|
"github.com/XinFinOrg/XDPoSChain/contracts/XDCx/contract"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
)
|
)
|
||||||
|
|
@ -57,7 +58,7 @@ func RunContract(chain consensus.ChainContext, statedb *state.StateDB, contractA
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
fakeCaller := common.HexToAddress("0x0000000000000000000000000000000000000001")
|
fakeCaller := common.HexToAddress("0x0000000000000000000000000000000000000001")
|
||||||
statedb.SetBalance(fakeCaller, common.BasePrice)
|
statedb.SetBalance(fakeCaller, common.BasePrice, tracing.BalanceChangeUnspecified)
|
||||||
msg := ethereum.CallMsg{To: &contractAddr, Data: input, From: fakeCaller}
|
msg := ethereum.CallMsg{To: &contractAddr, Data: input, From: fakeCaller}
|
||||||
result, err := CallContractWithState(msg, chain, statedb)
|
result, err := CallContractWithState(msg, chain, statedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
275
core/tracing/hooks.go
Normal file
275
core/tracing/hooks.go
Normal file
|
|
@ -0,0 +1,275 @@
|
||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package tracing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OpContext provides the context at which the opcode is being
|
||||||
|
// executed in, including the memory, stack and various contract-level information.
|
||||||
|
type OpContext interface {
|
||||||
|
MemoryData() []byte
|
||||||
|
StackData() []uint256.Int
|
||||||
|
Caller() common.Address
|
||||||
|
Address() common.Address
|
||||||
|
CallValue() *big.Int
|
||||||
|
CallInput() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateDB gives tracers access to the whole state.
|
||||||
|
type StateDB interface {
|
||||||
|
GetBalance(common.Address) *big.Int
|
||||||
|
GetNonce(common.Address) uint64
|
||||||
|
GetCode(common.Address) []byte
|
||||||
|
GetState(common.Address, common.Hash) common.Hash
|
||||||
|
Exist(common.Address) bool
|
||||||
|
GetRefund() uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// VMContext provides the context for the EVM execution.
|
||||||
|
type VMContext struct {
|
||||||
|
Coinbase common.Address
|
||||||
|
BlockNumber *big.Int
|
||||||
|
Time uint64
|
||||||
|
Random *common.Hash
|
||||||
|
// Effective tx gas price
|
||||||
|
GasPrice *big.Int
|
||||||
|
ChainConfig *params.ChainConfig
|
||||||
|
StateDB StateDB
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockEvent is emitted upon tracing an incoming block.
|
||||||
|
// It contains the block as well as consensus related information.
|
||||||
|
type BlockEvent struct {
|
||||||
|
Block *types.Block
|
||||||
|
TD *big.Int
|
||||||
|
Finalized *types.Header
|
||||||
|
Safe *types.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
/*
|
||||||
|
- VM events -
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TxStartHook is called before the execution of a transaction starts.
|
||||||
|
// Call simulations don't come with a valid signature. `from` field
|
||||||
|
// to be used for address of the caller.
|
||||||
|
TxStartHook = func(vm *VMContext, tx *types.Transaction, from common.Address)
|
||||||
|
|
||||||
|
// TxEndHook is called after the execution of a transaction ends.
|
||||||
|
TxEndHook = func(receipt *types.Receipt, err error)
|
||||||
|
|
||||||
|
// EnterHook is invoked when the processing of a message starts.
|
||||||
|
EnterHook = func(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
|
||||||
|
|
||||||
|
// ExitHook is invoked when the processing of a message ends.
|
||||||
|
// `revert` is true when there was an error during the execution.
|
||||||
|
// Exceptionally, before the homestead hardfork a contract creation that
|
||||||
|
// ran out of gas when attempting to persist the code to database did not
|
||||||
|
// count as a call failure and did not cause a revert of the call. This will
|
||||||
|
// be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`.
|
||||||
|
ExitHook = func(depth int, output []byte, gasUsed uint64, err error, reverted bool)
|
||||||
|
|
||||||
|
// OpcodeHook is invoked just prior to the execution of an opcode.
|
||||||
|
OpcodeHook = func(pc uint64, op byte, gas, cost uint64, scope OpContext, rData []byte, depth int, err error)
|
||||||
|
|
||||||
|
// FaultHook is invoked when an error occurs during the execution of an opcode.
|
||||||
|
FaultHook = func(pc uint64, op byte, gas, cost uint64, scope OpContext, depth int, err error)
|
||||||
|
|
||||||
|
// GasChangeHook is invoked when the gas changes.
|
||||||
|
GasChangeHook = func(old, new uint64, reason GasChangeReason)
|
||||||
|
|
||||||
|
/*
|
||||||
|
- Chain events -
|
||||||
|
*/
|
||||||
|
|
||||||
|
// BlockchainInitHook is called when the blockchain is initialized.
|
||||||
|
BlockchainInitHook = func(chainConfig *params.ChainConfig)
|
||||||
|
|
||||||
|
// BlockStartHook is called before executing `block`.
|
||||||
|
// `td` is the total difficulty prior to `block`.
|
||||||
|
BlockStartHook = func(event BlockEvent)
|
||||||
|
|
||||||
|
// BlockEndHook is called after executing a block.
|
||||||
|
BlockEndHook = func(err error)
|
||||||
|
|
||||||
|
// SkippedBlockHook indicates a block was skipped during processing
|
||||||
|
// due to it being known previously. This can happen e.g. when recovering
|
||||||
|
// from a crash.
|
||||||
|
SkippedBlockHook = func(event BlockEvent)
|
||||||
|
|
||||||
|
// GenesisBlockHook is called when the genesis block is being processed.
|
||||||
|
GenesisBlockHook = func(genesis *types.Block, alloc types.GenesisAlloc)
|
||||||
|
|
||||||
|
/*
|
||||||
|
- State events -
|
||||||
|
*/
|
||||||
|
|
||||||
|
// BalanceChangeHook is called when the balance of an account changes.
|
||||||
|
BalanceChangeHook = func(addr common.Address, prev, new *big.Int, reason BalanceChangeReason)
|
||||||
|
|
||||||
|
// NonceChangeHook is called when the nonce of an account changes.
|
||||||
|
NonceChangeHook = func(addr common.Address, prev, new uint64)
|
||||||
|
|
||||||
|
// CodeChangeHook is called when the code of an account changes.
|
||||||
|
CodeChangeHook = func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte)
|
||||||
|
|
||||||
|
// StorageChangeHook is called when the storage of an account changes.
|
||||||
|
StorageChangeHook = func(addr common.Address, slot common.Hash, prev, new common.Hash)
|
||||||
|
|
||||||
|
// LogHook is called when a log is emitted.
|
||||||
|
LogHook = func(log *types.Log)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Hooks struct {
|
||||||
|
// VM events
|
||||||
|
OnTxStart TxStartHook
|
||||||
|
OnTxEnd TxEndHook
|
||||||
|
OnEnter EnterHook
|
||||||
|
OnExit ExitHook
|
||||||
|
OnOpcode OpcodeHook
|
||||||
|
OnFault FaultHook
|
||||||
|
OnGasChange GasChangeHook
|
||||||
|
// Chain events
|
||||||
|
OnBlockchainInit BlockchainInitHook
|
||||||
|
OnBlockStart BlockStartHook
|
||||||
|
OnBlockEnd BlockEndHook
|
||||||
|
OnSkippedBlock SkippedBlockHook
|
||||||
|
OnGenesisBlock GenesisBlockHook
|
||||||
|
// State events
|
||||||
|
OnBalanceChange BalanceChangeHook
|
||||||
|
OnNonceChange NonceChangeHook
|
||||||
|
OnCodeChange CodeChangeHook
|
||||||
|
OnStorageChange StorageChangeHook
|
||||||
|
OnLog LogHook
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceChangeReason is used to indicate the reason for a balance change, useful
|
||||||
|
// for tracing and reporting.
|
||||||
|
type BalanceChangeReason byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
BalanceChangeUnspecified BalanceChangeReason = 0
|
||||||
|
|
||||||
|
// Issuance
|
||||||
|
// BalanceIncreaseRewardMineUncle is a reward for mining an uncle block.
|
||||||
|
BalanceIncreaseRewardMineUncle BalanceChangeReason = 1
|
||||||
|
// BalanceIncreaseRewardMineBlock is a reward for mining a block.
|
||||||
|
BalanceIncreaseRewardMineBlock BalanceChangeReason = 2
|
||||||
|
// BalanceIncreaseWithdrawal is ether withdrawn from the beacon chain.
|
||||||
|
BalanceIncreaseWithdrawal BalanceChangeReason = 3
|
||||||
|
// BalanceIncreaseGenesisBalance is ether allocated at the genesis block.
|
||||||
|
BalanceIncreaseGenesisBalance BalanceChangeReason = 4
|
||||||
|
|
||||||
|
// Transaction fees
|
||||||
|
// BalanceIncreaseRewardTransactionFee is the transaction tip increasing block builder's balance.
|
||||||
|
BalanceIncreaseRewardTransactionFee BalanceChangeReason = 5
|
||||||
|
// BalanceDecreaseGasBuy is spent to purchase gas for execution a transaction.
|
||||||
|
// Part of this gas will be burnt as per EIP-1559 rules.
|
||||||
|
BalanceDecreaseGasBuy BalanceChangeReason = 6
|
||||||
|
// BalanceIncreaseGasReturn is ether returned for unused gas at the end of execution.
|
||||||
|
BalanceIncreaseGasReturn BalanceChangeReason = 7
|
||||||
|
|
||||||
|
// DAO fork
|
||||||
|
// BalanceIncreaseDaoContract is ether sent to the DAO refund contract.
|
||||||
|
BalanceIncreaseDaoContract BalanceChangeReason = 8
|
||||||
|
// BalanceDecreaseDaoAccount is ether taken from a DAO account to be moved to the refund contract.
|
||||||
|
BalanceDecreaseDaoAccount BalanceChangeReason = 9
|
||||||
|
|
||||||
|
// BalanceChangeTransfer is ether transferred via a call.
|
||||||
|
// it is a decrease for the sender and an increase for the recipient.
|
||||||
|
BalanceChangeTransfer BalanceChangeReason = 10
|
||||||
|
// BalanceChangeTouchAccount is a transfer of zero value. It is only there to
|
||||||
|
// touch-create an account.
|
||||||
|
BalanceChangeTouchAccount BalanceChangeReason = 11
|
||||||
|
|
||||||
|
// BalanceIncreaseSelfdestruct is added to the recipient as indicated by a selfdestructing account.
|
||||||
|
BalanceIncreaseSelfdestruct BalanceChangeReason = 12
|
||||||
|
// BalanceDecreaseSelfdestruct is deducted from a contract due to self-destruct.
|
||||||
|
BalanceDecreaseSelfdestruct BalanceChangeReason = 13
|
||||||
|
// BalanceDecreaseSelfdestructBurn is ether that is sent to an already self-destructed
|
||||||
|
// account within the same tx (captured at end of tx).
|
||||||
|
// Note it doesn't account for a self-destruct which appoints itself as recipient.
|
||||||
|
BalanceDecreaseSelfdestructBurn BalanceChangeReason = 14
|
||||||
|
)
|
||||||
|
|
||||||
|
// GasChangeReason is used to indicate the reason for a gas change, useful
|
||||||
|
// for tracing and reporting.
|
||||||
|
//
|
||||||
|
// There is essentially two types of gas changes, those that can be emitted once per transaction
|
||||||
|
// and those that can be emitted on a call basis, so possibly multiple times per transaction.
|
||||||
|
//
|
||||||
|
// They can be recognized easily by their name, those that start with `GasChangeTx` are emitted
|
||||||
|
// once per transaction, while those that start with `GasChangeCall` are emitted on a call basis.
|
||||||
|
type GasChangeReason byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
GasChangeUnspecified GasChangeReason = 0
|
||||||
|
|
||||||
|
// GasChangeTxInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only
|
||||||
|
// one such gas change per transaction.
|
||||||
|
GasChangeTxInitialBalance GasChangeReason = 1
|
||||||
|
// GasChangeTxIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is
|
||||||
|
// always exactly one of those per transaction.
|
||||||
|
GasChangeTxIntrinsicGas GasChangeReason = 2
|
||||||
|
// GasChangeTxRefunds is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared)
|
||||||
|
// this generates an increase in gas. There is at most one of such gas change per transaction.
|
||||||
|
GasChangeTxRefunds GasChangeReason = 3
|
||||||
|
// GasChangeTxLeftOverReturned is the amount of gas left over at the end of transaction's execution that will be returned
|
||||||
|
// to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas
|
||||||
|
// left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller.
|
||||||
|
// There is at most one of such gas change per transaction.
|
||||||
|
GasChangeTxLeftOverReturned GasChangeReason = 4
|
||||||
|
|
||||||
|
// GasChangeCallInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only
|
||||||
|
// one such gas change per call.
|
||||||
|
GasChangeCallInitialBalance GasChangeReason = 5
|
||||||
|
// GasChangeCallLeftOverReturned is the amount of gas left over that will be returned to the caller, this change will always
|
||||||
|
// be a negative change as we "drain" left over gas towards 0. If there was no gas left at the end of execution, no such even
|
||||||
|
// will be emitted.
|
||||||
|
GasChangeCallLeftOverReturned GasChangeReason = 6
|
||||||
|
// GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the call after the child call execution it
|
||||||
|
// executed completed. This value is always positive as we are giving gas back to the you, the left over gas of the child.
|
||||||
|
// If there was no gas left to be refunded, no such even will be emitted.
|
||||||
|
GasChangeCallLeftOverRefunded GasChangeReason = 7
|
||||||
|
// GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE.
|
||||||
|
GasChangeCallContractCreation GasChangeReason = 8
|
||||||
|
// GasChangeContractCreation is the amount of gas that will be burned for a CREATE2.
|
||||||
|
GasChangeCallContractCreation2 GasChangeReason = 9
|
||||||
|
// GasChangeCallCodeStorage is the amount of gas that will be charged for code storage.
|
||||||
|
GasChangeCallCodeStorage GasChangeReason = 10
|
||||||
|
// GasChangeCallOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was
|
||||||
|
// performed can be check by `OnOpcode` handling.
|
||||||
|
GasChangeCallOpCode GasChangeReason = 11
|
||||||
|
// GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution.
|
||||||
|
GasChangeCallPrecompiledContract GasChangeReason = 12
|
||||||
|
// GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules.
|
||||||
|
GasChangeCallStorageColdAccess GasChangeReason = 13
|
||||||
|
// GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert.
|
||||||
|
GasChangeCallFailedExecution GasChangeReason = 14
|
||||||
|
|
||||||
|
// GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as
|
||||||
|
// it will be "manually" tracked by a direct emit of the gas change event.
|
||||||
|
GasChangeIgnored GasChangeReason = 0xFF
|
||||||
|
)
|
||||||
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/event"
|
"github.com/XinFinOrg/XDPoSChain/event"
|
||||||
|
|
@ -229,7 +230,7 @@ func (c *testChain) State() (*state.StateDB, error) {
|
||||||
c.statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(db))
|
c.statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(db))
|
||||||
// simulate that the new head block included tx0 and tx1
|
// simulate that the new head block included tx0 and tx1
|
||||||
c.statedb.SetNonce(c.address, 2)
|
c.statedb.SetNonce(c.address, 2)
|
||||||
c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether))
|
c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether), tracing.BalanceChangeUnspecified)
|
||||||
*c.trigger = false
|
*c.trigger = false
|
||||||
}
|
}
|
||||||
return stdb, nil
|
return stdb, nil
|
||||||
|
|
@ -250,7 +251,7 @@ func TestStateChangeDuringReset(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
// setup pool with 2 transaction in it
|
// setup pool with 2 transaction in it
|
||||||
statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether))
|
statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether), tracing.BalanceChangeUnspecified)
|
||||||
blockchain := &testChain{&testBlockChain{statedb, 1000000000, new(event.Feed)}, address, &trigger}
|
blockchain := &testChain{&testBlockChain{statedb, 1000000000, new(event.Feed)}, address, &trigger}
|
||||||
|
|
||||||
tx0 := transaction(0, 100000, key)
|
tx0 := transaction(0, 100000, key)
|
||||||
|
|
@ -283,7 +284,7 @@ func TestStateChangeDuringReset(t *testing.T) {
|
||||||
|
|
||||||
func testAddBalance(pool *TxPool, addr common.Address, amount *big.Int) {
|
func testAddBalance(pool *TxPool, addr common.Address, amount *big.Int) {
|
||||||
pool.mu.Lock()
|
pool.mu.Lock()
|
||||||
pool.currentState.AddBalance(addr, amount)
|
pool.currentState.AddBalance(addr, amount, tracing.BalanceChangeUnspecified)
|
||||||
pool.mu.Unlock()
|
pool.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -443,9 +444,8 @@ func TestChainFork(t *testing.T) {
|
||||||
|
|
||||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
resetState := func() {
|
resetState := func() {
|
||||||
db := rawdb.NewMemoryDatabase()
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(db))
|
statedb.AddBalance(addr, big.NewInt(100000000000000), tracing.BalanceChangeUnspecified)
|
||||||
statedb.AddBalance(addr, big.NewInt(100000000000000))
|
|
||||||
|
|
||||||
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
<-pool.requestReset(nil, nil)
|
<-pool.requestReset(nil, nil)
|
||||||
|
|
@ -473,9 +473,8 @@ func TestDoubleNonce(t *testing.T) {
|
||||||
|
|
||||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
resetState := func() {
|
resetState := func() {
|
||||||
db := rawdb.NewMemoryDatabase()
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(db))
|
statedb.AddBalance(addr, big.NewInt(100000000000000), tracing.BalanceChangeUnspecified)
|
||||||
statedb.AddBalance(addr, big.NewInt(100000000000000))
|
|
||||||
|
|
||||||
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||||
<-pool.requestReset(nil, nil)
|
<-pool.requestReset(nil, nil)
|
||||||
|
|
@ -2589,7 +2588,7 @@ func BenchmarkMultiAccountBatchInsert(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
account := crypto.PubkeyToAddress(key.PublicKey)
|
account := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
pool.currentState.AddBalance(account, big.NewInt(1000000))
|
pool.currentState.AddBalance(account, big.NewInt(1000000), tracing.BalanceChangeUnspecified)
|
||||||
tx := transaction(uint64(0), 100000, key)
|
tx := transaction(uint64(0), 100000, key)
|
||||||
batches[i] = tx
|
batches[i] = tx
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -159,14 +160,28 @@ func (c *Contract) Caller() common.Address {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UseGas attempts the use gas and subtracts it and returns true on success
|
// UseGas attempts the use gas and subtracts it and returns true on success
|
||||||
func (c *Contract) UseGas(gas uint64) (ok bool) {
|
func (c *Contract) UseGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) (ok bool) {
|
||||||
if c.Gas < gas {
|
if c.Gas < gas {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
|
||||||
|
logger.OnGasChange(c.Gas, c.Gas-gas, reason)
|
||||||
|
}
|
||||||
c.Gas -= gas
|
c.Gas -= gas
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefundGas refunds gas to the contract
|
||||||
|
func (c *Contract) RefundGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
||||||
|
if gas == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
|
||||||
|
logger.OnGasChange(c.Gas, c.Gas+gas, reason)
|
||||||
|
}
|
||||||
|
c.Gas += gas
|
||||||
|
}
|
||||||
|
|
||||||
// Address returns the contracts address
|
// Address returns the contracts address
|
||||||
func (c *Contract) Address() common.Address {
|
func (c *Contract) Address() common.Address {
|
||||||
return c.self.Address()
|
return c.self.Address()
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm/privacy"
|
"github.com/XinFinOrg/XDPoSChain/core/vm/privacy"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto/blake2b"
|
"github.com/XinFinOrg/XDPoSChain/crypto/blake2b"
|
||||||
|
|
@ -155,7 +156,7 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
|
||||||
// - the returned bytes,
|
// - the returned bytes,
|
||||||
// - the _remaining_ gas,
|
// - the _remaining_ gas,
|
||||||
// - any error that occurred
|
// - any error that occurred
|
||||||
func RunPrecompiledContract(evm *EVM, p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
|
func RunPrecompiledContract(evm *EVM, p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) {
|
||||||
if evm != nil {
|
if evm != nil {
|
||||||
if evm.chainConfig.IsTIPXDCXReceiver(evm.Context.BlockNumber) {
|
if evm.chainConfig.IsTIPXDCXReceiver(evm.Context.BlockNumber) {
|
||||||
switch p := p.(type) {
|
switch p := p.(type) {
|
||||||
|
|
@ -171,6 +172,9 @@ func RunPrecompiledContract(evm *EVM, p PrecompiledContract, input []byte, suppl
|
||||||
if suppliedGas < gasCost {
|
if suppliedGas < gasCost {
|
||||||
return nil, 0, ErrOutOfGas
|
return nil, 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
if logger != nil && logger.OnGasChange != nil {
|
||||||
|
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract)
|
||||||
|
}
|
||||||
suppliedGas -= gasCost
|
suppliedGas -= gasCost
|
||||||
output, err := p.Run(input)
|
output, err := p.Run(input)
|
||||||
return output, suppliedGas, err
|
return output, suppliedGas, err
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ func FuzzPrecompiledContracts(f *testing.F) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
inWant := string(input)
|
inWant := string(input)
|
||||||
RunPrecompiledContract(nil, p, input, gas)
|
RunPrecompiledContract(nil, p, input, gas, nil)
|
||||||
if inHave := string(input); inWant != inHave {
|
if inHave := string(input); inWant != inHave {
|
||||||
t.Errorf("Precompiled %v modified input data", a)
|
t.Errorf("Precompiled %v modified input data", a)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -514,7 +514,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
|
||||||
in := common.Hex2Bytes(test.input)
|
in := common.Hex2Bytes(test.input)
|
||||||
gas := p.RequiredGas(in)
|
gas := p.RequiredGas(in)
|
||||||
t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) {
|
||||||
if res, _, err := RunPrecompiledContract(nil, p, in, gas); err != nil {
|
if res, _, err := RunPrecompiledContract(nil, p, in, gas, nil); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
} else if common.Bytes2Hex(res) != test.expected {
|
} else if common.Bytes2Hex(res) != test.expected {
|
||||||
t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res))
|
t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res))
|
||||||
|
|
@ -533,7 +533,7 @@ func testXDCxPrecompiled(addr string, test precompiledTest, t *testing.T) {
|
||||||
in := common.Hex2Bytes(test.input)
|
in := common.Hex2Bytes(test.input)
|
||||||
gas := p.RequiredGas(in)
|
gas := p.RequiredGas(in)
|
||||||
t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) {
|
||||||
if res, _, err := RunPrecompiledContract(nil, p, in, gas); err != nil {
|
if res, _, err := RunPrecompiledContract(nil, p, in, gas, nil); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
} else if common.Bytes2Hex(res) != test.expected {
|
} else if common.Bytes2Hex(res) != test.expected {
|
||||||
t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res))
|
t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res))
|
||||||
|
|
@ -547,7 +547,7 @@ func testPrecompiledWithEmptyTradingState(addr string, test precompiledTest, t *
|
||||||
in := common.Hex2Bytes(test.input)
|
in := common.Hex2Bytes(test.input)
|
||||||
gas := p.RequiredGas(in)
|
gas := p.RequiredGas(in)
|
||||||
t.Run(fmt.Sprintf("testPrecompiledWithEmptyTradingState-%s-Gas=%d", test.name, gas), func(t *testing.T) {
|
t.Run(fmt.Sprintf("testPrecompiledWithEmptyTradingState-%s-Gas=%d", test.name, gas), func(t *testing.T) {
|
||||||
if res, _, err := RunPrecompiledContract(nil, p, in, gas); err != nil {
|
if res, _, err := RunPrecompiledContract(nil, p, in, gas, nil); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
} else if common.Bytes2Hex(res) != test.expected {
|
} else if common.Bytes2Hex(res) != test.expected {
|
||||||
t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res))
|
t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res))
|
||||||
|
|
@ -561,7 +561,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
|
||||||
gas := p.RequiredGas(in) - 1
|
gas := p.RequiredGas(in) - 1
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s-Gas=%d", test.name, gas), func(t *testing.T) {
|
||||||
_, _, err := RunPrecompiledContract(nil, p, in, gas)
|
_, _, err := RunPrecompiledContract(nil, p, in, gas, nil)
|
||||||
if err.Error() != "out of gas" {
|
if err.Error() != "out of gas" {
|
||||||
t.Errorf("Expected error [out of gas], got [%v]", err)
|
t.Errorf("Expected error [out of gas], got [%v]", err)
|
||||||
}
|
}
|
||||||
|
|
@ -578,7 +578,7 @@ func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing
|
||||||
in := common.Hex2Bytes(test.input)
|
in := common.Hex2Bytes(test.input)
|
||||||
gas := p.RequiredGas(in)
|
gas := p.RequiredGas(in)
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
_, _, err := RunPrecompiledContract(nil, p, in, gas)
|
_, _, err := RunPrecompiledContract(nil, p, in, gas, nil)
|
||||||
if err.Error() != test.expectedError.Error() {
|
if err.Error() != test.expectedError.Error() {
|
||||||
t.Errorf("Expected error [%v], got [%v]", test.expectedError, err)
|
t.Errorf("Expected error [%v], got [%v]", test.expectedError, err)
|
||||||
}
|
}
|
||||||
|
|
@ -608,7 +608,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
|
||||||
bench.ResetTimer()
|
bench.ResetTimer()
|
||||||
for i := 0; i < bench.N; i++ {
|
for i := 0; i < bench.N; i++ {
|
||||||
copy(data, in)
|
copy(data, in)
|
||||||
res, _, err = RunPrecompiledContract(nil, p, data, reqGas)
|
res, _, err = RunPrecompiledContract(nil, p, data, reqGas, nil)
|
||||||
}
|
}
|
||||||
bench.StopTimer()
|
bench.StopTimer()
|
||||||
//Check if it is correct
|
//Check if it is correct
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package vm
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// List evm execution errors
|
// List evm execution errors
|
||||||
|
|
@ -70,3 +71,122 @@ type ErrInvalidOpCode struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ErrInvalidOpCode) Error() string { return fmt.Sprintf("invalid opcode: %s", e.opcode) }
|
func (e *ErrInvalidOpCode) Error() string { return fmt.Sprintf("invalid opcode: %s", e.opcode) }
|
||||||
|
|
||||||
|
// rpcError is the same interface as the one defined in rpc/errors.go
|
||||||
|
// but we do not want to depend on rpc package here so we redefine it.
|
||||||
|
//
|
||||||
|
// It's used to ensure that the VMError implements the RPC error interface.
|
||||||
|
type rpcError interface {
|
||||||
|
Error() string // returns the message
|
||||||
|
ErrorCode() int // returns the code
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ rpcError = (*VMError)(nil)
|
||||||
|
|
||||||
|
// VMError wraps a VM error with an additional stable error code. The error
|
||||||
|
// field is the original error that caused the VM error and must be one of the
|
||||||
|
// VM error defined at the top of this file.
|
||||||
|
//
|
||||||
|
// If the error is not one of the known error above, the error code will be
|
||||||
|
// set to VMErrorCodeUnknown.
|
||||||
|
type VMError struct {
|
||||||
|
error
|
||||||
|
code int
|
||||||
|
}
|
||||||
|
|
||||||
|
func VMErrorFromErr(err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &VMError{
|
||||||
|
error: err,
|
||||||
|
code: vmErrorCodeFromErr(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *VMError) Error() string {
|
||||||
|
return e.error.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *VMError) Unwrap() error {
|
||||||
|
return e.error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *VMError) ErrorCode() int {
|
||||||
|
return e.code
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// We start the error code at 1 so that we can use 0 later for some possible extension. There
|
||||||
|
// is no unspecified value for the code today because it should always be set to a valid value
|
||||||
|
// that could be VMErrorCodeUnknown if the error is not mapped to a known error code.
|
||||||
|
|
||||||
|
VMErrorCodeOutOfGas = 1 + iota
|
||||||
|
VMErrorCodeCodeStoreOutOfGas
|
||||||
|
VMErrorCodeDepth
|
||||||
|
VMErrorCodeInsufficientBalance
|
||||||
|
VMErrorCodeContractAddressCollision
|
||||||
|
VMErrorCodeExecutionReverted
|
||||||
|
VMErrorCodeMaxCodeSizeExceeded
|
||||||
|
VMErrorCodeInvalidJump
|
||||||
|
VMErrorCodeWriteProtection
|
||||||
|
VMErrorCodeReturnDataOutOfBounds
|
||||||
|
VMErrorCodeGasUintOverflow
|
||||||
|
VMErrorCodeInvalidCode
|
||||||
|
VMErrorCodeNonceUintOverflow
|
||||||
|
VMErrorCodeStackUnderflow
|
||||||
|
VMErrorCodeStackOverflow
|
||||||
|
VMErrorCodeInvalidOpCode
|
||||||
|
|
||||||
|
// VMErrorCodeUnknown explicitly marks an error as unknown, this is useful when error is converted
|
||||||
|
// from an actual `error` in which case if the mapping is not known, we can use this value to indicate that.
|
||||||
|
VMErrorCodeUnknown = math.MaxInt - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func vmErrorCodeFromErr(err error) int {
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, ErrOutOfGas):
|
||||||
|
return VMErrorCodeOutOfGas
|
||||||
|
case errors.Is(err, ErrCodeStoreOutOfGas):
|
||||||
|
return VMErrorCodeCodeStoreOutOfGas
|
||||||
|
case errors.Is(err, ErrDepth):
|
||||||
|
return VMErrorCodeDepth
|
||||||
|
case errors.Is(err, ErrInsufficientBalance):
|
||||||
|
return VMErrorCodeInsufficientBalance
|
||||||
|
case errors.Is(err, ErrContractAddressCollision):
|
||||||
|
return VMErrorCodeContractAddressCollision
|
||||||
|
case errors.Is(err, ErrExecutionReverted):
|
||||||
|
return VMErrorCodeExecutionReverted
|
||||||
|
case errors.Is(err, ErrMaxCodeSizeExceeded):
|
||||||
|
return VMErrorCodeMaxCodeSizeExceeded
|
||||||
|
case errors.Is(err, ErrInvalidJump):
|
||||||
|
return VMErrorCodeInvalidJump
|
||||||
|
case errors.Is(err, ErrWriteProtection):
|
||||||
|
return VMErrorCodeWriteProtection
|
||||||
|
case errors.Is(err, ErrReturnDataOutOfBounds):
|
||||||
|
return VMErrorCodeReturnDataOutOfBounds
|
||||||
|
case errors.Is(err, ErrGasUintOverflow):
|
||||||
|
return VMErrorCodeGasUintOverflow
|
||||||
|
case errors.Is(err, ErrInvalidCode):
|
||||||
|
return VMErrorCodeInvalidCode
|
||||||
|
case errors.Is(err, ErrNonceUintOverflow):
|
||||||
|
return VMErrorCodeNonceUintOverflow
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Dynamic errors
|
||||||
|
if v := (*ErrStackUnderflow)(nil); errors.As(err, &v) {
|
||||||
|
return VMErrorCodeStackUnderflow
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := (*ErrStackOverflow)(nil); errors.As(err, &v) {
|
||||||
|
return VMErrorCodeStackOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := (*ErrInvalidOpCode)(nil); errors.As(err, &v) {
|
||||||
|
return VMErrorCodeInvalidOpCode
|
||||||
|
}
|
||||||
|
|
||||||
|
return VMErrorCodeUnknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
185
core/vm/evm.go
185
core/vm/evm.go
|
|
@ -17,11 +17,13 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
|
"github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
|
@ -177,6 +179,13 @@ func (evm *EVM) Interpreter() *EVMInterpreter {
|
||||||
// the necessary steps to create accounts and reverses the state in case of an
|
// the necessary steps to create accounts and reverses the state in case of an
|
||||||
// execution error or failed value transfer.
|
// execution error or failed value transfer.
|
||||||
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
|
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
|
||||||
|
// Capture the tracer start/end events in debug mode
|
||||||
|
if evm.Config.Tracer != nil {
|
||||||
|
evm.captureBegin(evm.depth, CALL, caller.Address(), addr, input, gas, value)
|
||||||
|
defer func(startGas uint64) {
|
||||||
|
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||||
|
}(gas)
|
||||||
|
}
|
||||||
// Fail if we're trying to execute above the call depth limit
|
// Fail if we're trying to execute above the call depth limit
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
return nil, gas, ErrDepth
|
return nil, gas, ErrDepth
|
||||||
|
|
@ -187,44 +196,18 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
}
|
}
|
||||||
snapshot := evm.StateDB.Snapshot()
|
snapshot := evm.StateDB.Snapshot()
|
||||||
p, isPrecompile := evm.precompile(addr)
|
p, isPrecompile := evm.precompile(addr)
|
||||||
debug := evm.Config.Tracer != nil
|
|
||||||
|
|
||||||
if !evm.StateDB.Exist(addr) {
|
if !evm.StateDB.Exist(addr) {
|
||||||
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
|
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
|
||||||
// Calling a non existing account, don't do anything, but ping the tracer
|
// Calling a non-existing account, don't do anything.
|
||||||
if debug {
|
|
||||||
if evm.depth == 0 {
|
|
||||||
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
|
|
||||||
evm.Config.Tracer.CaptureEnd(ret, 0, nil)
|
|
||||||
} else {
|
|
||||||
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
|
|
||||||
evm.Config.Tracer.CaptureExit(ret, 0, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, gas, nil
|
return nil, gas, nil
|
||||||
}
|
}
|
||||||
evm.StateDB.CreateAccount(addr)
|
evm.StateDB.CreateAccount(addr)
|
||||||
}
|
}
|
||||||
evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)
|
evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)
|
||||||
|
|
||||||
// Capture the tracer start/end events in debug mode
|
|
||||||
if debug {
|
|
||||||
if evm.depth == 0 {
|
|
||||||
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
|
|
||||||
defer func(startGas uint64) { // Lazy evaluation of the parameters
|
|
||||||
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err)
|
|
||||||
}(gas)
|
|
||||||
} else {
|
|
||||||
// Handle tracer events for entering and exiting a call frame
|
|
||||||
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
|
|
||||||
defer func(startGas uint64) {
|
|
||||||
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
|
|
||||||
}(gas)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isPrecompile {
|
if isPrecompile {
|
||||||
ret, gas, err = RunPrecompiledContract(evm, p, input, gas)
|
ret, gas, err = RunPrecompiledContract(evm, p, input, gas, evm.Config.Tracer)
|
||||||
} else {
|
} else {
|
||||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||||
// The contract is a scoped environment for this execution context only.
|
// The contract is a scoped environment for this execution context only.
|
||||||
|
|
@ -242,11 +225,15 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// When an error was returned by the EVM or when setting the creation code
|
// When an error was returned by the EVM or when setting the creation code
|
||||||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
// above we revert to the snapshot and consume any gas remaining. Additionally,
|
||||||
// when we're in homestead this also counts for code storage gas errors.
|
// when we're in homestead this also counts for code storage gas errors.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
|
}
|
||||||
|
|
||||||
gas = 0
|
gas = 0
|
||||||
}
|
}
|
||||||
// TODO: consider clearing up unused snapshots:
|
// TODO: consider clearing up unused snapshots:
|
||||||
|
|
@ -264,6 +251,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
// CallCode differs from Call in the sense that it executes the given address'
|
// CallCode differs from Call in the sense that it executes the given address'
|
||||||
// code with the caller as context.
|
// code with the caller as context.
|
||||||
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
|
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
|
||||||
|
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||||
|
if evm.Config.Tracer != nil {
|
||||||
|
evm.captureBegin(evm.depth, CALLCODE, caller.Address(), addr, input, gas, value)
|
||||||
|
defer func(startGas uint64) {
|
||||||
|
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||||
|
}(gas)
|
||||||
|
}
|
||||||
// Fail if we're trying to execute above the call depth limit
|
// Fail if we're trying to execute above the call depth limit
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
return nil, gas, ErrDepth
|
return nil, gas, ErrDepth
|
||||||
|
|
@ -277,17 +271,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||||
}
|
}
|
||||||
var snapshot = evm.StateDB.Snapshot()
|
var snapshot = evm.StateDB.Snapshot()
|
||||||
|
|
||||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
|
||||||
if evm.Config.Tracer != nil {
|
|
||||||
evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
|
|
||||||
defer func(startGas uint64) {
|
|
||||||
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
|
|
||||||
}(gas)
|
|
||||||
}
|
|
||||||
|
|
||||||
// It is allowed to call precompiles, even via delegatecall
|
// It is allowed to call precompiles, even via delegatecall
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
ret, gas, err = RunPrecompiledContract(evm, p, input, gas)
|
ret, gas, err = RunPrecompiledContract(evm, p, input, gas, evm.Config.Tracer)
|
||||||
} else {
|
} else {
|
||||||
addrCopy := addr
|
addrCopy := addr
|
||||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||||
|
|
@ -300,6 +286,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
|
}
|
||||||
|
|
||||||
gas = 0
|
gas = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -312,27 +302,26 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||||
// DelegateCall differs from CallCode in the sense that it executes the given address'
|
// DelegateCall differs from CallCode in the sense that it executes the given address'
|
||||||
// code with the caller as context and the caller is set to the caller of the caller.
|
// code with the caller as context and the caller is set to the caller of the caller.
|
||||||
func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
|
func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
|
||||||
// Fail if we're trying to execute above the call depth limit
|
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
|
||||||
return nil, gas, ErrDepth
|
|
||||||
}
|
|
||||||
var snapshot = evm.StateDB.Snapshot()
|
|
||||||
|
|
||||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||||
if evm.Config.Tracer != nil {
|
if evm.Config.Tracer != nil {
|
||||||
// NOTE: caller must, at all times be a contract. It should never happen
|
// NOTE: caller must, at all times be a contract. It should never happen
|
||||||
// that caller is something other than a Contract.
|
// that caller is something other than a Contract.
|
||||||
parent := caller.(*Contract)
|
parent := caller.(*Contract)
|
||||||
// DELEGATECALL inherits value from parent call
|
// DELEGATECALL inherits value from parent call
|
||||||
evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value)
|
evm.captureBegin(evm.depth, DELEGATECALL, caller.Address(), addr, input, gas, parent.value)
|
||||||
defer func(startGas uint64) {
|
defer func(startGas uint64) {
|
||||||
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
|
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||||
}(gas)
|
}(gas)
|
||||||
}
|
}
|
||||||
|
// Fail if we're trying to execute above the call depth limit
|
||||||
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
|
return nil, gas, ErrDepth
|
||||||
|
}
|
||||||
|
var snapshot = evm.StateDB.Snapshot()
|
||||||
|
|
||||||
// It is allowed to call precompiles, even via delegatecall
|
// It is allowed to call precompiles, even via delegatecall
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
ret, gas, err = RunPrecompiledContract(evm, p, input, gas)
|
ret, gas, err = RunPrecompiledContract(evm, p, input, gas, evm.Config.Tracer)
|
||||||
} else {
|
} else {
|
||||||
addrCopy := addr
|
addrCopy := addr
|
||||||
// Initialise a new contract and make initialise the delegate values
|
// Initialise a new contract and make initialise the delegate values
|
||||||
|
|
@ -344,6 +333,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
|
}
|
||||||
gas = 0
|
gas = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -355,6 +347,13 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||||
// Opcodes that attempt to perform such modifications will result in exceptions
|
// Opcodes that attempt to perform such modifications will result in exceptions
|
||||||
// instead of performing the modifications.
|
// instead of performing the modifications.
|
||||||
func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
|
func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
|
||||||
|
// Invoke tracer hooks that signal entering/exiting a call frame
|
||||||
|
if evm.Config.Tracer != nil {
|
||||||
|
evm.captureBegin(evm.depth, STATICCALL, caller.Address(), addr, input, gas, nil)
|
||||||
|
defer func(startGas uint64) {
|
||||||
|
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||||
|
}(gas)
|
||||||
|
}
|
||||||
// Fail if we're trying to execute above the call depth limit
|
// Fail if we're trying to execute above the call depth limit
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
return nil, gas, ErrDepth
|
return nil, gas, ErrDepth
|
||||||
|
|
@ -371,19 +370,11 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||||
// but is the correct thing to do and matters on other networks, in tests, and potential
|
// but is the correct thing to do and matters on other networks, in tests, and potential
|
||||||
// future scenarios
|
// future scenarios
|
||||||
if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber) {
|
if evm.ChainConfig().IsTIPXDCXCancellationFee(evm.Context.BlockNumber) {
|
||||||
evm.StateDB.AddBalance(addr, big0)
|
evm.StateDB.AddBalance(addr, big0, tracing.BalanceChangeTouchAccount)
|
||||||
}
|
|
||||||
|
|
||||||
// Invoke tracer hooks that signal entering/exiting a call frame
|
|
||||||
if evm.Config.Tracer != nil {
|
|
||||||
evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
|
|
||||||
defer func(startGas uint64) {
|
|
||||||
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
|
|
||||||
}(gas)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
ret, gas, err = RunPrecompiledContract(evm, p, input, gas)
|
ret, gas, err = RunPrecompiledContract(evm, p, input, gas, evm.Config.Tracer)
|
||||||
} else {
|
} else {
|
||||||
// At this point, we use a copy of address. If we don't, the go compiler will
|
// At this point, we use a copy of address. If we don't, the go compiler will
|
||||||
// leak the 'contract' to the outer scope, and make allocation for 'contract'
|
// leak the 'contract' to the outer scope, and make allocation for 'contract'
|
||||||
|
|
@ -403,6 +394,10 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
|
}
|
||||||
|
|
||||||
gas = 0
|
gas = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -422,7 +417,13 @@ func (c *codeAndHash) Hash() common.Hash {
|
||||||
}
|
}
|
||||||
|
|
||||||
// create creates a new contract using code as deployment code.
|
// create creates a new contract using code as deployment code.
|
||||||
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
|
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) {
|
||||||
|
if evm.Config.Tracer != nil {
|
||||||
|
evm.captureBegin(evm.depth, typ, caller.Address(), address, codeAndHash.code, gas, value)
|
||||||
|
defer func(startGas uint64) {
|
||||||
|
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||||
|
}(gas)
|
||||||
|
}
|
||||||
// Depth check execution. Fail if we're trying to execute above the
|
// Depth check execution. Fail if we're trying to execute above the
|
||||||
// limit.
|
// limit.
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
|
|
@ -444,6 +445,10 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
// Ensure there's no existing contract already at the designated address
|
// Ensure there's no existing contract already at the designated address
|
||||||
contractHash := evm.StateDB.GetCodeHash(address)
|
contractHash := evm.StateDB.GetCodeHash(address)
|
||||||
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) {
|
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) {
|
||||||
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
|
}
|
||||||
|
|
||||||
return nil, common.Address{}, 0, ErrContractAddressCollision
|
return nil, common.Address{}, 0, ErrContractAddressCollision
|
||||||
}
|
}
|
||||||
// Create a new account on the state
|
// Create a new account on the state
|
||||||
|
|
@ -459,15 +464,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
contract := NewContract(caller, AccountRef(address), value, gas)
|
contract := NewContract(caller, AccountRef(address), value, gas)
|
||||||
contract.SetCodeOptionalHash(&address, codeAndHash)
|
contract.SetCodeOptionalHash(&address, codeAndHash)
|
||||||
|
|
||||||
if evm.Config.Tracer != nil {
|
ret, err = evm.interpreter.Run(contract, nil, false)
|
||||||
if evm.depth == 0 {
|
|
||||||
evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
|
|
||||||
} else {
|
|
||||||
evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret, err := evm.interpreter.Run(contract, nil, false)
|
|
||||||
|
|
||||||
// Check whether the max code size has been exceeded, assign err if the case.
|
// Check whether the max code size has been exceeded, assign err if the case.
|
||||||
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
|
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
|
||||||
|
|
@ -485,7 +482,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
// by the error checking condition below.
|
// by the error checking condition below.
|
||||||
if err == nil {
|
if err == nil {
|
||||||
createDataGas := uint64(len(ret)) * params.CreateDataGas
|
createDataGas := uint64(len(ret)) * params.CreateDataGas
|
||||||
if contract.UseGas(createDataGas) {
|
if contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
|
||||||
evm.StateDB.SetCode(address, ret)
|
evm.StateDB.SetCode(address, ret)
|
||||||
} else {
|
} else {
|
||||||
err = ErrCodeStoreOutOfGas
|
err = ErrCodeStoreOutOfGas
|
||||||
|
|
@ -493,22 +490,15 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
}
|
}
|
||||||
|
|
||||||
// When an error was returned by the EVM or when setting the creation code
|
// When an error was returned by the EVM or when setting the creation code
|
||||||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
// above we revert to the snapshot and consume any gas remaining. Additionally,
|
||||||
// when we're in homestead this also counts for code storage gas errors.
|
// when we're in homestead this also counts for code storage gas errors.
|
||||||
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
contract.UseGas(contract.Gas)
|
contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if evm.Config.Tracer != nil {
|
|
||||||
if evm.depth == 0 {
|
|
||||||
evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, err)
|
|
||||||
} else {
|
|
||||||
evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret, address, contract.Gas, err
|
return ret, address, contract.Gas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -530,3 +520,44 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *
|
||||||
|
|
||||||
// ChainConfig returns the environment's chain configuration
|
// ChainConfig returns the environment's chain configuration
|
||||||
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
|
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
|
||||||
|
|
||||||
|
func (evm *EVM) captureBegin(depth int, typ OpCode, from common.Address, to common.Address, input []byte, startGas uint64, value *big.Int) {
|
||||||
|
tracer := evm.Config.Tracer
|
||||||
|
if tracer.OnEnter != nil {
|
||||||
|
tracer.OnEnter(depth, byte(typ), from, to, input, startGas, value)
|
||||||
|
}
|
||||||
|
if tracer.OnGasChange != nil {
|
||||||
|
tracer.OnGasChange(0, startGas, tracing.GasChangeCallInitialBalance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (evm *EVM) captureEnd(depth int, startGas uint64, leftOverGas uint64, ret []byte, err error) {
|
||||||
|
tracer := evm.Config.Tracer
|
||||||
|
if leftOverGas != 0 && tracer.OnGasChange != nil {
|
||||||
|
tracer.OnGasChange(leftOverGas, 0, tracing.GasChangeCallLeftOverReturned)
|
||||||
|
}
|
||||||
|
var reverted bool
|
||||||
|
if err != nil {
|
||||||
|
reverted = true
|
||||||
|
}
|
||||||
|
if !evm.chainRules.IsHomestead && errors.Is(err, ErrCodeStoreOutOfGas) {
|
||||||
|
reverted = false
|
||||||
|
}
|
||||||
|
if tracer.OnExit != nil {
|
||||||
|
tracer.OnExit(depth, ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVMContext provides context about the block being executed as well as state
|
||||||
|
// to the tracers.
|
||||||
|
func (evm *EVM) GetVMContext() *tracing.VMContext {
|
||||||
|
return &tracing.VMContext{
|
||||||
|
Coinbase: evm.Context.Coinbase,
|
||||||
|
BlockNumber: evm.Context.BlockNumber,
|
||||||
|
Time: evm.Context.Time,
|
||||||
|
Random: evm.Context.Random,
|
||||||
|
GasPrice: evm.TxContext.GasPrice,
|
||||||
|
ChainConfig: evm.ChainConfig(),
|
||||||
|
StateDB: evm.StateDB,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
|
@ -244,7 +245,6 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
|
||||||
if evm.Config.EnablePreimageRecording {
|
if evm.Config.EnablePreimageRecording {
|
||||||
evm.StateDB.AddPreimage(interpreter.hasherBuf, data)
|
evm.StateDB.AddPreimage(interpreter.hasherBuf, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
size.SetBytes(interpreter.hasherBuf[:])
|
size.SetBytes(interpreter.hasherBuf[:])
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
@ -600,13 +600,13 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
||||||
// reuse size int for stackvalue
|
// reuse size int for stackvalue
|
||||||
stackvalue := size
|
stackvalue := size
|
||||||
|
|
||||||
scope.Contract.UseGas(gas)
|
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation)
|
||||||
//TODO: use uint256.Int instead of converting with toBig()
|
|
||||||
|
// TODO(daniel): use uint256.Int instead of converting with toBig()
|
||||||
var bigVal = big0
|
var bigVal = big0
|
||||||
if !value.IsZero() {
|
if !value.IsZero() {
|
||||||
bigVal = value.ToBig()
|
bigVal = value.ToBig()
|
||||||
}
|
}
|
||||||
|
|
||||||
res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, bigVal)
|
res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, bigVal)
|
||||||
// Push item on the stack based on the returned error. If the ruleset is
|
// Push item on the stack based on the returned error. If the ruleset is
|
||||||
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
// homestead we must check for CodeStoreOutOfGasError (homestead only
|
||||||
|
|
@ -620,7 +620,8 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
||||||
stackvalue.SetBytes(addr.Bytes())
|
stackvalue.SetBytes(addr.Bytes())
|
||||||
}
|
}
|
||||||
scope.Stack.push(&stackvalue)
|
scope.Stack.push(&stackvalue)
|
||||||
scope.Contract.Gas += returnGas
|
|
||||||
|
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
if suberr == ErrExecutionReverted {
|
if suberr == ErrExecutionReverted {
|
||||||
interpreter.returnData = res // set REVERT data to return data buffer
|
interpreter.returnData = res // set REVERT data to return data buffer
|
||||||
|
|
@ -643,10 +644,10 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
||||||
)
|
)
|
||||||
// Apply EIP150
|
// Apply EIP150
|
||||||
gas -= gas / 64
|
gas -= gas / 64
|
||||||
scope.Contract.UseGas(gas)
|
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
|
||||||
// reuse size int for stackvalue
|
// reuse size int for stackvalue
|
||||||
stackvalue := size
|
stackvalue := size
|
||||||
//TODO: use uint256.Int instead of converting with toBig()
|
// TODO(daniel): use uint256.Int instead of converting with toBig()
|
||||||
bigEndowment := big0
|
bigEndowment := big0
|
||||||
if !endowment.IsZero() {
|
if !endowment.IsZero() {
|
||||||
bigEndowment = endowment.ToBig()
|
bigEndowment = endowment.ToBig()
|
||||||
|
|
@ -660,7 +661,8 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
|
||||||
stackvalue.SetBytes(addr.Bytes())
|
stackvalue.SetBytes(addr.Bytes())
|
||||||
}
|
}
|
||||||
scope.Stack.push(&stackvalue)
|
scope.Stack.push(&stackvalue)
|
||||||
scope.Contract.Gas += returnGas
|
|
||||||
|
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
if suberr == ErrExecutionReverted {
|
if suberr == ErrExecutionReverted {
|
||||||
interpreter.returnData = res // set REVERT data to return data buffer
|
interpreter.returnData = res // set REVERT data to return data buffer
|
||||||
|
|
@ -687,7 +689,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
|
||||||
}
|
}
|
||||||
|
|
||||||
var bigVal = big0
|
var bigVal = big0
|
||||||
//TODO: use uint256.Int instead of converting with toBig()
|
// TODO(daniel): use uint256.Int instead of converting with toBig()
|
||||||
// By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
|
// By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
|
||||||
// but it would make more sense to extend the usage of uint256.Int
|
// but it would make more sense to extend the usage of uint256.Int
|
||||||
if !value.IsZero() {
|
if !value.IsZero() {
|
||||||
|
|
@ -706,7 +708,8 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
|
||||||
if err == nil || err == ErrExecutionReverted {
|
if err == nil || err == ErrExecutionReverted {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
scope.Contract.Gas += returnGas
|
|
||||||
|
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
interpreter.returnData = ret
|
interpreter.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
|
@ -724,7 +727,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
||||||
// Get arguments from the memory.
|
// Get arguments from the memory.
|
||||||
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
|
||||||
|
|
||||||
//TODO: use uint256.Int instead of converting with toBig()
|
// TODO(daniel): use uint256.Int instead of converting with toBig()
|
||||||
var bigVal = big0
|
var bigVal = big0
|
||||||
if !value.IsZero() {
|
if !value.IsZero() {
|
||||||
gas += params.CallStipend
|
gas += params.CallStipend
|
||||||
|
|
@ -741,7 +744,8 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
||||||
if err == nil || err == ErrExecutionReverted {
|
if err == nil || err == ErrExecutionReverted {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
scope.Contract.Gas += returnGas
|
|
||||||
|
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
interpreter.returnData = ret
|
interpreter.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
|
@ -769,7 +773,8 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
||||||
if err == nil || err == ErrExecutionReverted {
|
if err == nil || err == ErrExecutionReverted {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
scope.Contract.Gas += returnGas
|
|
||||||
|
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
interpreter.returnData = ret
|
interpreter.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
|
@ -797,7 +802,8 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
||||||
if err == nil || err == ErrExecutionReverted {
|
if err == nil || err == ErrExecutionReverted {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
scope.Contract.Gas += returnGas
|
|
||||||
|
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
interpreter.returnData = ret
|
interpreter.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
|
@ -832,11 +838,15 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
||||||
}
|
}
|
||||||
beneficiary := scope.Stack.pop()
|
beneficiary := scope.Stack.pop()
|
||||||
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
|
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
|
||||||
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
|
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct)
|
||||||
interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address())
|
interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address())
|
||||||
if tracer := interpreter.evm.Config.Tracer; tracer != nil {
|
if tracer := interpreter.evm.Config.Tracer; tracer != nil {
|
||||||
tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
if tracer.OnEnter != nil {
|
||||||
tracer.CaptureExit([]byte{}, 0, nil)
|
tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
||||||
|
}
|
||||||
|
if tracer.OnExit != nil {
|
||||||
|
tracer.OnExit(interpreter.evm.depth, []byte{}, 0, nil, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil, errStopToken
|
return nil, errStopToken
|
||||||
}
|
}
|
||||||
|
|
@ -847,12 +857,16 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon
|
||||||
}
|
}
|
||||||
beneficiary := scope.Stack.pop()
|
beneficiary := scope.Stack.pop()
|
||||||
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
|
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
|
||||||
interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance)
|
interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct)
|
||||||
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
|
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct)
|
||||||
interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address())
|
interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address())
|
||||||
if tracer := interpreter.evm.Config.Tracer; tracer != nil {
|
if tracer := interpreter.evm.Config.Tracer; tracer != nil {
|
||||||
tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
if tracer.OnEnter != nil {
|
||||||
tracer.CaptureExit([]byte{}, 0, nil)
|
tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
||||||
|
}
|
||||||
|
if tracer.OnExit != nil {
|
||||||
|
tracer.OnExit(interpreter.evm.depth, []byte{}, 0, nil, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil, errStopToken
|
return nil, errStopToken
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
)
|
)
|
||||||
|
|
@ -28,8 +29,8 @@ import (
|
||||||
type StateDB interface {
|
type StateDB interface {
|
||||||
CreateAccount(common.Address)
|
CreateAccount(common.Address)
|
||||||
|
|
||||||
SubBalance(common.Address, *big.Int)
|
SubBalance(common.Address, *big.Int, tracing.BalanceChangeReason)
|
||||||
AddBalance(common.Address, *big.Int)
|
AddBalance(common.Address, *big.Int, tracing.BalanceChangeReason)
|
||||||
GetBalance(common.Address) *big.Int
|
GetBalance(common.Address) *big.Int
|
||||||
|
|
||||||
GetNonce(common.Address) uint64
|
GetNonce(common.Address) uint64
|
||||||
|
|
|
||||||
|
|
@ -17,18 +17,22 @@
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config are the configuration options for the Interpreter
|
// Config are the configuration options for the Interpreter
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Tracer EVMLogger // Opcode logger
|
Tracer *tracing.Hooks
|
||||||
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
|
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
|
||||||
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
|
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
|
||||||
ExtraEips []int // Additional EIPS that are to be enabled
|
ExtraEips []int // Additional EIPS that are to be enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScopeContext contains the things that are per-call, such as stack and memory,
|
// ScopeContext contains the things that are per-call, such as stack and memory,
|
||||||
|
|
@ -39,6 +43,45 @@ type ScopeContext struct {
|
||||||
Contract *Contract
|
Contract *Contract
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MemoryData returns the underlying memory slice. Callers must not modify the contents
|
||||||
|
// of the returned data.
|
||||||
|
func (ctx *ScopeContext) MemoryData() []byte {
|
||||||
|
if ctx.Memory == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ctx.Memory.Data()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemoryData returns the stack data. Callers must not modify the contents
|
||||||
|
// of the returned data.
|
||||||
|
func (ctx *ScopeContext) StackData() []uint256.Int {
|
||||||
|
if ctx.Stack == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ctx.Stack.Data()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caller returns the current caller.
|
||||||
|
func (ctx *ScopeContext) Caller() common.Address {
|
||||||
|
return ctx.Contract.Caller()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address returns the address where this scope of execution is taking place.
|
||||||
|
func (ctx *ScopeContext) Address() common.Address {
|
||||||
|
return ctx.Contract.Address()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallValue returns the value supplied with this call.
|
||||||
|
func (ctx *ScopeContext) CallValue() *big.Int {
|
||||||
|
return ctx.Contract.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallInput returns the input/calldata with this call. Callers must not modify
|
||||||
|
// the contents of the returned data.
|
||||||
|
func (ctx *ScopeContext) CallInput() []byte {
|
||||||
|
return ctx.Contract.Input
|
||||||
|
}
|
||||||
|
|
||||||
// EVMInterpreter represents an EVM interpreter
|
// EVMInterpreter represents an EVM interpreter
|
||||||
type EVMInterpreter struct {
|
type EVMInterpreter struct {
|
||||||
evm *EVM
|
evm *EVM
|
||||||
|
|
@ -148,8 +191,8 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
res []byte // result of the opcode execution function
|
res []byte // result of the opcode execution function
|
||||||
debug = in.evm.Config.Tracer != nil
|
debug = in.evm.Config.Tracer != nil
|
||||||
)
|
)
|
||||||
// Don't move this deferred function, it's placed before the capturestate-deferred method,
|
// Don't move this deferred function, it's placed before the OnOpcode-deferred method,
|
||||||
// so that it gets executed _after_: the capturestate needs the stacks before
|
// so that it gets executed _after_: the OnOpcode needs the stacks before
|
||||||
// they are returned to the pools
|
// they are returned to the pools
|
||||||
defer func() {
|
defer func() {
|
||||||
returnStack(stack)
|
returnStack(stack)
|
||||||
|
|
@ -157,13 +200,15 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
contract.Input = input
|
contract.Input = input
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
defer func() {
|
defer func() { // this deferred method handles exit-with-error
|
||||||
if err != nil {
|
if err == nil {
|
||||||
if !logged {
|
return
|
||||||
in.evm.Config.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
}
|
||||||
} else {
|
if !logged && in.evm.Config.Tracer.OnOpcode != nil {
|
||||||
in.evm.Config.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
|
in.evm.Config.Tracer.OnOpcode(pcCopy, byte(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err))
|
||||||
}
|
}
|
||||||
|
if logged && in.evm.Config.Tracer.OnFault != nil {
|
||||||
|
in.evm.Config.Tracer.OnFault(pcCopy, byte(op), gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
@ -188,9 +233,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
} else if sLen > operation.maxStack {
|
} else if sLen > operation.maxStack {
|
||||||
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
|
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
|
||||||
}
|
}
|
||||||
if !contract.UseGas(cost) {
|
if !contract.UseGas(cost, in.evm.Config.Tracer, tracing.GasChangeIgnored) {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
||||||
if operation.dynamicGas != nil {
|
if operation.dynamicGas != nil {
|
||||||
// All ops with a dynamic memory usage also has a dynamic gas cost.
|
// All ops with a dynamic memory usage also has a dynamic gas cost.
|
||||||
var memorySize uint64
|
var memorySize uint64
|
||||||
|
|
@ -214,22 +260,33 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||||
var dynamicCost uint64
|
var dynamicCost uint64
|
||||||
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
|
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
|
||||||
cost += dynamicCost // for tracing
|
cost += dynamicCost // for tracing
|
||||||
if err != nil || !contract.UseGas(dynamicCost) {
|
if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, tracing.GasChangeIgnored) {
|
||||||
return nil, ErrOutOfGas
|
return nil, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do tracing before memory expansion
|
// Do tracing before memory expansion
|
||||||
if debug {
|
if debug {
|
||||||
in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
if in.evm.Config.Tracer.OnGasChange != nil {
|
||||||
logged = true
|
in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode)
|
||||||
|
}
|
||||||
|
if in.evm.Config.Tracer.OnOpcode != nil {
|
||||||
|
in.evm.Config.Tracer.OnOpcode(pc, byte(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err))
|
||||||
|
logged = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if memorySize > 0 {
|
if memorySize > 0 {
|
||||||
mem.Resize(memorySize)
|
mem.Resize(memorySize)
|
||||||
}
|
}
|
||||||
} else if debug {
|
} else if debug {
|
||||||
in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
|
if in.evm.Config.Tracer.OnGasChange != nil {
|
||||||
logged = true
|
in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode)
|
||||||
|
}
|
||||||
|
if in.evm.Config.Tracer.OnOpcode != nil {
|
||||||
|
in.evm.Config.Tracer.OnOpcode(pc, byte(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err))
|
||||||
|
logged = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute the operation
|
// execute the operation
|
||||||
res, err = operation.execute(&pc, in, callContext)
|
res, err = operation.execute(&pc, in, callContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EVMLogger is used to collect execution traces from an EVM transaction
|
|
||||||
// execution. CaptureState is called for each step of the VM with the
|
|
||||||
// current VM state.
|
|
||||||
// Note that reference types are actual VM data structures; make copies
|
|
||||||
// if you need to retain them beyond the current call.
|
|
||||||
type EVMLogger interface {
|
|
||||||
// Transaction level
|
|
||||||
CaptureTxStart(gasLimit uint64)
|
|
||||||
CaptureTxEnd(restGas uint64)
|
|
||||||
// Top call frame
|
|
||||||
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
|
|
||||||
CaptureEnd(output []byte, gasUsed uint64, err error)
|
|
||||||
// Rest of call frames
|
|
||||||
CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
|
|
||||||
CaptureExit(output []byte, gasUsed uint64, err error)
|
|
||||||
// Opcode level
|
|
||||||
CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
|
|
||||||
CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
|
|
||||||
}
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -169,7 +170,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(addr)
|
||||||
// Charge the remaining difference here already, to correctly calculate available
|
// Charge the remaining difference here already, to correctly calculate available
|
||||||
// gas for call
|
// gas for call
|
||||||
if !contract.UseGas(coldCost) {
|
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||||
return 0, ErrOutOfGas
|
return 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,9 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||||
sender = vm.AccountRef(cfg.Origin)
|
sender = vm.AccountRef(cfg.Origin)
|
||||||
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber)
|
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber)
|
||||||
)
|
)
|
||||||
|
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxStart != nil {
|
||||||
|
cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin)
|
||||||
|
}
|
||||||
// Execute the preparatory steps for state transition which includes:
|
// Execute the preparatory steps for state transition which includes:
|
||||||
// - prepare accessList(post-berlin)
|
// - prepare accessList(post-berlin)
|
||||||
// - reset transient storage(eip 1153)
|
// - reset transient storage(eip 1153)
|
||||||
|
|
@ -154,6 +157,9 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
|
||||||
sender = vm.AccountRef(cfg.Origin)
|
sender = vm.AccountRef(cfg.Origin)
|
||||||
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber)
|
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber)
|
||||||
)
|
)
|
||||||
|
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxStart != nil {
|
||||||
|
cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin)
|
||||||
|
}
|
||||||
// Execute the preparatory steps for state transition which includes:
|
// Execute the preparatory steps for state transition which includes:
|
||||||
// - prepare accessList(post-berlin)
|
// - prepare accessList(post-berlin)
|
||||||
// - reset transient storage(eip 1153)
|
// - reset transient storage(eip 1153)
|
||||||
|
|
@ -183,6 +189,9 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
|
||||||
statedb = cfg.State
|
statedb = cfg.State
|
||||||
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber)
|
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber)
|
||||||
)
|
)
|
||||||
|
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxStart != nil {
|
||||||
|
cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin)
|
||||||
|
}
|
||||||
// Execute the preparatory steps for state transition which includes:
|
// Execute the preparatory steps for state transition which includes:
|
||||||
// - prepare accessList(post-berlin)
|
// - prepare accessList(post-berlin)
|
||||||
// - reset transient storage(eip 1153)
|
// - reset transient storage(eip 1153)
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/tracers/logger"
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers/logger"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
|
||||||
|
|
@ -511,7 +512,7 @@ func TestEip2929Cases(t *testing.T) {
|
||||||
code, ops)
|
code, ops)
|
||||||
Execute(code, nil, &Config{
|
Execute(code, nil, &Config{
|
||||||
EVMConfig: vm.Config{
|
EVMConfig: vm.Config{
|
||||||
Tracer: logger.NewMarkdownLogger(nil, os.Stdout),
|
Tracer: logger.NewMarkdownLogger(nil, os.Stdout).Hooks(),
|
||||||
ExtraEips: []int{2929},
|
ExtraEips: []int{2929},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
@ -664,7 +665,7 @@ func TestColdAccountAccessCost(t *testing.T) {
|
||||||
tracer := logger.NewStructLogger(nil)
|
tracer := logger.NewStructLogger(nil)
|
||||||
Execute(tc.code, nil, &Config{
|
Execute(tc.code, nil, &Config{
|
||||||
EVMConfig: vm.Config{
|
EVMConfig: vm.Config{
|
||||||
Tracer: tracer,
|
Tracer: tracer.Hooks(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
have := tracer.StructLogs()[tc.step].GasCost
|
have := tracer.StructLogs()[tc.step].GasCost
|
||||||
|
|
@ -676,3 +677,226 @@ func TestColdAccountAccessCost(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRuntimeJSTracer(t *testing.T) {
|
||||||
|
jsTracers := []string{
|
||||||
|
`{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, steps:0,
|
||||||
|
step: function() { this.steps++},
|
||||||
|
fault: function() {},
|
||||||
|
result: function() {
|
||||||
|
return [this.enters, this.exits,this.enterGas,this.gasUsed, this.steps].join(",")
|
||||||
|
},
|
||||||
|
enter: function(frame) {
|
||||||
|
this.enters++;
|
||||||
|
this.enterGas = frame.getGas();
|
||||||
|
},
|
||||||
|
exit: function(res) {
|
||||||
|
this.exits++;
|
||||||
|
this.gasUsed = res.getGasUsed();
|
||||||
|
}}`,
|
||||||
|
`{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, steps:0,
|
||||||
|
fault: function() {},
|
||||||
|
result: function() {
|
||||||
|
return [this.enters, this.exits,this.enterGas,this.gasUsed, this.steps].join(",")
|
||||||
|
},
|
||||||
|
enter: function(frame) {
|
||||||
|
this.enters++;
|
||||||
|
this.enterGas = frame.getGas();
|
||||||
|
},
|
||||||
|
exit: function(res) {
|
||||||
|
this.exits++;
|
||||||
|
this.gasUsed = res.getGasUsed();
|
||||||
|
}}`}
|
||||||
|
tests := []struct {
|
||||||
|
code []byte
|
||||||
|
// One result per tracer
|
||||||
|
results []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
// CREATE
|
||||||
|
code: []byte{
|
||||||
|
// Store initcode in memory at 0x00 (5 bytes left-padded to 32 bytes)
|
||||||
|
byte(vm.PUSH5),
|
||||||
|
// Init code: PUSH1 0, PUSH1 0, RETURN (3 steps)
|
||||||
|
byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN),
|
||||||
|
byte(vm.PUSH1), 0,
|
||||||
|
byte(vm.MSTORE),
|
||||||
|
// length, offset, value
|
||||||
|
byte(vm.PUSH1), 5, byte(vm.PUSH1), 27, byte(vm.PUSH1), 0,
|
||||||
|
byte(vm.CREATE),
|
||||||
|
byte(vm.POP),
|
||||||
|
},
|
||||||
|
results: []string{`"1,1,952853,6,12"`, `"1,1,952853,6,0"`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// CREATE2
|
||||||
|
code: []byte{
|
||||||
|
// Store initcode in memory at 0x00 (5 bytes left-padded to 32 bytes)
|
||||||
|
byte(vm.PUSH5),
|
||||||
|
// Init code: PUSH1 0, PUSH1 0, RETURN (3 steps)
|
||||||
|
byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN),
|
||||||
|
byte(vm.PUSH1), 0,
|
||||||
|
byte(vm.MSTORE),
|
||||||
|
// salt, length, offset, value
|
||||||
|
byte(vm.PUSH1), 1, byte(vm.PUSH1), 5, byte(vm.PUSH1), 27, byte(vm.PUSH1), 0,
|
||||||
|
byte(vm.CREATE2),
|
||||||
|
byte(vm.POP),
|
||||||
|
},
|
||||||
|
results: []string{`"1,1,952844,6,13"`, `"1,1,952844,6,0"`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// CALL
|
||||||
|
code: []byte{
|
||||||
|
// outsize, outoffset, insize, inoffset
|
||||||
|
byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0,
|
||||||
|
byte(vm.PUSH1), 0, // value
|
||||||
|
byte(vm.PUSH1), 0xbb, //address
|
||||||
|
byte(vm.GAS), // gas
|
||||||
|
byte(vm.CALL),
|
||||||
|
byte(vm.POP),
|
||||||
|
},
|
||||||
|
results: []string{`"1,1,981796,6,13"`, `"1,1,981796,6,0"`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// CALLCODE
|
||||||
|
code: []byte{
|
||||||
|
// outsize, outoffset, insize, inoffset
|
||||||
|
byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0,
|
||||||
|
byte(vm.PUSH1), 0, // value
|
||||||
|
byte(vm.PUSH1), 0xcc, //address
|
||||||
|
byte(vm.GAS), // gas
|
||||||
|
byte(vm.CALLCODE),
|
||||||
|
byte(vm.POP),
|
||||||
|
},
|
||||||
|
results: []string{`"1,1,981796,6,13"`, `"1,1,981796,6,0"`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// STATICCALL
|
||||||
|
code: []byte{
|
||||||
|
// outsize, outoffset, insize, inoffset
|
||||||
|
byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0,
|
||||||
|
byte(vm.PUSH1), 0xdd, //address
|
||||||
|
byte(vm.GAS), // gas
|
||||||
|
byte(vm.STATICCALL),
|
||||||
|
byte(vm.POP),
|
||||||
|
},
|
||||||
|
results: []string{`"1,1,981799,6,12"`, `"1,1,981799,6,0"`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// DELEGATECALL
|
||||||
|
code: []byte{
|
||||||
|
// outsize, outoffset, insize, inoffset
|
||||||
|
byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0,
|
||||||
|
byte(vm.PUSH1), 0xee, //address
|
||||||
|
byte(vm.GAS), // gas
|
||||||
|
byte(vm.DELEGATECALL),
|
||||||
|
byte(vm.POP),
|
||||||
|
},
|
||||||
|
results: []string{`"1,1,981799,6,12"`, `"1,1,981799,6,0"`},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// CALL self-destructing contract
|
||||||
|
code: []byte{
|
||||||
|
// outsize, outoffset, insize, inoffset
|
||||||
|
byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0,
|
||||||
|
byte(vm.PUSH1), 0, // value
|
||||||
|
byte(vm.PUSH1), 0xff, //address
|
||||||
|
byte(vm.GAS), // gas
|
||||||
|
byte(vm.CALL),
|
||||||
|
byte(vm.POP),
|
||||||
|
},
|
||||||
|
results: []string{`"2,2,0,5003,12"`, `"2,2,0,5003,0"`},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
calleeCode := []byte{
|
||||||
|
byte(vm.PUSH1), 0,
|
||||||
|
byte(vm.PUSH1), 0,
|
||||||
|
byte(vm.RETURN),
|
||||||
|
}
|
||||||
|
suicideCode := []byte{
|
||||||
|
byte(vm.PUSH1), 0xaa,
|
||||||
|
byte(vm.SELFDESTRUCT),
|
||||||
|
}
|
||||||
|
main := common.HexToAddress("0xaa")
|
||||||
|
for i, jsTracer := range jsTracers {
|
||||||
|
for j, tc := range tests {
|
||||||
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
|
statedb.SetCode(main, tc.code)
|
||||||
|
statedb.SetCode(common.HexToAddress("0xbb"), calleeCode)
|
||||||
|
statedb.SetCode(common.HexToAddress("0xcc"), calleeCode)
|
||||||
|
statedb.SetCode(common.HexToAddress("0xdd"), calleeCode)
|
||||||
|
statedb.SetCode(common.HexToAddress("0xee"), calleeCode)
|
||||||
|
statedb.SetCode(common.HexToAddress("0xff"), suicideCode)
|
||||||
|
|
||||||
|
tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, _, err = Call(main, nil, &Config{
|
||||||
|
GasLimit: 1000000,
|
||||||
|
State: statedb,
|
||||||
|
EVMConfig: vm.Config{
|
||||||
|
Tracer: tracer.Hooks,
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("didn't expect error", err)
|
||||||
|
}
|
||||||
|
res, err := tracer.GetResult()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if have, want := string(res), tc.results[i]; have != want {
|
||||||
|
t.Errorf("wrong result for tracer %d testcase %d, have \n%v\nwant\n%v\n", i, j, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSTracerCreateTx(t *testing.T) {
|
||||||
|
jsTracer := `
|
||||||
|
{enters: 0, exits: 0,
|
||||||
|
step: function() {},
|
||||||
|
fault: function() {},
|
||||||
|
result: function() { return [this.enters, this.exits].join(",") },
|
||||||
|
enter: function(frame) { this.enters++ },
|
||||||
|
exit: function(res) { this.exits++ }}`
|
||||||
|
code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)}
|
||||||
|
|
||||||
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
|
tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, _, _, err = Create(code, &Config{
|
||||||
|
State: statedb,
|
||||||
|
EVMConfig: vm.Config{
|
||||||
|
Tracer: tracer.Hooks,
|
||||||
|
}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := tracer.GetResult()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if have, want := string(res), `"0,0"`; have != want {
|
||||||
|
t.Errorf("wrong result for tracer, have \n%v\nwant\n%v\n", have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkTracerStepVsCallFrame(b *testing.B) {
|
||||||
|
// Simply pushes and pops some values in a loop
|
||||||
|
code := []byte{
|
||||||
|
byte(vm.JUMPDEST),
|
||||||
|
byte(vm.PUSH1), 0,
|
||||||
|
byte(vm.PUSH1), 0,
|
||||||
|
byte(vm.POP),
|
||||||
|
byte(vm.POP),
|
||||||
|
byte(vm.PUSH1), 0, // jumpdestination
|
||||||
|
byte(vm.JUMP),
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmarkNonModifyingCode(10000000, code, "tracer-step-10M", b)
|
||||||
|
benchmarkNonModifyingCode(10000000, code, "tracer-call-frame-10M", b)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
|
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/txpool"
|
"github.com/XinFinOrg/XDPoSChain/core/txpool"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
|
|
@ -262,7 +263,7 @@ func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *st
|
||||||
if vmConfig == nil {
|
if vmConfig == nil {
|
||||||
vmConfig = b.eth.blockchain.GetVMConfig()
|
vmConfig = b.eth.blockchain.GetVMConfig()
|
||||||
}
|
}
|
||||||
state.SetBalance(msg.From, math.MaxBig256)
|
state.SetBalance(msg.From, math.MaxBig256, tracing.BalanceChangeUnspecified)
|
||||||
txContext := core.NewEVMTxContext(msg)
|
txContext := core.NewEVMTxContext(msg)
|
||||||
var context vm.BlockContext
|
var context vm.BlockContext
|
||||||
if blockCtx != nil {
|
if blockCtx != nil {
|
||||||
|
|
@ -423,7 +424,7 @@ func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, re
|
||||||
return b.eth.StateAtBlock(ctx, block, reexec, base, readOnly, preferDisk)
|
return b.eth.StateAtBlock(ctx, block, reexec, base, readOnly, preferDisk)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
|
func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
|
||||||
return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
|
return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/trie"
|
"github.com/XinFinOrg/XDPoSChain/trie"
|
||||||
|
|
@ -65,35 +66,37 @@ func (h resultHash) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||||
func (h resultHash) Less(i, j int) bool { return bytes.Compare(h[i].Bytes(), h[j].Bytes()) < 0 }
|
func (h resultHash) Less(i, j int) bool { return bytes.Compare(h[i].Bytes(), h[j].Bytes()) < 0 }
|
||||||
|
|
||||||
func TestAccountRange(t *testing.T) {
|
func TestAccountRange(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &trie.Config{Preimages: true})
|
statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &trie.Config{Preimages: true})
|
||||||
state, _ = state.New(common.Hash{}, statedb)
|
sdb, _ = state.New(common.Hash{}, statedb)
|
||||||
addrs = [AccountRangeMaxResults * 2]common.Address{}
|
addrs = [AccountRangeMaxResults * 2]common.Address{}
|
||||||
m = map[common.Address]bool{}
|
m = map[common.Address]bool{}
|
||||||
)
|
)
|
||||||
|
|
||||||
for i := range addrs {
|
for i := range addrs {
|
||||||
hash := common.HexToHash(fmt.Sprintf("%x", i))
|
hash := common.HexToHash(fmt.Sprintf("%x", i))
|
||||||
addr := common.BytesToAddress(crypto.Keccak256Hash(hash.Bytes()).Bytes())
|
addr := common.BytesToAddress(crypto.Keccak256Hash(hash.Bytes()).Bytes())
|
||||||
addrs[i] = addr
|
addrs[i] = addr
|
||||||
state.SetBalance(addrs[i], big.NewInt(1))
|
sdb.SetBalance(addrs[i], big.NewInt(1), tracing.BalanceChangeUnspecified)
|
||||||
if _, ok := m[addr]; ok {
|
if _, ok := m[addr]; ok {
|
||||||
t.Fatalf("bad")
|
t.Fatalf("bad")
|
||||||
} else {
|
} else {
|
||||||
m[addr] = true
|
m[addr] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.Commit(true)
|
sdb.Commit(true)
|
||||||
root := state.IntermediateRoot(true)
|
root := sdb.IntermediateRoot(true)
|
||||||
|
|
||||||
trie, err := statedb.OpenTrie(root)
|
trie, err := statedb.OpenTrie(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
accountRangeTest(t, &trie, state, common.Hash{}, AccountRangeMaxResults/2, AccountRangeMaxResults/2)
|
accountRangeTest(t, &trie, sdb, common.Hash{}, AccountRangeMaxResults/2, AccountRangeMaxResults/2)
|
||||||
// test pagination
|
// test pagination
|
||||||
firstResult := accountRangeTest(t, &trie, state, common.Hash{}, AccountRangeMaxResults, AccountRangeMaxResults)
|
firstResult := accountRangeTest(t, &trie, sdb, common.Hash{}, AccountRangeMaxResults, AccountRangeMaxResults)
|
||||||
secondResult := accountRangeTest(t, &trie, state, common.BytesToHash(firstResult.Next), AccountRangeMaxResults, AccountRangeMaxResults)
|
secondResult := accountRangeTest(t, &trie, sdb, common.BytesToHash(firstResult.Next), AccountRangeMaxResults, AccountRangeMaxResults)
|
||||||
|
|
||||||
hList := make(resultHash, 0)
|
hList := make(resultHash, 0)
|
||||||
for addr1 := range firstResult.Accounts {
|
for addr1 := range firstResult.Accounts {
|
||||||
|
|
@ -111,7 +114,7 @@ func TestAccountRange(t *testing.T) {
|
||||||
// set and get an even split between the first and second sets.
|
// set and get an even split between the first and second sets.
|
||||||
sort.Sort(hList)
|
sort.Sort(hList)
|
||||||
middleH := hList[AccountRangeMaxResults/2]
|
middleH := hList[AccountRangeMaxResults/2]
|
||||||
middleResult := accountRangeTest(t, &trie, state, middleH, AccountRangeMaxResults, AccountRangeMaxResults)
|
middleResult := accountRangeTest(t, &trie, sdb, middleH, AccountRangeMaxResults, AccountRangeMaxResults)
|
||||||
missing, infirst, insecond := 0, 0, 0
|
missing, infirst, insecond := 0, 0, 0
|
||||||
for h := range middleResult.Accounts {
|
for h := range middleResult.Accounts {
|
||||||
if _, ok := firstResult.Accounts[h]; ok {
|
if _, ok := firstResult.Accounts[h]; ok {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -47,6 +48,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/filters"
|
"github.com/XinFinOrg/XDPoSChain/eth/filters"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/gasprice"
|
"github.com/XinFinOrg/XDPoSChain/eth/gasprice"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/hooks"
|
"github.com/XinFinOrg/XDPoSChain/eth/hooks"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/event"
|
"github.com/XinFinOrg/XDPoSChain/event"
|
||||||
"github.com/XinFinOrg/XDPoSChain/internal/ethapi"
|
"github.com/XinFinOrg/XDPoSChain/internal/ethapi"
|
||||||
|
|
@ -184,6 +186,17 @@ func New(stack *node.Node, config *ethconfig.Config, XDCXServ *XDCx.XDCX, lendin
|
||||||
Preimages: config.Preimages,
|
Preimages: config.Preimages,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if config.VMTrace != "" {
|
||||||
|
var traceConfig json.RawMessage
|
||||||
|
if config.VMTraceConfig != "" {
|
||||||
|
traceConfig = json.RawMessage(config.VMTraceConfig)
|
||||||
|
}
|
||||||
|
t, err := tracers.LiveDirectory.New(config.VMTrace, traceConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to create tracer %s: %v", config.VMTrace, err)
|
||||||
|
}
|
||||||
|
vmConfig.Tracer = t
|
||||||
|
}
|
||||||
if eth.chainConfig.XDPoS != nil {
|
if eth.chainConfig.XDPoS != nil {
|
||||||
c := eth.engine.(*XDPoS.XDPoS)
|
c := eth.engine.(*XDPoS.XDPoS)
|
||||||
c.GetXDCXService = func() utils.TradingService {
|
c.GetXDCXService = func() utils.TradingService {
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,10 @@ type Config struct {
|
||||||
// Enables tracking of SHA3 preimages in the VM
|
// Enables tracking of SHA3 preimages in the VM
|
||||||
EnablePreimageRecording bool
|
EnablePreimageRecording bool
|
||||||
|
|
||||||
|
// Enables VM tracing
|
||||||
|
VMTrace string
|
||||||
|
VMTraceConfig string
|
||||||
|
|
||||||
// RPCGasCap is the global gas cap for eth-call variants.
|
// RPCGasCap is the global gas cap for eth-call variants.
|
||||||
RPCGasCap uint64
|
RPCGasCap uint64
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import (
|
||||||
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
|
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/util"
|
"github.com/XinFinOrg/XDPoSChain/eth/util"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
|
|
@ -293,7 +294,7 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
|
||||||
}
|
}
|
||||||
if len(rewards) > 0 {
|
if len(rewards) > 0 {
|
||||||
for holder, reward := range rewards {
|
for holder, reward := range rewards {
|
||||||
stateBlock.AddBalance(holder, reward)
|
stateBlock.AddBalance(holder, reward, tracing.BalanceIncreaseRewardMineBlock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
voterResults[signer] = rewards
|
voterResults[signer] = rewards
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/contracts"
|
"github.com/XinFinOrg/XDPoSChain/contracts"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/util"
|
"github.com/XinFinOrg/XDPoSChain/eth/util"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
|
|
@ -299,7 +300,7 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
|
||||||
}
|
}
|
||||||
if len(rewards) > 0 {
|
if len(rewards) > 0 {
|
||||||
for holder, reward := range rewards {
|
for holder, reward := range rewards {
|
||||||
stateBlock.AddBalance(holder, reward)
|
stateBlock.AddBalance(holder, reward, tracing.BalanceIncreaseRewardMineBlock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rewardResults[signer] = rewards
|
rewardResults[signer] = rewards
|
||||||
|
|
@ -337,7 +338,7 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf
|
||||||
}
|
}
|
||||||
if len(rewards) > 0 {
|
if len(rewards) > 0 {
|
||||||
for holder, reward := range rewards {
|
for holder, reward := range rewards {
|
||||||
stateBlock.AddBalance(holder, reward)
|
stateBlock.AddBalance(holder, reward, tracing.BalanceIncreaseRewardMineBlock)
|
||||||
rewardSum.Add(rewardSum, reward)
|
rewardSum.Add(rewardSum, reward)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@ func (eth *Ethereum) StateAtBlock(ctx context.Context, block *types.Block, reexe
|
||||||
}
|
}
|
||||||
|
|
||||||
// stateAtTransaction returns the execution environment of a certain transaction.
|
// stateAtTransaction returns the execution environment of a certain transaction.
|
||||||
func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
|
func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
|
||||||
// Short circuit if it's genesis block.
|
// Short circuit if it's genesis block.
|
||||||
if block.NumberU64() == 0 {
|
if block.NumberU64() == 0 {
|
||||||
return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis")
|
return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis")
|
||||||
|
|
@ -224,7 +224,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
|
||||||
txContext := core.NewEVMTxContext(msg)
|
txContext := core.NewEVMTxContext(msg)
|
||||||
context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
|
context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
|
||||||
if idx == txIndex {
|
if idx == txIndex {
|
||||||
return msg, context, statedb, release, nil
|
return tx, context, statedb, release, nil
|
||||||
}
|
}
|
||||||
// Not yet the searched for transaction, execute on top of the current state
|
// Not yet the searched for transaction, execute on top of the current state
|
||||||
vmenv := vm.NewEVM(context, txContext, statedb, nil, eth.blockchain.Config(), vm.Config{})
|
vmenv := vm.NewEVM(context, txContext, statedb, nil, eth.blockchain.Config(), vm.Config{})
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ type Backend interface {
|
||||||
Engine() consensus.Engine
|
Engine() consensus.Engine
|
||||||
ChainDb() ethdb.Database
|
ChainDb() ethdb.Database
|
||||||
StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, StateReleaseFunc, error)
|
StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, StateReleaseFunc, error)
|
||||||
StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, StateReleaseFunc, error)
|
StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, StateReleaseFunc, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// API is the collection of tracing APIs exposed over the private debugging endpoint.
|
// API is the collection of tracing APIs exposed over the private debugging endpoint.
|
||||||
|
|
@ -280,14 +280,12 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
|
||||||
TxIndex: i,
|
TxIndex: i,
|
||||||
TxHash: tx.Hash(),
|
TxHash: tx.Hash(),
|
||||||
}
|
}
|
||||||
res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
|
res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, task.statedb, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()}
|
task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()}
|
||||||
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
|
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
|
||||||
task.statedb.Finalise(api.backend.ChainConfig().IsEIP158(task.block.Number()))
|
|
||||||
task.results[i] = &txTraceResult{TxHash: tx.Hash(), Result: res}
|
task.results[i] = &txTraceResult{TxHash: tx.Hash(), Result: res}
|
||||||
}
|
}
|
||||||
// Tracing state is used up, queue it for de-referencing. Note the
|
// Tracing state is used up, queue it for de-referencing. Note the
|
||||||
|
|
@ -580,7 +578,6 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
||||||
var (
|
var (
|
||||||
txs = block.Transactions()
|
txs = block.Transactions()
|
||||||
blockHash = block.Hash()
|
blockHash = block.Hash()
|
||||||
is158 = api.backend.ChainConfig().IsEIP158(block.Number())
|
|
||||||
blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||||
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
|
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
|
||||||
results = make([]*txTraceResult, len(txs))
|
results = make([]*txTraceResult, len(txs))
|
||||||
|
|
@ -604,14 +601,11 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
||||||
TxIndex: i,
|
TxIndex: i,
|
||||||
TxHash: tx.Hash(),
|
TxHash: tx.Hash(),
|
||||||
}
|
}
|
||||||
res, err := api.traceTx(ctx, msg, txctx, blockCtx, statedb, config)
|
res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, statedb, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
results[i] = &txTraceResult{TxHash: tx.Hash(), Result: res}
|
results[i] = &txTraceResult{TxHash: tx.Hash(), Result: res}
|
||||||
// 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(is158)
|
|
||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
@ -659,7 +653,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
|
||||||
// concurrent use.
|
// concurrent use.
|
||||||
// See: https://github.com/ethereum/go-ethereum/issues/29114
|
// See: https://github.com/ethereum/go-ethereum/issues/29114
|
||||||
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||||
res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config)
|
res, err := api.traceTx(ctx, txs[task.index], msg, txctx, blockCtx, task.statedb, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()}
|
results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()}
|
||||||
continue
|
continue
|
||||||
|
|
@ -742,11 +736,22 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
msg, vmctx, statedb, release, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec)
|
tx, vmctx, statedb, release, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer release()
|
defer release()
|
||||||
|
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
|
||||||
|
var balance *big.Int
|
||||||
|
if tx.To() != nil {
|
||||||
|
if value, ok := feeCapacity[*tx.To()]; ok {
|
||||||
|
balance = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg, err := core.TransactionToMessage(tx, types.MakeSigner(api.backend.ChainConfig(), block.Number()), balance, block.Number(), block.BaseFee())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
txctx := &Context{
|
txctx := &Context{
|
||||||
BlockHash: blockHash,
|
BlockHash: blockHash,
|
||||||
|
|
@ -754,7 +759,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
|
||||||
TxIndex: int(index),
|
TxIndex: int(index),
|
||||||
TxHash: hash,
|
TxHash: hash,
|
||||||
}
|
}
|
||||||
return api.traceTx(ctx, msg, txctx, vmctx, statedb, config)
|
return api.traceTx(ctx, tx, msg, txctx, vmctx, statedb, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraceCall lets you trace a given eth_call. It collects the structured logs
|
// TraceCall lets you trace a given eth_call. It collects the structured logs
|
||||||
|
|
@ -815,39 +820,49 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
|
||||||
config.BlockOverrides.Apply(&vmctx)
|
config.BlockOverrides.Apply(&vmctx)
|
||||||
}
|
}
|
||||||
// Execute the trace
|
// Execute the trace
|
||||||
msg, err := args.ToMessage(api.backend, block.Number(), api.backend.RPCGasCap(), vmctx.BaseFee)
|
if err := args.CallDefaults(api.backend.RPCGasCap(), vmctx.BaseFee, api.backend.ChainConfig().ChainId); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var traceConfig *TraceConfig
|
var (
|
||||||
|
msg = args.ToMessage(api.backend, vmctx.BaseFee)
|
||||||
|
tx = args.ToTransaction()
|
||||||
|
traceConfig *TraceConfig
|
||||||
|
)
|
||||||
if config != nil {
|
if config != nil {
|
||||||
traceConfig = &config.TraceConfig
|
traceConfig = &config.TraceConfig
|
||||||
}
|
}
|
||||||
return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig)
|
return api.traceTx(ctx, tx, msg, new(Context), vmctx, statedb, traceConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// traceTx configures a new tracer according to the provided configuration, and
|
// traceTx configures a new tracer according to the provided configuration, and
|
||||||
// executes the given message in the provided environment. The return value will
|
// executes the given message in the provided environment. The return value will
|
||||||
// be tracer dependent.
|
// 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) {
|
func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
|
||||||
var (
|
var (
|
||||||
tracer Tracer
|
tracer *Tracer
|
||||||
err error
|
err error
|
||||||
timeout = defaultTraceTimeout
|
timeout = defaultTraceTimeout
|
||||||
txContext = core.NewEVMTxContext(message)
|
usedGas uint64
|
||||||
)
|
)
|
||||||
if config == nil {
|
if config == nil {
|
||||||
config = &TraceConfig{}
|
config = &TraceConfig{}
|
||||||
}
|
}
|
||||||
// Default tracer is the struct logger
|
// Default tracer is the struct logger
|
||||||
tracer = logger.NewStructLogger(config.Config)
|
if config.Tracer == nil {
|
||||||
if config.Tracer != nil {
|
logger := logger.NewStructLogger(config.Config)
|
||||||
|
tracer = &Tracer{
|
||||||
|
Hooks: logger.Hooks(),
|
||||||
|
GetResult: logger.GetResult,
|
||||||
|
Stop: logger.Stop,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
tracer, err = DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig)
|
tracer, err = DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vmenv := vm.NewEVM(vmctx, txContext, statedb, nil, api.backend.ChainConfig(), vm.Config{Tracer: tracer, NoBaseFee: true})
|
vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: big.NewInt(0)}, statedb, nil, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true})
|
||||||
|
statedb.SetLogger(tracer.Hooks)
|
||||||
|
|
||||||
// Define a meaningful timeout of a single transaction trace
|
// Define a meaningful timeout of a single transaction trace
|
||||||
if config.Timeout != nil {
|
if config.Timeout != nil {
|
||||||
|
|
@ -866,9 +881,18 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Conte
|
||||||
}()
|
}()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
|
||||||
|
var balance *big.Int
|
||||||
|
if tx.To() != nil {
|
||||||
|
if value, ok := feeCapacity[*tx.To()]; ok {
|
||||||
|
balance = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Call SetTxContext to clear out the statedb access list
|
// Call SetTxContext to clear out the statedb access list
|
||||||
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
|
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
|
||||||
if _, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit), common.Address{}); err != nil {
|
_, _, err, _ = core.ApplyTransactionWithEVM(message, api.backend.ChainConfig(), new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, vmenv, balance, common.Address{})
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("tracing failed: %w", err)
|
return nil, fmt.Errorf("tracing failed: %w", err)
|
||||||
}
|
}
|
||||||
return tracer.GetResult()
|
return tracer.GetResult()
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reex
|
||||||
return statedb, release, nil
|
return statedb, release, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, StateReleaseFunc, error) {
|
func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, StateReleaseFunc, error) {
|
||||||
parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return nil, vm.BlockContext{}, nil, nil, errBlockNotFound
|
return nil, vm.BlockContext{}, nil, nil, errBlockNotFound
|
||||||
|
|
@ -178,7 +178,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
|
||||||
txContext := core.NewEVMTxContext(msg)
|
txContext := core.NewEVMTxContext(msg)
|
||||||
context := core.NewEVMBlockContext(block.Header(), b.chain, nil)
|
context := core.NewEVMBlockContext(block.Header(), b.chain, nil)
|
||||||
if idx == txIndex {
|
if idx == txIndex {
|
||||||
return msg, context, statedb, release, nil
|
return tx, context, statedb, release, nil
|
||||||
}
|
}
|
||||||
vmenv := vm.NewEVM(context, txContext, statedb, nil, b.chainConfig, vm.Config{})
|
vmenv := vm.NewEVM(context, txContext, statedb, nil, b.chainConfig, vm.Config{})
|
||||||
owner := common.Address{}
|
owner := common.Address{}
|
||||||
|
|
@ -667,7 +667,6 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||||
From: &accounts[0].addr,
|
From: &accounts[0].addr,
|
||||||
// BLOCKNUMBER PUSH1 MSTORE
|
// BLOCKNUMBER PUSH1 MSTORE
|
||||||
Input: newRPCBytes(common.Hex2Bytes("4360005260206000f3")),
|
Input: newRPCBytes(common.Hex2Bytes("4360005260206000f3")),
|
||||||
//&hexutil.Bytes{0x43}, // blocknumber
|
|
||||||
},
|
},
|
||||||
config: &TraceCallConfig{
|
config: &TraceCallConfig{
|
||||||
BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
|
BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))},
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,14 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// Package tracers is a collection of JavaScript transaction tracers.
|
|
||||||
package tracers
|
package tracers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Context contains some contextual infos for a transaction execution that is not
|
// Context contains some contextual infos for a transaction execution that is not
|
||||||
|
|
@ -36,17 +33,19 @@ type Context struct {
|
||||||
TxHash common.Hash // Hash of the transaction being traced (zero if dangling call)
|
TxHash common.Hash // Hash of the transaction being traced (zero if dangling call)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tracer interface extends vm.EVMLogger and additionally
|
// The set of methods that must be exposed by a tracer
|
||||||
// allows collecting the tracing result.
|
// for it to be available through the RPC interface.
|
||||||
type Tracer interface {
|
// This involves a method to retrieve results and one to
|
||||||
vm.EVMLogger
|
// stop tracing.
|
||||||
GetResult() (json.RawMessage, error)
|
type Tracer struct {
|
||||||
|
*tracing.Hooks
|
||||||
|
GetResult func() (json.RawMessage, error)
|
||||||
// Stop terminates execution of the tracer at the first opportune moment.
|
// Stop terminates execution of the tracer at the first opportune moment.
|
||||||
Stop(err error)
|
Stop func(err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ctorFn func(*Context, json.RawMessage) (Tracer, error)
|
type ctorFn func(*Context, json.RawMessage) (*Tracer, error)
|
||||||
type jsCtorFn func(string, *Context, json.RawMessage) (Tracer, error)
|
type jsCtorFn func(string, *Context, json.RawMessage) (*Tracer, error)
|
||||||
|
|
||||||
type elem struct {
|
type elem struct {
|
||||||
ctor ctorFn
|
ctor ctorFn
|
||||||
|
|
@ -79,7 +78,7 @@ func (d *directory) RegisterJSEval(f jsCtorFn) {
|
||||||
// New returns a new instance of a tracer, by iterating through the
|
// New returns a new instance of a tracer, by iterating through the
|
||||||
// registered lookups. Name is either name of an existing tracer
|
// registered lookups. Name is either name of an existing tracer
|
||||||
// or an arbitrary JS code.
|
// or an arbitrary JS code.
|
||||||
func (d *directory) New(name string, ctx *Context, cfg json.RawMessage) (Tracer, error) {
|
func (d *directory) New(name string, ctx *Context, cfg json.RawMessage) (*Tracer, error) {
|
||||||
if elem, ok := d.elems[name]; ok {
|
if elem, ok := d.elems[name]; ok {
|
||||||
return elem.ctor(ctx, cfg)
|
return elem.ctor(ctx, cfg)
|
||||||
}
|
}
|
||||||
|
|
@ -97,27 +96,3 @@ func (d *directory) IsJS(name string) bool {
|
||||||
// JS eval will execute JS code
|
// JS eval will execute JS code
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
memoryPadLimit = 1024 * 1024
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetMemoryCopyPadded returns offset + size as a new slice.
|
|
||||||
// It zero-pads the slice if it extends beyond memory bounds.
|
|
||||||
func GetMemoryCopyPadded(m *vm.Memory, offset, size int64) ([]byte, error) {
|
|
||||||
if offset < 0 || size < 0 {
|
|
||||||
return nil, errors.New("offset or size must not be negative")
|
|
||||||
}
|
|
||||||
if int(offset+size) < m.Len() { // slice fully inside memory
|
|
||||||
return m.GetCopy(offset, size), nil
|
|
||||||
}
|
|
||||||
paddingNeeded := int(offset+size) - m.Len()
|
|
||||||
if paddingNeeded > memoryPadLimit {
|
|
||||||
return nil, fmt.Errorf("reached limit for padding memory slice: %d", paddingNeeded)
|
|
||||||
}
|
|
||||||
cpy := make([]byte, size)
|
|
||||||
if overlap := int64(m.Len()) - offset; overlap > 0 {
|
|
||||||
copy(cpy, m.GetPtr(offset, overlap))
|
|
||||||
}
|
|
||||||
return cpy, nil
|
|
||||||
}
|
|
||||||
10
eth/tracers/internal/tracetest/README.md
Normal file
10
eth/tracers/internal/tracetest/README.md
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Filling test cases
|
||||||
|
|
||||||
|
To fill test cases for the built-in tracers, the `makeTest.js` script can be used. Given a transaction on a dev/test network, `makeTest.js` will fetch its prestate and then traces with the given configuration.
|
||||||
|
In the Geth console do:
|
||||||
|
|
||||||
|
```terminal
|
||||||
|
let tx = '0x...'
|
||||||
|
loadScript('makeTest.js')
|
||||||
|
makeTest(tx, { tracer: 'callTracer' })
|
||||||
|
```
|
||||||
|
|
@ -18,6 +18,7 @@ package tracetest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -32,6 +33,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
|
|
@ -133,21 +135,26 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
|
||||||
GasLimit: uint64(test.Context.GasLimit),
|
GasLimit: uint64(test.Context.GasLimit),
|
||||||
BaseFee: test.Genesis.BaseFee,
|
BaseFee: test.Genesis.BaseFee,
|
||||||
}
|
}
|
||||||
statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
|
state = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
|
||||||
)
|
)
|
||||||
|
|
||||||
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
|
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.SetLogger(tracer.Hooks)
|
||||||
msg, err := core.TransactionToMessage(tx, signer, nil, nil, context.BaseFee)
|
msg, err := core.TransactionToMessage(tx, signer, nil, nil, context.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
}
|
}
|
||||||
evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, nil, test.Genesis.Config, vm.Config{Tracer: tracer})
|
evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
|
||||||
|
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{})
|
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to execute transaction: %v", err)
|
t.Fatalf("failed to execute transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil)
|
||||||
// Retrieve the trace result and compare against the expected.
|
// Retrieve the trace result and compare against the expected.
|
||||||
res, err := tracer.GetResult()
|
res, err := tracer.GetResult()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -232,7 +239,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
}
|
}
|
||||||
statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
|
state := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
|
||||||
|
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
@ -241,8 +248,8 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed to create call tracer: %v", err)
|
b.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
evm := vm.NewEVM(context, txContext, statedb, nil, test.Genesis.Config, vm.Config{Tracer: tracer})
|
evm := vm.NewEVM(context, txContext, state, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
|
||||||
snap := statedb.Snapshot()
|
snap := state.Snapshot()
|
||||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||||
if _, err = st.TransitionDb(common.Address{}); err != nil {
|
if _, err = st.TransitionDb(common.Address{}); err != nil {
|
||||||
b.Fatalf("failed to execute transaction: %v", err)
|
b.Fatalf("failed to execute transaction: %v", err)
|
||||||
|
|
@ -250,19 +257,19 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
|
||||||
if _, err = tracer.GetResult(); err != nil {
|
if _, err = tracer.GetResult(); err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
statedb.RevertToSnapshot(snap)
|
state.RevertToSnapshot(snap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInternals(t *testing.T) {
|
func TestInternals(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
|
config = params.MainnetChainConfig
|
||||||
to = common.HexToAddress("0x00000000000000000000000000000000deadbeef")
|
to = common.HexToAddress("0x00000000000000000000000000000000deadbeef")
|
||||||
origin = common.HexToAddress("0x00000000000000000000000000000000feed")
|
originHex = "0x71562b71999873db5b286df957af199ec94617f7"
|
||||||
txContext = vm.TxContext{
|
origin = common.HexToAddress(originHex)
|
||||||
Origin: origin,
|
signer = types.LatestSigner(config)
|
||||||
GasPrice: big.NewInt(1),
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
}
|
context = vm.BlockContext{
|
||||||
context = vm.BlockContext{
|
|
||||||
CanTransfer: core.CanTransfer,
|
CanTransfer: core.CanTransfer,
|
||||||
Transfer: core.Transfer,
|
Transfer: core.Transfer,
|
||||||
Coinbase: common.Address{},
|
Coinbase: common.Address{},
|
||||||
|
|
@ -270,9 +277,10 @@ func TestInternals(t *testing.T) {
|
||||||
Time: 5,
|
Time: 5,
|
||||||
Difficulty: big.NewInt(0x30000),
|
Difficulty: big.NewInt(0x30000),
|
||||||
GasLimit: uint64(6000000),
|
GasLimit: uint64(6000000),
|
||||||
|
BaseFee: new(big.Int),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
mkTracer := func(name string, cfg json.RawMessage) tracers.Tracer {
|
mkTracer := func(name string, cfg json.RawMessage) *tracers.Tracer {
|
||||||
tr, err := tracers.DefaultDirectory.New(name, nil, cfg)
|
tr, err := tracers.DefaultDirectory.New(name, nil, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
|
|
@ -283,7 +291,7 @@ func TestInternals(t *testing.T) {
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
code []byte
|
code []byte
|
||||||
tracer tracers.Tracer
|
tracer *tracers.Tracer
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
|
@ -297,13 +305,13 @@ func TestInternals(t *testing.T) {
|
||||||
byte(vm.CALL),
|
byte(vm.CALL),
|
||||||
},
|
},
|
||||||
tracer: mkTracer("callTracer", nil),
|
tracer: mkTracer("callTracer", nil),
|
||||||
want: `{"from":"0x000000000000000000000000000000000000feed","gas":"0x13880","gasUsed":"0x54d8","to":"0x00000000000000000000000000000000deadbeef","input":"0x","calls":[{"from":"0x00000000000000000000000000000000deadbeef","gas":"0xe01a","gasUsed":"0x0","to":"0x00000000000000000000000000000000000000ff","input":"0x","value":"0x0","type":"CALL"}],"value":"0x0","type":"CALL"}`,
|
want: fmt.Sprintf(`{"from":"%s","gas":"0x13880","gasUsed":"0x54d8","to":"0x00000000000000000000000000000000deadbeef","input":"0x","calls":[{"from":"0x00000000000000000000000000000000deadbeef","gas":"0xe01a","gasUsed":"0x0","to":"0x00000000000000000000000000000000000000ff","input":"0x","value":"0x0","type":"CALL"}],"value":"0x0","type":"CALL"}`, originHex),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Stack depletion in LOG0",
|
name: "Stack depletion in LOG0",
|
||||||
code: []byte{byte(vm.LOG3)},
|
code: []byte{byte(vm.LOG3)},
|
||||||
tracer: mkTracer("callTracer", json.RawMessage(`{ "withLog": true }`)),
|
tracer: mkTracer("callTracer", json.RawMessage(`{ "withLog": true }`)),
|
||||||
want: `{"from":"0x000000000000000000000000000000000000feed","gas":"0x13880","gasUsed":"0x13880","to":"0x00000000000000000000000000000000deadbeef","input":"0x","error":"stack underflow (0 \u003c=\u003e 5)","value":"0x0","type":"CALL"}`,
|
want: fmt.Sprintf(`{"from":"%s","gas":"0x13880","gasUsed":"0x13880","to":"0x00000000000000000000000000000000deadbeef","input":"0x","error":"stack underflow (0 \u003c=\u003e 5)","value":"0x0","type":"CALL"}`, originHex),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Mem expansion in LOG0",
|
name: "Mem expansion in LOG0",
|
||||||
|
|
@ -316,7 +324,7 @@ func TestInternals(t *testing.T) {
|
||||||
byte(vm.LOG0),
|
byte(vm.LOG0),
|
||||||
},
|
},
|
||||||
tracer: mkTracer("callTracer", json.RawMessage(`{ "withLog": true }`)),
|
tracer: mkTracer("callTracer", json.RawMessage(`{ "withLog": true }`)),
|
||||||
want: `{"from":"0x000000000000000000000000000000000000feed","gas":"0x13880","gasUsed":"0x5b9e","to":"0x00000000000000000000000000000000deadbeef","input":"0x","logs":[{"address":"0x00000000000000000000000000000000deadbeef","topics":[],"data":"0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","position":"0x0"}],"value":"0x0","type":"CALL"}`,
|
want: fmt.Sprintf(`{"from":"%s","gas":"0x13880","gasUsed":"0x5b9e","to":"0x00000000000000000000000000000000deadbeef","input":"0x","logs":[{"address":"0x00000000000000000000000000000000deadbeef","topics":[],"data":"0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","position":"0x0"}],"value":"0x0","type":"CALL"}`, originHex),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Leads to OOM on the prestate tracer
|
// Leads to OOM on the prestate tracer
|
||||||
|
|
@ -335,7 +343,7 @@ func TestInternals(t *testing.T) {
|
||||||
byte(vm.LOG0),
|
byte(vm.LOG0),
|
||||||
},
|
},
|
||||||
tracer: mkTracer("prestateTracer", nil),
|
tracer: mkTracer("prestateTracer", nil),
|
||||||
want: `{"0x0000000000000000000000000000000000000000":{"balance":"0x0"},"0x000000000000000000000000000000000000feed":{"balance":"0x1c6bf52647880"},"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600164ffffffffff60016000f560ff6000a0"}}`,
|
want: fmt.Sprintf(`{"0x0000000000000000000000000000000000000000":{"balance":"0x0"},"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600164ffffffffff60016000f560ff6000a0"},"%s":{"balance":"0x1c6bf52634000"}}`, originHex),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// CREATE2 which requires padding memory by prestate tracer
|
// CREATE2 which requires padding memory by prestate tracer
|
||||||
|
|
@ -354,11 +362,11 @@ func TestInternals(t *testing.T) {
|
||||||
byte(vm.LOG0),
|
byte(vm.LOG0),
|
||||||
},
|
},
|
||||||
tracer: mkTracer("prestateTracer", nil),
|
tracer: mkTracer("prestateTracer", nil),
|
||||||
want: `{"0x0000000000000000000000000000000000000000":{"balance":"0x0"},"0x000000000000000000000000000000000000feed":{"balance":"0x1c6bf52647880"},"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600160ff60016000f560ff6000a0"},"0x91ff9a805d36f54e3e272e230f3e3f5c1b330804":{"balance":"0x0"}}`,
|
want: fmt.Sprintf(`{"0x0000000000000000000000000000000000000000":{"balance":"0x0"},"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600160ff60016000f560ff6000a0"},"%s":{"balance":"0x1c6bf52634000"}}`, originHex),
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
statedb := tests.MakePreState(rawdb.NewMemoryDatabase(),
|
state := tests.MakePreState(rawdb.NewMemoryDatabase(),
|
||||||
types.GenesisAlloc{
|
types.GenesisAlloc{
|
||||||
to: types.Account{
|
to: types.Account{
|
||||||
Code: tc.code,
|
Code: tc.code,
|
||||||
|
|
@ -367,21 +375,31 @@ func TestInternals(t *testing.T) {
|
||||||
Balance: big.NewInt(500000000000000),
|
Balance: big.NewInt(500000000000000),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
evm := vm.NewEVM(context, txContext, statedb, nil, params.MainnetChainConfig, vm.Config{Tracer: tc.tracer})
|
state.SetLogger(tc.tracer.Hooks)
|
||||||
msg := &core.Message{
|
tx, err := types.SignNewTx(key, signer, &types.LegacyTx{
|
||||||
To: &to,
|
To: &to,
|
||||||
From: origin,
|
Value: big.NewInt(0),
|
||||||
Value: big.NewInt(0),
|
Gas: 80000,
|
||||||
GasLimit: 80000,
|
GasPrice: big.NewInt(1),
|
||||||
GasPrice: big.NewInt(0),
|
})
|
||||||
GasFeeCap: big.NewInt(0),
|
if err != nil {
|
||||||
GasTipCap: big.NewInt(0),
|
t.Fatalf("test %v: failed to sign transaction: %v", tc.name, err)
|
||||||
SkipAccountChecks: false,
|
|
||||||
}
|
}
|
||||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(msg.GasLimit))
|
txContext := vm.TxContext{
|
||||||
if _, err := st.TransitionDb(common.Address{}); err != nil {
|
Origin: origin,
|
||||||
|
GasPrice: tx.GasPrice(),
|
||||||
|
}
|
||||||
|
evm := vm.NewEVM(context, txContext, state, nil, config, vm.Config{Tracer: tc.tracer.Hooks})
|
||||||
|
msg, err := core.TransactionToMessage(tx, signer, nil, nil, big.NewInt(0))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %v: failed to create message: %v", tc.name, err)
|
||||||
|
}
|
||||||
|
tc.tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
|
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{})
|
||||||
|
if err != nil {
|
||||||
t.Fatalf("test %v: failed to execute transaction: %v", tc.name, err)
|
t.Fatalf("test %v: failed to execute transaction: %v", tc.name, err)
|
||||||
}
|
}
|
||||||
|
tc.tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil)
|
||||||
// Retrieve the trace result and compare against the expected
|
// Retrieve the trace result and compare against the expected
|
||||||
res, err := tc.tracer.GetResult()
|
res, err := tc.tracer.GetResult()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -444,13 +462,14 @@ func testContractTracer(tracerName string, dirPath string, t *testing.T) {
|
||||||
Difficulty: (*big.Int)(test.Context.Difficulty),
|
Difficulty: (*big.Int)(test.Context.Difficulty),
|
||||||
GasLimit: uint64(test.Context.GasLimit),
|
GasLimit: uint64(test.Context.GasLimit),
|
||||||
}
|
}
|
||||||
statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
|
state = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
|
||||||
)
|
)
|
||||||
|
|
||||||
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
|
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
evm := vm.NewEVM(context, txContext, statedb, nil, test.Genesis.Config, vm.Config{Tracer: tracer})
|
evm := vm.NewEVM(context, txContext, state, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
|
||||||
msg, err := core.TransactionToMessage(tx, signer, nil, nil, nil)
|
msg, err := core.TransactionToMessage(tx, signer, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,9 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
||||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
"github.com/XinFinOrg/XDPoSChain/tests"
|
"github.com/XinFinOrg/XDPoSChain/tests"
|
||||||
|
|
||||||
// Force-load the native, to trigger registration
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// flatCallTrace is the result of a callTracerParity run.
|
// flatCallTrace is the result of a callTracerParity run.
|
||||||
|
|
@ -95,23 +93,26 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
|
||||||
Difficulty: (*big.Int)(test.Context.Difficulty),
|
Difficulty: (*big.Int)(test.Context.Difficulty),
|
||||||
GasLimit: uint64(test.Context.GasLimit),
|
GasLimit: uint64(test.Context.GasLimit),
|
||||||
}
|
}
|
||||||
statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
|
state := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
|
||||||
|
|
||||||
// Create the tracer, the EVM environment and run it
|
// Create the tracer, the EVM environment and run it
|
||||||
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
|
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create call tracer: %v", err)
|
return fmt.Errorf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.SetLogger(tracer.Hooks)
|
||||||
msg, err := core.TransactionToMessage(tx, signer, nil, context.BlockNumber, context.BaseFee)
|
msg, err := core.TransactionToMessage(tx, signer, nil, context.BlockNumber, context.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to prepare transaction for tracing: %v", err)
|
return fmt.Errorf("failed to prepare transaction for tracing: %v", err)
|
||||||
}
|
}
|
||||||
evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, nil, test.Genesis.Config, vm.Config{Tracer: tracer})
|
evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
|
||||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
|
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{})
|
||||||
if _, err = st.TransitionDb(common.Address{}); err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to execute transaction: %v", err)
|
return fmt.Errorf("failed to execute transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil)
|
||||||
|
|
||||||
// Retrieve the trace result and compare against the etalon
|
// Retrieve the trace result and compare against the etalon
|
||||||
res, err := tracer.GetResult()
|
res, err := tracer.GetResult()
|
||||||
|
|
@ -123,7 +124,7 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
|
||||||
return fmt.Errorf("failed to unmarshal trace result: %v", err)
|
return fmt.Errorf("failed to unmarshal trace result: %v", err)
|
||||||
}
|
}
|
||||||
if !jsonEqualFlat(ret, test.Result) {
|
if !jsonEqualFlat(ret, test.Result) {
|
||||||
t.Logf("tracer name: %s", tracerName)
|
t.Logf("test %s failed", filename)
|
||||||
|
|
||||||
// uncomment this for easier debugging
|
// uncomment this for easier debugging
|
||||||
// have, _ := json.MarshalIndent(ret, "", " ")
|
// have, _ := json.MarshalIndent(ret, "", " ")
|
||||||
|
|
|
||||||
48
eth/tracers/internal/tracetest/makeTest.js
Normal file
48
eth/tracers/internal/tracetest/makeTest.js
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
// makeTest generates a test for the configured tracer by running
|
||||||
|
// a prestate reassembled and a call trace run, assembling all the
|
||||||
|
// gathered information into a test case.
|
||||||
|
var makeTest = function(tx, traceConfig) {
|
||||||
|
// Generate the genesis block from the block, transaction and prestate data
|
||||||
|
var block = eth.getBlock(eth.getTransaction(tx).blockHash);
|
||||||
|
var genesis = eth.getBlock(block.parentHash);
|
||||||
|
|
||||||
|
delete genesis.gasUsed;
|
||||||
|
delete genesis.logsBloom;
|
||||||
|
delete genesis.parentHash;
|
||||||
|
delete genesis.receiptsRoot;
|
||||||
|
delete genesis.sha3Uncles;
|
||||||
|
delete genesis.size;
|
||||||
|
delete genesis.transactions;
|
||||||
|
delete genesis.transactionsRoot;
|
||||||
|
delete genesis.uncles;
|
||||||
|
|
||||||
|
genesis.gasLimit = genesis.gasLimit.toString();
|
||||||
|
genesis.number = genesis.number.toString();
|
||||||
|
genesis.timestamp = genesis.timestamp.toString();
|
||||||
|
|
||||||
|
genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer"});
|
||||||
|
for (var key in genesis.alloc) {
|
||||||
|
var nonce = genesis.alloc[key].nonce;
|
||||||
|
if (nonce) {
|
||||||
|
genesis.alloc[key].nonce = nonce.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
genesis.config = admin.nodeInfo.protocols.eth.config;
|
||||||
|
|
||||||
|
// Generate the call trace and produce the test input
|
||||||
|
var result = debug.traceTransaction(tx, traceConfig);
|
||||||
|
delete result.time;
|
||||||
|
|
||||||
|
console.log(JSON.stringify({
|
||||||
|
genesis: genesis,
|
||||||
|
context: {
|
||||||
|
number: block.number.toString(),
|
||||||
|
difficulty: block.difficulty,
|
||||||
|
timestamp: block.timestamp.toString(),
|
||||||
|
gasLimit: block.gasLimit.toString(),
|
||||||
|
miner: block.miner,
|
||||||
|
},
|
||||||
|
input: eth.getRawTransaction(tx),
|
||||||
|
result: result,
|
||||||
|
}, null, 2));
|
||||||
|
}
|
||||||
|
|
@ -103,22 +103,37 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
|
||||||
GasLimit: uint64(test.Context.GasLimit),
|
GasLimit: uint64(test.Context.GasLimit),
|
||||||
BaseFee: test.Genesis.BaseFee,
|
BaseFee: test.Genesis.BaseFee,
|
||||||
}
|
}
|
||||||
statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
|
state = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
|
||||||
)
|
)
|
||||||
|
|
||||||
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
|
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
// msg, err := core.TransactionToMessage(tx, signer, nil, context.BlockNumber, context.BaseFee)
|
||||||
|
// if err != nil {
|
||||||
|
// t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
|
// }
|
||||||
|
// evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, nil, test.Genesis.Config, vm.Config{Tracer: tracer})
|
||||||
|
// st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||||
|
// if _, err = st.TransitionDb(common.Address{}); err != nil {
|
||||||
|
// t.Fatalf("failed to execute transaction: %v", err)
|
||||||
|
// }
|
||||||
|
// // Retrieve the trace result and compare against the expected.
|
||||||
|
|
||||||
|
state.SetLogger(tracer.Hooks)
|
||||||
msg, err := core.TransactionToMessage(tx, signer, nil, context.BlockNumber, context.BaseFee)
|
msg, err := core.TransactionToMessage(tx, signer, nil, context.BlockNumber, context.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
t.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
}
|
}
|
||||||
evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, nil, test.Genesis.Config, vm.Config{Tracer: tracer})
|
evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state, nil, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
|
||||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
if _, err = st.TransitionDb(common.Address{}); err != nil {
|
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()), common.Address{})
|
||||||
|
if err != nil {
|
||||||
t.Fatalf("failed to execute transaction: %v", err)
|
t.Fatalf("failed to execute transaction: %v", err)
|
||||||
}
|
}
|
||||||
// Retrieve the trace result and compare against the expected.
|
tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil)
|
||||||
|
// Retrieve the trace result and compare against the expected
|
||||||
res, err := tracer.GetResult()
|
res, err := tracer.GetResult()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to retrieve trace result: %v", err)
|
t.Fatalf("failed to retrieve trace result: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,16 @@
|
||||||
"value": "0x0",
|
"value": "0x0",
|
||||||
"gas": "0x1f97e",
|
"gas": "0x1f97e",
|
||||||
"gasUsed": "0x72de",
|
"gasUsed": "0x72de",
|
||||||
"input": "0x2e1a7d4d00000000000000000000000000000000000000000000000014d1120d7b160000"
|
"input": "0x2e1a7d4d00000000000000000000000000000000000000000000000014d1120d7b160000",
|
||||||
|
"calls": [{
|
||||||
|
"from":"0x6c06b16512b332e6cd8293a2974872674716ce18",
|
||||||
|
"gas":"0x8fc",
|
||||||
|
"gasUsed":"0x0",
|
||||||
|
"to":"0x66fdfd05e46126a07465ad24e40cc0597bc1ef31",
|
||||||
|
"input":"0x",
|
||||||
|
"error":"insufficient balance for transfer",
|
||||||
|
"value":"0x14d1120d7b160000",
|
||||||
|
"type":"CALL"
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,12 +63,29 @@
|
||||||
"address": "0x5f8a7e007172ba80afbff1b15f800eb0b260f224"
|
"address": "0x5f8a7e007172ba80afbff1b15f800eb0b260f224"
|
||||||
},
|
},
|
||||||
"traceAddress": [],
|
"traceAddress": [],
|
||||||
"subtraces": 0,
|
"subtraces": 1,
|
||||||
"transactionPosition": 74,
|
"transactionPosition": 74,
|
||||||
"transactionHash": "0x5ef60b27ac971c22a7d484e546e50093ca62300c8986d165154e47773764b6a4",
|
"transactionHash": "0x5ef60b27ac971c22a7d484e546e50093ca62300c8986d165154e47773764b6a4",
|
||||||
"blockNumber": 1555279,
|
"blockNumber": 1555279,
|
||||||
"blockHash": "0xd6c98d1b87dfa92a210d99bad2873adaf0c9e51fe43addc63fd9cca03a5c6f46",
|
"blockHash": "0xd6c98d1b87dfa92a210d99bad2873adaf0c9e51fe43addc63fd9cca03a5c6f46",
|
||||||
"time": "209.346µs"
|
"time": "209.346µs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"callType": "callcode",
|
||||||
|
"from": "0x5f8a7e007172ba80afbff1b15f800eb0b260f224",
|
||||||
|
"gas": "0xab31",
|
||||||
|
"to": "0x0000000000000000000000000000000000000004",
|
||||||
|
"value": "0x13"
|
||||||
|
},
|
||||||
|
"error": "insufficient balance for transfer",
|
||||||
|
"result": {},
|
||||||
|
"subtraces": 0,
|
||||||
|
"traceAddress": [
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"type": "call"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,23 @@
|
||||||
"gasUsed": "0x72de",
|
"gasUsed": "0x72de",
|
||||||
"output": "0x"
|
"output": "0x"
|
||||||
},
|
},
|
||||||
"subtraces": 0,
|
"subtraces": 1,
|
||||||
"traceAddress": [],
|
"traceAddress": [],
|
||||||
"type": "call"
|
"type": "call"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"callType": "call",
|
||||||
|
"from": "0x6c06b16512b332e6cd8293a2974872674716ce18",
|
||||||
|
"gas": "0x8fc",
|
||||||
|
"to": "0x66fdfd05e46126a07465ad24e40cc0597bc1ef31",
|
||||||
|
"value": "0x14d1120d7b160000"
|
||||||
|
},
|
||||||
|
"error": "insufficient balance for transfer",
|
||||||
|
"result": {},
|
||||||
|
"subtraces": 0,
|
||||||
|
"traceAddress": [0],
|
||||||
|
"type": "call"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,12 +70,25 @@
|
||||||
"output": "0x"
|
"output": "0x"
|
||||||
},
|
},
|
||||||
"traceAddress": [],
|
"traceAddress": [],
|
||||||
"subtraces": 0,
|
"subtraces": 1,
|
||||||
"transactionPosition": 26,
|
"transactionPosition": 26,
|
||||||
"transactionHash": "0xcb1090fa85d2a3da8326b75333e92b3dca89963c895d9c981bfdaa64643135e4",
|
"transactionHash": "0xcb1090fa85d2a3da8326b75333e92b3dca89963c895d9c981bfdaa64643135e4",
|
||||||
"blockNumber": 839247,
|
"blockNumber": 839247,
|
||||||
"blockHash": "0xce7ff7d84ca97f0f89d6065e2c12409a795c9f607cdb14aef0713cad5d7e311c",
|
"blockHash": "0xce7ff7d84ca97f0f89d6065e2c12409a795c9f607cdb14aef0713cad5d7e311c",
|
||||||
"time": "182.267µs"
|
"time": "182.267µs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"from": "0x76554b33410b6d90b7dc889bfed0451ad195f27e",
|
||||||
|
"gas": "0x25a18",
|
||||||
|
"init": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"value": "0xa"
|
||||||
|
},
|
||||||
|
"error": "insufficient balance for transfer",
|
||||||
|
"result": {},
|
||||||
|
"subtraces": 0,
|
||||||
|
"traceAddress": [0],
|
||||||
|
"type": "create"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -63,13 +63,28 @@
|
||||||
"address": "0x1d99a1a3efa9181f540f9e24fa6e4e08eb7844ca"
|
"address": "0x1d99a1a3efa9181f540f9e24fa6e4e08eb7844ca"
|
||||||
},
|
},
|
||||||
"traceAddress": [],
|
"traceAddress": [],
|
||||||
"subtraces": 1,
|
"subtraces": 2,
|
||||||
"transactionPosition": 14,
|
"transactionPosition": 14,
|
||||||
"transactionHash": "0xdd76f02407e2f8329303ba688e111cae4f7008ad0d14d6e42c5698424ea36d79",
|
"transactionHash": "0xdd76f02407e2f8329303ba688e111cae4f7008ad0d14d6e42c5698424ea36d79",
|
||||||
"blockNumber": 1555146,
|
"blockNumber": 1555146,
|
||||||
"blockHash": "0xafb4f1dd27b9054c805acb81a88ed04384788cb31d84164c21874935c81e5c7e",
|
"blockHash": "0xafb4f1dd27b9054c805acb81a88ed04384788cb31d84164c21874935c81e5c7e",
|
||||||
"time": "187.145µs"
|
"time": "187.145µs"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"from": "0x1d99a1a3efa9181f540f9e24fa6e4e08eb7844ca",
|
||||||
|
"gas": "0x4e79",
|
||||||
|
"init": "0x5a",
|
||||||
|
"value": "0x1"
|
||||||
|
},
|
||||||
|
"error": "insufficient balance for transfer",
|
||||||
|
"result": {},
|
||||||
|
"subtraces": 0,
|
||||||
|
"traceAddress": [
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"type": "create"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "suicide",
|
"type": "suicide",
|
||||||
"action": {
|
"action": {
|
||||||
|
|
@ -79,7 +94,7 @@
|
||||||
},
|
},
|
||||||
"result": null,
|
"result": null,
|
||||||
"traceAddress": [
|
"traceAddress": [
|
||||||
0
|
1
|
||||||
],
|
],
|
||||||
"subtraces": 0,
|
"subtraces": 0,
|
||||||
"transactionPosition": 14,
|
"transactionPosition": 14,
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
189
eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json
vendored
Normal file
189
eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json
vendored
Normal file
File diff suppressed because one or more lines are too long
63
eth/tracers/internal/tracetest/testdata/prestate_tracer/create_create.json
vendored
Normal file
63
eth/tracers/internal/tracetest/testdata/prestate_tracer/create_create.json
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
{
|
||||||
|
"genesis": {
|
||||||
|
"baseFeePerGas": "875000000",
|
||||||
|
"difficulty": "0",
|
||||||
|
"extraData": "0xd983010d05846765746888676f312e32312e318664617277696e",
|
||||||
|
"gasLimit": "11511229",
|
||||||
|
"hash": "0xd462585c6c5a3b3bf14850ebcde71b6615b9aaf6541403f9a0457212dd0502e0",
|
||||||
|
"miner": "0x0000000000000000000000000000000000000000",
|
||||||
|
"mixHash": "0xfa51e868d6a7c0728f18800e4cc8d4cc1c87430cc9975e947eb6c9c03599b4e2",
|
||||||
|
"nonce": "0x0000000000000000",
|
||||||
|
"number": "1",
|
||||||
|
"stateRoot": "0xd2ebe0a7f3572ffe3e5b4c78147376d3fca767f236e4dd23f9151acfec7cb0d1",
|
||||||
|
"timestamp": "1699617692",
|
||||||
|
"totalDifficulty": "0",
|
||||||
|
"withdrawals": [],
|
||||||
|
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||||
|
"alloc": {
|
||||||
|
"0x0000000000000000000000000000000000000000": {
|
||||||
|
"balance": "0x5208"
|
||||||
|
},
|
||||||
|
"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": {
|
||||||
|
"balance": "0x8ac7230489e80000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"chainId": 1337,
|
||||||
|
"homesteadBlock": 0,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip155Block": 0,
|
||||||
|
"eip158Block": 0,
|
||||||
|
"byzantiumBlock": 0,
|
||||||
|
"constantinopleBlock": 0,
|
||||||
|
"petersburgBlock": 0,
|
||||||
|
"istanbulBlock": 0,
|
||||||
|
"muirGlacierBlock": 0,
|
||||||
|
"berlinBlock": 0,
|
||||||
|
"londonBlock": 0,
|
||||||
|
"eip1559Block": 0,
|
||||||
|
"arrowGlacierBlock": 0,
|
||||||
|
"grayGlacierBlock": 0,
|
||||||
|
"shanghaiTime": 0,
|
||||||
|
"terminalTotalDifficulty": 0,
|
||||||
|
"terminalTotalDifficultyPassed": true,
|
||||||
|
"isDev": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"number": "2",
|
||||||
|
"difficulty": "0",
|
||||||
|
"timestamp": "1699617847",
|
||||||
|
"gasLimit": "11522469",
|
||||||
|
"miner": "0x0000000000000000000000000000000000000000"
|
||||||
|
},
|
||||||
|
"input": "0x02f902b48205398084b2d05e0085011b1f3f8083031ca88080b90258608060405234801561001057600080fd5b5060405161001d906100e3565b604051809103906000f080158015610039573d6000803e3d6000fd5b50600080546001600160a01b0319166001600160a01b039290921691821781556040517fc66247bafd1305823857fb4c3e651e684d918df8554ef560bbbcb025fdd017039190a26000546040516360fe47b160e01b8152600560048201526001600160a01b03909116906360fe47b190602401600060405180830381600087803b1580156100c657600080fd5b505af11580156100da573d6000803e3d6000fd5b505050506100ef565b60ca8061018e83390190565b6091806100fd6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806380de699314602d575b600080fd5b600054603f906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f3fea2646970667358221220dab781465e7f4cf20304cc388130a763508e20edd25b4bc8ea8f57743a0de8da64736f6c634300081700336080604052348015600f57600080fd5b5060ac8061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806360fe47b11460375780636d4ce63c146049575b600080fd5b60476042366004605e565b600055565b005b60005460405190815260200160405180910390f35b600060208284031215606f57600080fd5b503591905056fea264697066735822122049e09da6320793487d58eaa7b97f802618a062cbc35f08ca1ce92c17349141f864736f6c63430008170033c080a01d4fce93ad08bf413052645721f20e6136830cf5a2759fa57e76a134e90899a7a0399a72832d52118991dc04c4f9e1c0fec3d5e441ad7d4b055f0cf03130d8f815",
|
||||||
|
"result": {
|
||||||
|
"0x0000000000000000000000000000000000000000": {
|
||||||
|
"balance": "0x5208"
|
||||||
|
},
|
||||||
|
"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": {
|
||||||
|
"balance": "0x8ac7230489e80000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,59 +9,6 @@ import (
|
||||||
_ "github.com/XinFinOrg/XDPoSChain/eth/tracers/native"
|
_ "github.com/XinFinOrg/XDPoSChain/eth/tracers/native"
|
||||||
)
|
)
|
||||||
|
|
||||||
// To generate a new callTracer test, copy paste the makeTest method below into
|
|
||||||
// a Geth console and call it with a transaction hash you which to export.
|
|
||||||
|
|
||||||
/*
|
|
||||||
// makeTest generates a callTracer test by running a prestate reassembled and a
|
|
||||||
// call trace run, assembling all the gathered information into a test case.
|
|
||||||
var makeTest = function(tx, rewind) {
|
|
||||||
// Generate the genesis block from the block, transaction and prestate data
|
|
||||||
var block = eth.getBlock(eth.getTransaction(tx).blockHash);
|
|
||||||
var genesis = eth.getBlock(block.parentHash);
|
|
||||||
|
|
||||||
delete genesis.gasUsed;
|
|
||||||
delete genesis.logsBloom;
|
|
||||||
delete genesis.parentHash;
|
|
||||||
delete genesis.receiptsRoot;
|
|
||||||
delete genesis.sha3Uncles;
|
|
||||||
delete genesis.size;
|
|
||||||
delete genesis.transactions;
|
|
||||||
delete genesis.transactionsRoot;
|
|
||||||
delete genesis.uncles;
|
|
||||||
|
|
||||||
genesis.gasLimit = genesis.gasLimit.toString();
|
|
||||||
genesis.number = genesis.number.toString();
|
|
||||||
genesis.timestamp = genesis.timestamp.toString();
|
|
||||||
|
|
||||||
genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer", rewind: rewind});
|
|
||||||
for (var key in genesis.alloc) {
|
|
||||||
var nonce = genesis.alloc[key].nonce;
|
|
||||||
if (nonce) {
|
|
||||||
genesis.alloc[key].nonce = nonce.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
genesis.config = admin.nodeInfo.protocols.eth.config;
|
|
||||||
|
|
||||||
// Generate the call trace and produce the test input
|
|
||||||
var result = debug.traceTransaction(tx, {tracer: "callTracer", rewind: rewind});
|
|
||||||
delete result.time;
|
|
||||||
|
|
||||||
console.log(JSON.stringify({
|
|
||||||
genesis: genesis,
|
|
||||||
context: {
|
|
||||||
number: block.number.toString(),
|
|
||||||
difficulty: block.difficulty,
|
|
||||||
timestamp: block.timestamp.toString(),
|
|
||||||
gasLimit: block.gasLimit.toString(),
|
|
||||||
miner: block.miner,
|
|
||||||
},
|
|
||||||
input: eth.getRawTransaction(tx),
|
|
||||||
result: result,
|
|
||||||
}, null, 2));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// camel converts a snake cased input string into a camel cased output.
|
// camel converts a snake cased input string into a camel cased output.
|
||||||
func camel(str string) string {
|
func camel(str string) string {
|
||||||
pieces := strings.Split(str, "_")
|
pieces := strings.Split(str, "_")
|
||||||
|
|
|
||||||
81
eth/tracers/internal/util.go
Normal file
81
eth/tracers/internal/util.go
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
// Copyright 2023 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
memoryPadLimit = 1024 * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetMemoryCopyPadded returns offset + size as a new slice.
|
||||||
|
// It zero-pads the slice if it extends beyond memory bounds.
|
||||||
|
func GetMemoryCopyPadded(m []byte, offset, size int64) ([]byte, error) {
|
||||||
|
if offset < 0 || size < 0 {
|
||||||
|
return nil, errors.New("offset or size must not be negative")
|
||||||
|
}
|
||||||
|
length := int64(len(m))
|
||||||
|
if offset+size < length { // slice fully inside memory
|
||||||
|
return memoryCopy(m, offset, size), nil
|
||||||
|
}
|
||||||
|
paddingNeeded := offset + size - length
|
||||||
|
if paddingNeeded > memoryPadLimit {
|
||||||
|
return nil, fmt.Errorf("reached limit for padding memory slice: %d", paddingNeeded)
|
||||||
|
}
|
||||||
|
cpy := make([]byte, size)
|
||||||
|
if overlap := length - offset; overlap > 0 {
|
||||||
|
copy(cpy, MemoryPtr(m, offset, overlap))
|
||||||
|
}
|
||||||
|
return cpy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func memoryCopy(m []byte, offset, size int64) (cpy []byte) {
|
||||||
|
if size == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(m) > int(offset) {
|
||||||
|
cpy = make([]byte, size)
|
||||||
|
copy(cpy, m[offset:offset+size])
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemoryPtr returns a pointer to a slice of memory.
|
||||||
|
func MemoryPtr(m []byte, offset, size int64) []byte {
|
||||||
|
if size == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(m) > int(offset) {
|
||||||
|
return m[offset : offset+size]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back returns the n'th item in stack
|
||||||
|
func StackBack(st []uint256.Int, n int) *uint256.Int {
|
||||||
|
return &st[len(st)-n-1]
|
||||||
|
}
|
||||||
60
eth/tracers/internal/util_test.go
Normal file
60
eth/tracers/internal/util_test.go
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright 2023 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMemCopying(t *testing.T) {
|
||||||
|
for i, tc := range []struct {
|
||||||
|
memsize int64
|
||||||
|
offset int64
|
||||||
|
size int64
|
||||||
|
wantErr string
|
||||||
|
wantSize int
|
||||||
|
}{
|
||||||
|
{0, 0, 100, "", 100}, // Should pad up to 100
|
||||||
|
{0, 100, 0, "", 0}, // No need to pad (0 size)
|
||||||
|
{100, 50, 100, "", 100}, // Should pad 100-150
|
||||||
|
{100, 50, 5, "", 5}, // Wanted range fully within memory
|
||||||
|
{100, -50, 0, "offset or size must not be negative", 0}, // Error
|
||||||
|
{0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Error
|
||||||
|
{10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Error
|
||||||
|
|
||||||
|
} {
|
||||||
|
mem := vm.NewMemory()
|
||||||
|
mem.Resize(uint64(tc.memsize))
|
||||||
|
cpy, err := GetMemoryCopyPadded(mem.Data(), tc.offset, tc.size)
|
||||||
|
if want := tc.wantErr; want != "" {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("test %d: want '%v' have no error", i, want)
|
||||||
|
}
|
||||||
|
if have := err.Error(); want != have {
|
||||||
|
t.Fatalf("test %d: want '%v' have '%v'", i, want, have)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test %d: unexpected error: %v", i, err)
|
||||||
|
}
|
||||||
|
if want, have := tc.wantSize, len(cpy); have != want {
|
||||||
|
t.Fatalf("test %d: want %v have %v", i, want, have)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,11 +24,15 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers/internal"
|
||||||
jsassets "github.com/XinFinOrg/XDPoSChain/eth/tracers/js/internal/tracers"
|
jsassets "github.com/XinFinOrg/XDPoSChain/eth/tracers/js/internal/tracers"
|
||||||
"github.com/dop251/goja"
|
"github.com/dop251/goja"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
var assetTracers = make(map[string]string)
|
var assetTracers = make(map[string]string)
|
||||||
|
|
@ -40,9 +44,9 @@ func init() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error)
|
type ctorFn = func(*tracers.Context, json.RawMessage) (*tracers.Tracer, error)
|
||||||
lookup := func(code string) ctorFn {
|
lookup := func(code string) ctorFn {
|
||||||
return func(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
|
return func(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
|
||||||
return newJsTracer(code, ctx, cfg)
|
return newJsTracer(code, ctx, cfg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +99,7 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b
|
||||||
// JS functions on the relevant EVM hooks. It uses Goja as its JS engine.
|
// JS functions on the relevant EVM hooks. It uses Goja as its JS engine.
|
||||||
type jsTracer struct {
|
type jsTracer struct {
|
||||||
vm *goja.Runtime
|
vm *goja.Runtime
|
||||||
env *vm.EVM
|
env *tracing.VMContext
|
||||||
toBig toBigFn // Converts a hex string into a JS bigint
|
toBig toBigFn // Converts a hex string into a JS bigint
|
||||||
toBuf toBufFn // Converts a []byte into a JS buffer
|
toBuf toBufFn // Converts a []byte into a JS buffer
|
||||||
fromBuf fromBufFn // Converts an array, hex string or Uint8Array to a []byte
|
fromBuf fromBufFn // Converts an array, hex string or Uint8Array to a []byte
|
||||||
|
|
@ -103,7 +107,6 @@ type jsTracer struct {
|
||||||
activePrecompiles []common.Address // List of active precompiles at current block
|
activePrecompiles []common.Address // List of active precompiles at current block
|
||||||
traceStep bool // True if tracer object exposes a `step()` method
|
traceStep bool // True if tracer object exposes a `step()` method
|
||||||
traceFrame bool // True if tracer object exposes the `enter()` and `exit()` methods
|
traceFrame bool // True if tracer object exposes the `enter()` and `exit()` methods
|
||||||
gasLimit uint64 // Amount of gas bought for the whole tx
|
|
||||||
err error // Any error that should stop tracing
|
err error // Any error that should stop tracing
|
||||||
obj *goja.Object // Trace object
|
obj *goja.Object // Trace object
|
||||||
|
|
||||||
|
|
@ -133,7 +136,7 @@ type jsTracer struct {
|
||||||
// The methods `result` and `fault` are required to be present.
|
// The methods `result` and `fault` are required to be present.
|
||||||
// The methods `step`, `enter`, and `exit` are optional, but note that
|
// The methods `step`, `enter`, and `exit` are optional, but note that
|
||||||
// `enter` and `exit` always go together.
|
// `enter` and `exit` always go together.
|
||||||
func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
|
func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
|
||||||
vm := goja.New()
|
vm := goja.New()
|
||||||
// By default field names are exported to JS as is, i.e. capitalized.
|
// By default field names are exported to JS as is, i.e. capitalized.
|
||||||
vm.SetFieldNameMapper(goja.UncapFieldNameMapper())
|
vm.SetFieldNameMapper(goja.UncapFieldNameMapper())
|
||||||
|
|
@ -216,30 +219,62 @@ func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracer
|
||||||
t.frameValue = t.frame.setupObject()
|
t.frameValue = t.frame.setupObject()
|
||||||
t.frameResultValue = t.frameResult.setupObject()
|
t.frameResultValue = t.frameResult.setupObject()
|
||||||
t.logValue = t.log.setupObject()
|
t.logValue = t.log.setupObject()
|
||||||
return t, nil
|
|
||||||
|
return &tracers.Tracer{
|
||||||
|
Hooks: &tracing.Hooks{
|
||||||
|
OnTxStart: t.OnTxStart,
|
||||||
|
OnTxEnd: t.OnTxEnd,
|
||||||
|
OnEnter: t.OnEnter,
|
||||||
|
OnExit: t.OnExit,
|
||||||
|
OnOpcode: t.OnOpcode,
|
||||||
|
OnFault: t.OnFault,
|
||||||
|
},
|
||||||
|
GetResult: t.GetResult,
|
||||||
|
Stop: t.Stop,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureTxStart implements the Tracer interface and is invoked at the beginning of
|
// OnTxStart implements the Tracer interface and is invoked at the beginning of
|
||||||
// transaction processing.
|
// transaction processing.
|
||||||
func (t *jsTracer) CaptureTxStart(gasLimit uint64) {
|
func (t *jsTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
t.gasLimit = gasLimit
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaptureTxEnd implements the Tracer interface and is invoked at the end of
|
|
||||||
// transaction processing.
|
|
||||||
func (t *jsTracer) CaptureTxEnd(restGas uint64) {
|
|
||||||
t.ctx["gasUsed"] = t.vm.ToValue(t.gasLimit - restGas)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
|
||||||
func (t *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
|
||||||
cancel := func(err error) {
|
|
||||||
t.err = err
|
|
||||||
t.env.Cancel()
|
|
||||||
}
|
|
||||||
t.env = env
|
t.env = env
|
||||||
|
// Need statedb access for db object
|
||||||
db := &dbObj{db: env.StateDB, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf}
|
db := &dbObj{db: env.StateDB, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf}
|
||||||
t.dbValue = db.setupObject()
|
t.dbValue = db.setupObject()
|
||||||
|
// Update list of precompiles based on current block
|
||||||
|
rules := env.ChainConfig.Rules(env.BlockNumber)
|
||||||
|
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
||||||
|
t.ctx["block"] = t.vm.ToValue(t.env.BlockNumber.Uint64())
|
||||||
|
t.ctx["gas"] = t.vm.ToValue(tx.Gas())
|
||||||
|
gasPriceBig, err := t.toBig(t.vm, env.GasPrice.String())
|
||||||
|
if err != nil {
|
||||||
|
t.err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.ctx["gasPrice"] = gasPriceBig
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnTxEnd implements the Tracer interface and is invoked at the end of
|
||||||
|
// transaction processing.
|
||||||
|
func (t *jsTracer) OnTxEnd(receipt *types.Receipt, err error) {
|
||||||
|
if t.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
// Don't override vm error
|
||||||
|
if _, ok := t.ctx["error"]; !ok {
|
||||||
|
t.ctx["error"] = t.vm.ToValue(err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.ctx["gasUsed"] = t.vm.ToValue(receipt.GasUsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// onStart implements the Tracer interface to initialize the tracing operation.
|
||||||
|
func (t *jsTracer) onStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||||
|
if t.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
if create {
|
if create {
|
||||||
t.ctx["type"] = t.vm.ToValue("CREATE")
|
t.ctx["type"] = t.vm.ToValue("CREATE")
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -247,43 +282,32 @@ func (t *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Addr
|
||||||
}
|
}
|
||||||
fromVal, err := t.toBuf(t.vm, from.Bytes())
|
fromVal, err := t.toBuf(t.vm, from.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel(err)
|
t.err = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.ctx["from"] = fromVal
|
t.ctx["from"] = fromVal
|
||||||
toVal, err := t.toBuf(t.vm, to.Bytes())
|
toVal, err := t.toBuf(t.vm, to.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel(err)
|
t.err = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.ctx["to"] = toVal
|
t.ctx["to"] = toVal
|
||||||
inputVal, err := t.toBuf(t.vm, input)
|
inputVal, err := t.toBuf(t.vm, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel(err)
|
t.err = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.ctx["input"] = inputVal
|
t.ctx["input"] = inputVal
|
||||||
t.ctx["gas"] = t.vm.ToValue(t.gasLimit)
|
|
||||||
gasPriceBig, err := t.toBig(t.vm, env.TxContext.GasPrice.String())
|
|
||||||
if err != nil {
|
|
||||||
cancel(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.ctx["gasPrice"] = gasPriceBig
|
|
||||||
valueBig, err := t.toBig(t.vm, value.String())
|
valueBig, err := t.toBig(t.vm, value.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel(err)
|
t.err = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.ctx["value"] = valueBig
|
t.ctx["value"] = valueBig
|
||||||
t.ctx["block"] = t.vm.ToValue(env.Context.BlockNumber.Uint64())
|
|
||||||
// Update list of precompiles based on current block
|
|
||||||
rules := env.ChainConfig().Rules(env.Context.BlockNumber)
|
|
||||||
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureState implements the Tracer interface to trace a single step of VM execution.
|
// OnOpcode implements the Tracer interface to trace a single step of VM execution.
|
||||||
func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
func (t *jsTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||||
if !t.traceStep {
|
if !t.traceStep {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -292,10 +316,10 @@ func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope
|
||||||
}
|
}
|
||||||
|
|
||||||
log := t.log
|
log := t.log
|
||||||
log.op.op = op
|
log.op.op = vm.OpCode(op)
|
||||||
log.memory.memory = scope.Memory
|
log.memory.memory = scope.MemoryData()
|
||||||
log.stack.stack = scope.Stack
|
log.stack.stack = scope.StackData()
|
||||||
log.contract.contract = scope.Contract
|
log.contract.scope = scope
|
||||||
log.pc = pc
|
log.pc = pc
|
||||||
log.gas = gas
|
log.gas = gas
|
||||||
log.cost = cost
|
log.cost = cost
|
||||||
|
|
@ -307,20 +331,23 @@ func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureFault implements the Tracer interface to trace an execution fault
|
// OnFault implements the Tracer interface to trace an execution fault
|
||||||
func (t *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
func (t *jsTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
|
||||||
if t.err != nil {
|
if t.err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Other log fields have been already set as part of the last CaptureState.
|
// Other log fields have been already set as part of the last OnOpcode.
|
||||||
t.log.err = err
|
t.log.err = err
|
||||||
if _, err := t.fault(t.obj, t.logValue, t.dbValue); err != nil {
|
if _, err := t.fault(t.obj, t.logValue, t.dbValue); err != nil {
|
||||||
t.onError("fault", err)
|
t.onError("fault", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
// onEnd is called after the call finishes to finalize the tracing.
|
||||||
func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
func (t *jsTracer) onEnd(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||||
|
if t.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.ctx["error"] = t.vm.ToValue(err.Error())
|
t.ctx["error"] = t.vm.ToValue(err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -332,16 +359,20 @@ func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||||
t.ctx["output"] = outputVal
|
t.ctx["output"] = outputVal
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
// OnEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||||
func (t *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
func (t *jsTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
if !t.traceFrame {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if t.err != nil {
|
if t.err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if depth == 0 {
|
||||||
|
t.onStart(from, to, vm.OpCode(typ) == vm.CREATE, input, gas, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !t.traceFrame {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
t.frame.typ = typ.String()
|
t.frame.typ = vm.OpCode(typ).String()
|
||||||
t.frame.from = from
|
t.frame.from = from
|
||||||
t.frame.to = to
|
t.frame.to = to
|
||||||
t.frame.input = common.CopyBytes(input)
|
t.frame.input = common.CopyBytes(input)
|
||||||
|
|
@ -356,9 +387,16 @@ func (t *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Ad
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
// OnExit is called when EVM exits a scope, even if the scope didn't
|
||||||
// execute any code.
|
// execute any code.
|
||||||
func (t *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
func (t *jsTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||||
|
if t.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if depth == 0 {
|
||||||
|
t.onEnd(output, gasUsed, err, reverted)
|
||||||
|
return
|
||||||
|
}
|
||||||
if !t.traceFrame {
|
if !t.traceFrame {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -374,6 +412,9 @@ func (t *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
||||||
|
|
||||||
// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
|
// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
|
||||||
func (t *jsTracer) GetResult() (json.RawMessage, error) {
|
func (t *jsTracer) GetResult() (json.RawMessage, error) {
|
||||||
|
if t.err != nil {
|
||||||
|
return nil, t.err
|
||||||
|
}
|
||||||
ctx := t.vm.ToValue(t.ctx)
|
ctx := t.vm.ToValue(t.ctx)
|
||||||
res, err := t.result(t.obj, ctx, t.dbValue)
|
res, err := t.result(t.obj, ctx, t.dbValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -383,7 +424,7 @@ func (t *jsTracer) GetResult() (json.RawMessage, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return json.RawMessage(encoded), t.err
|
return encoded, t.err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop terminates execution of the tracer at the first opportune moment.
|
// Stop terminates execution of the tracer at the first opportune moment.
|
||||||
|
|
@ -396,9 +437,6 @@ func (t *jsTracer) Stop(err error) {
|
||||||
// execution.
|
// execution.
|
||||||
func (t *jsTracer) onError(context string, err error) {
|
func (t *jsTracer) onError(context string, err error) {
|
||||||
t.err = wrapError(context, err)
|
t.err = wrapError(context, err)
|
||||||
// `env` is set on CaptureStart which comes before any JS execution.
|
|
||||||
// So it should be non-nil.
|
|
||||||
t.env.Cancel()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrapError(context string, err error) error {
|
func wrapError(context string, err error) error {
|
||||||
|
|
@ -577,7 +615,7 @@ func (o *opObj) setupObject() *goja.Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
type memoryObj struct {
|
type memoryObj struct {
|
||||||
memory *vm.Memory
|
memory []byte
|
||||||
vm *goja.Runtime
|
vm *goja.Runtime
|
||||||
toBig toBigFn
|
toBig toBigFn
|
||||||
toBuf toBufFn
|
toBuf toBufFn
|
||||||
|
|
@ -605,7 +643,7 @@ func (mo *memoryObj) slice(begin, end int64) ([]byte, error) {
|
||||||
if end < begin || begin < 0 {
|
if end < begin || begin < 0 {
|
||||||
return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end)
|
return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end)
|
||||||
}
|
}
|
||||||
slice, err := tracers.GetMemoryCopyPadded(mo.memory, begin, end-begin)
|
slice, err := internal.GetMemoryCopyPadded(mo.memory, begin, end-begin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -628,14 +666,14 @@ func (mo *memoryObj) GetUint(addr int64) goja.Value {
|
||||||
|
|
||||||
// getUint returns the 32 bytes at the specified address interpreted as a uint.
|
// getUint returns the 32 bytes at the specified address interpreted as a uint.
|
||||||
func (mo *memoryObj) getUint(addr int64) (*big.Int, error) {
|
func (mo *memoryObj) getUint(addr int64) (*big.Int, error) {
|
||||||
if mo.memory.Len() < int(addr)+32 || addr < 0 {
|
if len(mo.memory) < int(addr)+32 || addr < 0 {
|
||||||
return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), addr, 32)
|
return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", len(mo.memory), addr, 32)
|
||||||
}
|
}
|
||||||
return new(big.Int).SetBytes(mo.memory.GetPtr(addr, 32)), nil
|
return new(big.Int).SetBytes(internal.MemoryPtr(mo.memory, addr, 32)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mo *memoryObj) Length() int {
|
func (mo *memoryObj) Length() int {
|
||||||
return mo.memory.Len()
|
return len(mo.memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryObj) setupObject() *goja.Object {
|
func (m *memoryObj) setupObject() *goja.Object {
|
||||||
|
|
@ -647,7 +685,7 @@ func (m *memoryObj) setupObject() *goja.Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
type stackObj struct {
|
type stackObj struct {
|
||||||
stack *vm.Stack
|
stack []uint256.Int
|
||||||
vm *goja.Runtime
|
vm *goja.Runtime
|
||||||
toBig toBigFn
|
toBig toBigFn
|
||||||
}
|
}
|
||||||
|
|
@ -668,14 +706,14 @@ func (s *stackObj) Peek(idx int) goja.Value {
|
||||||
|
|
||||||
// peek returns the nth-from-the-top element of the stack.
|
// peek returns the nth-from-the-top element of the stack.
|
||||||
func (s *stackObj) peek(idx int) (*big.Int, error) {
|
func (s *stackObj) peek(idx int) (*big.Int, error) {
|
||||||
if len(s.stack.Data()) <= idx || idx < 0 {
|
if len(s.stack) <= idx || idx < 0 {
|
||||||
return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack.Data()), idx)
|
return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack), idx)
|
||||||
}
|
}
|
||||||
return s.stack.Back(idx).ToBig(), nil
|
return internal.StackBack(s.stack, idx).ToBig(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stackObj) Length() int {
|
func (s *stackObj) Length() int {
|
||||||
return len(s.stack.Data())
|
return len(s.stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stackObj) setupObject() *goja.Object {
|
func (s *stackObj) setupObject() *goja.Object {
|
||||||
|
|
@ -686,7 +724,7 @@ func (s *stackObj) setupObject() *goja.Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
type dbObj struct {
|
type dbObj struct {
|
||||||
db vm.StateDB
|
db tracing.StateDB
|
||||||
vm *goja.Runtime
|
vm *goja.Runtime
|
||||||
toBig toBigFn
|
toBig toBigFn
|
||||||
toBuf toBufFn
|
toBuf toBufFn
|
||||||
|
|
@ -778,14 +816,14 @@ func (do *dbObj) setupObject() *goja.Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
type contractObj struct {
|
type contractObj struct {
|
||||||
contract *vm.Contract
|
scope tracing.OpContext
|
||||||
vm *goja.Runtime
|
vm *goja.Runtime
|
||||||
toBig toBigFn
|
toBig toBigFn
|
||||||
toBuf toBufFn
|
toBuf toBufFn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co *contractObj) GetCaller() goja.Value {
|
func (co *contractObj) GetCaller() goja.Value {
|
||||||
caller := co.contract.Caller().Bytes()
|
caller := co.scope.Caller().Bytes()
|
||||||
res, err := co.toBuf(co.vm, caller)
|
res, err := co.toBuf(co.vm, caller)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
co.vm.Interrupt(err)
|
co.vm.Interrupt(err)
|
||||||
|
|
@ -795,7 +833,7 @@ func (co *contractObj) GetCaller() goja.Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co *contractObj) GetAddress() goja.Value {
|
func (co *contractObj) GetAddress() goja.Value {
|
||||||
addr := co.contract.Address().Bytes()
|
addr := co.scope.Address().Bytes()
|
||||||
res, err := co.toBuf(co.vm, addr)
|
res, err := co.toBuf(co.vm, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
co.vm.Interrupt(err)
|
co.vm.Interrupt(err)
|
||||||
|
|
@ -805,7 +843,7 @@ func (co *contractObj) GetAddress() goja.Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co *contractObj) GetValue() goja.Value {
|
func (co *contractObj) GetValue() goja.Value {
|
||||||
value := co.contract.Value()
|
value := co.scope.CallValue()
|
||||||
res, err := co.toBig(co.vm, value.String())
|
res, err := co.toBig(co.vm, value.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
co.vm.Interrupt(err)
|
co.vm.Interrupt(err)
|
||||||
|
|
@ -815,7 +853,7 @@ func (co *contractObj) GetValue() goja.Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (co *contractObj) GetInput() goja.Value {
|
func (co *contractObj) GetInput() goja.Value {
|
||||||
input := common.CopyBytes(co.contract.Input)
|
input := common.CopyBytes(co.scope.CallInput())
|
||||||
res, err := co.toBuf(co.vm, input)
|
res, err := co.toBuf(co.vm, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
co.vm.Interrupt(err)
|
co.vm.Interrupt(err)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
|
@ -60,9 +61,9 @@ func testCtx() *vmContext {
|
||||||
return &vmContext{ctx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txContext: vm.TxContext{GasPrice: big.NewInt(100000)}}
|
return &vmContext{ctx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txContext: vm.TxContext{GasPrice: big.NewInt(100000)}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) {
|
func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) {
|
||||||
var (
|
var (
|
||||||
env = vm.NewEVM(vmctx.ctx, vmctx.txContext, &dummyStatedb{}, nil, chaincfg, vm.Config{Tracer: tracer})
|
env = vm.NewEVM(vmctx.ctx, vmctx.txContext, &dummyStatedb{}, nil, chaincfg, vm.Config{Tracer: tracer.Hooks})
|
||||||
gasLimit uint64 = 31000
|
gasLimit uint64 = 31000
|
||||||
startGas uint64 = 10000
|
startGas uint64 = 10000
|
||||||
value = big.NewInt(0)
|
value = big.NewInt(0)
|
||||||
|
|
@ -73,12 +74,12 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon
|
||||||
contract.Code = contractCode
|
contract.Code = contractCode
|
||||||
}
|
}
|
||||||
|
|
||||||
tracer.CaptureTxStart(gasLimit)
|
tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller())
|
||||||
tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value)
|
tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value)
|
||||||
ret, err := env.Interpreter().Run(contract, []byte{}, false)
|
ret, err := env.Interpreter().Run(contract, []byte{}, false)
|
||||||
tracer.CaptureEnd(ret, startGas-contract.Gas, err)
|
tracer.OnExit(0, ret, startGas-contract.Gas, err, true)
|
||||||
// Rest gas assumes no refund
|
// Rest gas assumes no refund
|
||||||
tracer.CaptureTxEnd(contract.Gas)
|
tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -180,15 +181,16 @@ func TestHaltBetweenSteps(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Tracer: tracer})
|
|
||||||
scope := &vm.ScopeContext{
|
scope := &vm.ScopeContext{
|
||||||
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
|
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
|
||||||
}
|
}
|
||||||
tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0))
|
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Tracer: tracer.Hooks})
|
||||||
tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil)
|
tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{})
|
||||||
|
tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 0, big.NewInt(0))
|
||||||
|
tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil)
|
||||||
timeout := errors.New("stahp")
|
timeout := errors.New("stahp")
|
||||||
tracer.Stop(timeout)
|
tracer.Stop(timeout)
|
||||||
tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil)
|
tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil)
|
||||||
|
|
||||||
if _, err := tracer.GetResult(); !strings.Contains(err.Error(), timeout.Error()) {
|
if _, err := tracer.GetResult(); !strings.Contains(err.Error(), timeout.Error()) {
|
||||||
t.Errorf("Expected timeout error, got %v", err)
|
t.Errorf("Expected timeout error, got %v", err)
|
||||||
|
|
@ -204,9 +206,10 @@ func TestNoStepExec(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100000)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Tracer: tracer})
|
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Tracer: tracer.Hooks})
|
||||||
tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0))
|
tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{})
|
||||||
tracer.CaptureEnd(nil, 0, nil)
|
tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 1000, big.NewInt(0))
|
||||||
|
tracer.OnExit(0, nil, 0, nil, false)
|
||||||
ret, err := tracer.GetResult()
|
ret, err := tracer.GetResult()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -293,8 +296,8 @@ func TestEnterExit(t *testing.T) {
|
||||||
scope := &vm.ScopeContext{
|
scope := &vm.ScopeContext{
|
||||||
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
|
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
|
||||||
}
|
}
|
||||||
tracer.CaptureEnter(vm.CALL, scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
|
tracer.OnEnter(1, byte(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
|
||||||
tracer.CaptureExit([]byte{}, 400, nil)
|
tracer.OnExit(1, []byte{}, 400, nil, false)
|
||||||
|
|
||||||
have, err := tracer.GetResult()
|
have, err := tracer.GetResult()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
31
eth/tracers/live.go
Normal file
31
eth/tracers/live.go
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
package tracers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ctorFunc func(config json.RawMessage) (*tracing.Hooks, error)
|
||||||
|
|
||||||
|
// LiveDirectory is the collection of tracers which can be used
|
||||||
|
// during normal block import operations.
|
||||||
|
var LiveDirectory = liveDirectory{elems: make(map[string]ctorFunc)}
|
||||||
|
|
||||||
|
type liveDirectory struct {
|
||||||
|
elems map[string]ctorFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register registers a tracer constructor by name.
|
||||||
|
func (d *liveDirectory) Register(name string, f ctorFunc) {
|
||||||
|
d.elems[name] = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// New instantiates a tracer by name.
|
||||||
|
func (d *liveDirectory) New(name string, config json.RawMessage) (*tracing.Hooks, error) {
|
||||||
|
if f, ok := d.elems[name]; ok {
|
||||||
|
return f(config)
|
||||||
|
}
|
||||||
|
return nil, errors.New("not found")
|
||||||
|
}
|
||||||
96
eth/tracers/live/noop.go
Normal file
96
eth/tracers/live/noop.go
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
package live
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
tracers.LiveDirectory.Register("noop", newNoopTracer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// noop is a no-op live tracer. It's there to
|
||||||
|
// catch changes in the tracing interface, as well as
|
||||||
|
// for testing live tracing performance. Can be removed
|
||||||
|
// as soon as we have a real live tracer.
|
||||||
|
type noop struct{}
|
||||||
|
|
||||||
|
func newNoopTracer(_ json.RawMessage) (*tracing.Hooks, error) {
|
||||||
|
t := &noop{}
|
||||||
|
return &tracing.Hooks{
|
||||||
|
OnTxStart: t.OnTxStart,
|
||||||
|
OnTxEnd: t.OnTxEnd,
|
||||||
|
OnEnter: t.OnEnter,
|
||||||
|
OnExit: t.OnExit,
|
||||||
|
OnOpcode: t.OnOpcode,
|
||||||
|
OnFault: t.OnFault,
|
||||||
|
OnGasChange: t.OnGasChange,
|
||||||
|
OnBlockchainInit: t.OnBlockchainInit,
|
||||||
|
OnBlockStart: t.OnBlockStart,
|
||||||
|
OnBlockEnd: t.OnBlockEnd,
|
||||||
|
OnSkippedBlock: t.OnSkippedBlock,
|
||||||
|
OnGenesisBlock: t.OnGenesisBlock,
|
||||||
|
OnBalanceChange: t.OnBalanceChange,
|
||||||
|
OnNonceChange: t.OnNonceChange,
|
||||||
|
OnCodeChange: t.OnCodeChange,
|
||||||
|
OnStorageChange: t.OnStorageChange,
|
||||||
|
OnLog: t.OnLog,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnFault(pc uint64, op byte, gas, cost uint64, _ tracing.OpContext, depth int, err error) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnTxEnd(receipt *types.Receipt, err error) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnBlockStart(ev tracing.BlockEvent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnBlockEnd(err error) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnSkippedBlock(ev tracing.BlockEvent) {}
|
||||||
|
|
||||||
|
func (t *noop) OnBlockchainInit(chainConfig *params.ChainConfig) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnBalanceChange(a common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnNonceChange(a common.Address, prev, new uint64) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnStorageChange(a common.Address, k, prev, new common.Hash) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnLog(l *types.Log) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {
|
||||||
|
}
|
||||||
|
|
@ -17,9 +17,8 @@
|
||||||
package logger
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
)
|
)
|
||||||
|
|
@ -132,17 +131,20 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AccessListTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (a *AccessListTracer) Hooks() *tracing.Hooks {
|
||||||
|
return &tracing.Hooks{
|
||||||
|
OnOpcode: a.OnOpcode,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
|
// OnOpcode captures all opcodes that touch storage or addresses and adds them to the accesslist.
|
||||||
func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
func (a *AccessListTracer) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||||
stack := scope.Stack
|
stackData := scope.StackData()
|
||||||
stackData := stack.Data()
|
|
||||||
stackLen := len(stackData)
|
stackLen := len(stackData)
|
||||||
|
op := vm.OpCode(opcode)
|
||||||
if (op == vm.SLOAD || op == vm.SSTORE) && stackLen >= 1 {
|
if (op == vm.SLOAD || op == vm.SSTORE) && stackLen >= 1 {
|
||||||
slot := common.Hash(stackData[stackLen-1].Bytes32())
|
slot := common.Hash(stackData[stackLen-1].Bytes32())
|
||||||
a.list.addSlot(scope.Contract.Address(), slot)
|
a.list.addSlot(scope.Address(), slot)
|
||||||
}
|
}
|
||||||
if (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT) && stackLen >= 1 {
|
if (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT) && stackLen >= 1 {
|
||||||
addr := common.Address(stackData[stackLen-1].Bytes20())
|
addr := common.Address(stackData[stackLen-1].Bytes20())
|
||||||
|
|
@ -158,20 +160,6 @@ func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*AccessListTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {}
|
|
||||||
|
|
||||||
func (*AccessListTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*AccessListTracer) CaptureExit(output []byte, gasUsed uint64, err error) {}
|
|
||||||
|
|
||||||
func (*AccessListTracer) CaptureTxStart(gasLimit uint64) {}
|
|
||||||
|
|
||||||
func (*AccessListTracer) CaptureTxEnd(restGas uint64) {}
|
|
||||||
|
|
||||||
// AccessList returns the current accesslist maintained by the tracer.
|
// AccessList returns the current accesslist maintained by the tracer.
|
||||||
func (a *AccessListTracer) AccessList() types.AccessList {
|
func (a *AccessListTracer) AccessList() types.AccessList {
|
||||||
return a.list.accessList()
|
return a.list.accessList()
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
|
@ -108,14 +109,13 @@ func (s *StructLog) ErrorString() string {
|
||||||
// contract their storage.
|
// contract their storage.
|
||||||
type StructLogger struct {
|
type StructLogger struct {
|
||||||
cfg Config
|
cfg Config
|
||||||
env *vm.EVM
|
env *tracing.VMContext
|
||||||
|
|
||||||
storage map[common.Address]Storage
|
storage map[common.Address]Storage
|
||||||
logs []StructLog
|
logs []StructLog
|
||||||
output []byte
|
output []byte
|
||||||
err error
|
err error
|
||||||
gasLimit uint64
|
usedGas uint64
|
||||||
usedGas uint64
|
|
||||||
|
|
||||||
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
||||||
reason error // Textual reason for the interruption
|
reason error // Textual reason for the interruption
|
||||||
|
|
@ -132,6 +132,15 @@ func NewStructLogger(cfg *Config) *StructLogger {
|
||||||
return logger
|
return logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *StructLogger) Hooks() *tracing.Hooks {
|
||||||
|
return &tracing.Hooks{
|
||||||
|
OnTxStart: l.OnTxStart,
|
||||||
|
OnTxEnd: l.OnTxEnd,
|
||||||
|
OnExit: l.OnExit,
|
||||||
|
OnOpcode: l.OnOpcode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Reset clears the data held by the logger.
|
// Reset clears the data held by the logger.
|
||||||
func (l *StructLogger) Reset() {
|
func (l *StructLogger) Reset() {
|
||||||
l.storage = make(map[common.Address]Storage)
|
l.storage = make(map[common.Address]Storage)
|
||||||
|
|
@ -140,15 +149,10 @@ func (l *StructLogger) Reset() {
|
||||||
l.err = nil
|
l.err = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
// OnOpcode logs a new structured log message and pushes it out to the environment
|
||||||
func (l *StructLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
|
||||||
l.env = env
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaptureState logs a new structured log message and pushes it out to the environment
|
|
||||||
//
|
//
|
||||||
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
|
// OnOpcode also tracks SLOAD/SSTORE ops to track storage change.
|
||||||
func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||||
// If tracing was interrupted, set the error and stop
|
// If tracing was interrupted, set the error and stop
|
||||||
if l.interrupt.Load() {
|
if l.interrupt.Load() {
|
||||||
return
|
return
|
||||||
|
|
@ -158,49 +162,47 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
memory := scope.Memory
|
op := vm.OpCode(opcode)
|
||||||
stack := scope.Stack
|
memory := scope.MemoryData()
|
||||||
contract := scope.Contract
|
stack := scope.StackData()
|
||||||
// Copy a snapshot of the current memory state to a new buffer
|
// Copy a snapshot of the current memory state to a new buffer
|
||||||
var mem []byte
|
var mem []byte
|
||||||
if l.cfg.EnableMemory {
|
if l.cfg.EnableMemory {
|
||||||
mem = make([]byte, len(memory.Data()))
|
mem = make([]byte, len(memory))
|
||||||
copy(mem, memory.Data())
|
copy(mem, memory)
|
||||||
}
|
}
|
||||||
// Copy a snapshot of the current stack state to a new buffer
|
// Copy a snapshot of the current stack state to a new buffer
|
||||||
var stck []uint256.Int
|
var stck []uint256.Int
|
||||||
if !l.cfg.DisableStack {
|
if !l.cfg.DisableStack {
|
||||||
stck = make([]uint256.Int, len(stack.Data()))
|
stck = make([]uint256.Int, len(stack))
|
||||||
for i, item := range stack.Data() {
|
copy(stck, stack)
|
||||||
stck[i] = item
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
stackData := stack.Data()
|
contractAddr := scope.Address()
|
||||||
stackLen := len(stackData)
|
stackLen := len(stack)
|
||||||
// Copy a snapshot of the current storage to a new container
|
// Copy a snapshot of the current storage to a new container
|
||||||
var storage Storage
|
var storage Storage
|
||||||
if !l.cfg.DisableStorage && (op == vm.SLOAD || op == vm.SSTORE) {
|
if !l.cfg.DisableStorage && (op == vm.SLOAD || op == vm.SSTORE) {
|
||||||
// initialise new changed values storage container for this contract
|
// initialise new changed values storage container for this contract
|
||||||
// if not present.
|
// if not present.
|
||||||
if l.storage[contract.Address()] == nil {
|
if l.storage[contractAddr] == nil {
|
||||||
l.storage[contract.Address()] = make(Storage)
|
l.storage[contractAddr] = make(Storage)
|
||||||
}
|
}
|
||||||
// capture SLOAD opcodes and record the read entry in the local storage
|
// capture SLOAD opcodes and record the read entry in the local storage
|
||||||
if op == vm.SLOAD && stackLen >= 1 {
|
if op == vm.SLOAD && stackLen >= 1 {
|
||||||
var (
|
var (
|
||||||
address = common.Hash(stackData[stackLen-1].Bytes32())
|
address = common.Hash(stack[stackLen-1].Bytes32())
|
||||||
value = l.env.StateDB.GetState(contract.Address(), address)
|
value = l.env.StateDB.GetState(contractAddr, address)
|
||||||
)
|
)
|
||||||
l.storage[contract.Address()][address] = value
|
l.storage[contractAddr][address] = value
|
||||||
storage = l.storage[contract.Address()].Copy()
|
storage = l.storage[contractAddr].Copy()
|
||||||
} else if op == vm.SSTORE && stackLen >= 2 {
|
} else if op == vm.SSTORE && stackLen >= 2 {
|
||||||
// capture SSTORE opcodes and record the written entry in the local storage.
|
// capture SSTORE opcodes and record the written entry in the local storage.
|
||||||
var (
|
var (
|
||||||
value = common.Hash(stackData[stackLen-2].Bytes32())
|
value = common.Hash(stack[stackLen-2].Bytes32())
|
||||||
address = common.Hash(stackData[stackLen-1].Bytes32())
|
address = common.Hash(stack[stackLen-1].Bytes32())
|
||||||
)
|
)
|
||||||
l.storage[contract.Address()][address] = value
|
l.storage[contractAddr][address] = value
|
||||||
storage = l.storage[contract.Address()].Copy()
|
storage = l.storage[contractAddr].Copy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var rdata []byte
|
var rdata []byte
|
||||||
|
|
@ -209,17 +211,15 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s
|
||||||
copy(rdata, rData)
|
copy(rdata, rData)
|
||||||
}
|
}
|
||||||
// create a new snapshot of the EVM.
|
// create a new snapshot of the EVM.
|
||||||
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err}
|
log := StructLog{pc, op, gas, cost, mem, len(memory), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err}
|
||||||
l.logs = append(l.logs, log)
|
l.logs = append(l.logs, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureFault implements the EVMLogger interface to trace an execution fault
|
// OnExit is called a call frame finishes processing.
|
||||||
// while running an opcode.
|
func (l *StructLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||||
func (l *StructLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
if depth != 0 {
|
||||||
}
|
return
|
||||||
|
}
|
||||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
|
||||||
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
|
||||||
l.output = output
|
l.output = output
|
||||||
l.err = err
|
l.err = err
|
||||||
if l.cfg.Debug {
|
if l.cfg.Debug {
|
||||||
|
|
@ -230,12 +230,6 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *StructLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *StructLogger) GetResult() (json.RawMessage, error) {
|
func (l *StructLogger) GetResult() (json.RawMessage, error) {
|
||||||
// Tracing aborted
|
// Tracing aborted
|
||||||
if l.reason != nil {
|
if l.reason != nil {
|
||||||
|
|
@ -262,12 +256,19 @@ func (l *StructLogger) Stop(err error) {
|
||||||
l.interrupt.Store(true)
|
l.interrupt.Store(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *StructLogger) CaptureTxStart(gasLimit uint64) {
|
func (l *StructLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
l.gasLimit = gasLimit
|
l.env = env
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *StructLogger) CaptureTxEnd(restGas uint64) {
|
func (l *StructLogger) OnTxEnd(receipt *types.Receipt, err error) {
|
||||||
l.usedGas = l.gasLimit - restGas
|
if err != nil {
|
||||||
|
// Don't override vm error
|
||||||
|
if l.err == nil {
|
||||||
|
l.err = err
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l.usedGas = receipt.GasUsed
|
||||||
}
|
}
|
||||||
|
|
||||||
// StructLogs returns the captured log entries.
|
// StructLogs returns the captured log entries.
|
||||||
|
|
@ -329,7 +330,7 @@ func WriteLogs(writer io.Writer, logs []*types.Log) {
|
||||||
type mdLogger struct {
|
type mdLogger struct {
|
||||||
out io.Writer
|
out io.Writer
|
||||||
cfg *Config
|
cfg *Config
|
||||||
env *vm.EVM
|
env *tracing.VMContext
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMarkdownLogger creates a logger which outputs information in a format adapted
|
// NewMarkdownLogger creates a logger which outputs information in a format adapted
|
||||||
|
|
@ -342,8 +343,25 @@ func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger {
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *mdLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (t *mdLogger) Hooks() *tracing.Hooks {
|
||||||
|
return &tracing.Hooks{
|
||||||
|
OnTxStart: t.OnTxStart,
|
||||||
|
OnEnter: t.OnEnter,
|
||||||
|
OnExit: t.OnExit,
|
||||||
|
OnOpcode: t.OnOpcode,
|
||||||
|
OnFault: t.OnFault,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *mdLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
t.env = env
|
t.env = env
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *mdLogger) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
|
if depth != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
create := vm.OpCode(typ) == vm.CREATE
|
||||||
if !create {
|
if !create {
|
||||||
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n",
|
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n",
|
||||||
from.String(), to.String(),
|
from.String(), to.String(),
|
||||||
|
|
@ -360,15 +378,22 @@ func (t *mdLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Addr
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
|
func (t *mdLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||||
func (t *mdLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
if depth == 0 {
|
||||||
stack := scope.Stack
|
fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
|
||||||
|
output, gasUsed, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnOpcode also tracks SLOAD/SSTORE ops to track storage change.
|
||||||
|
func (t *mdLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||||
|
stack := scope.StackData()
|
||||||
fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost)
|
fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost)
|
||||||
|
|
||||||
if !t.cfg.DisableStack {
|
if !t.cfg.DisableStack {
|
||||||
// format stack
|
// format stack
|
||||||
var a []string
|
var a []string
|
||||||
for _, elem := range stack.Data() {
|
for _, elem := range stack {
|
||||||
a = append(a, elem.Hex())
|
a = append(a, elem.Hex())
|
||||||
}
|
}
|
||||||
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
|
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
|
||||||
|
|
@ -381,24 +406,10 @@ func (t *mdLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
func (t *mdLogger) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
|
||||||
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
|
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
|
||||||
fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
|
|
||||||
output, gasUsed, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *mdLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
|
|
||||||
|
|
||||||
func (*mdLogger) CaptureTxStart(gasLimit uint64) {}
|
|
||||||
|
|
||||||
func (*mdLogger) CaptureTxEnd(restGas uint64) {}
|
|
||||||
|
|
||||||
// ExecutionResult groups all structured logs emitted by the EVM
|
// ExecutionResult groups all structured logs emitted by the EVM
|
||||||
// while replaying a transaction in debug mode as well as transaction
|
// while replaying a transaction in debug mode as well as transaction
|
||||||
// execution status, the amount of gas used and the return value
|
// execution status, the amount of gas used and the return value
|
||||||
|
|
|
||||||
|
|
@ -19,58 +19,59 @@ package logger
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type JSONLogger struct {
|
type jsonLogger struct {
|
||||||
encoder *json.Encoder
|
encoder *json.Encoder
|
||||||
cfg *Config
|
cfg *Config
|
||||||
env *vm.EVM
|
env *tracing.VMContext
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
|
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
|
||||||
// into the provided stream.
|
// into the provided stream.
|
||||||
func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger {
|
func NewJSONLogger(cfg *Config, writer io.Writer) *tracing.Hooks {
|
||||||
l := &JSONLogger{encoder: json.NewEncoder(writer), cfg: cfg}
|
l := &jsonLogger{encoder: json.NewEncoder(writer), cfg: cfg}
|
||||||
if l.cfg == nil {
|
if l.cfg == nil {
|
||||||
l.cfg = &Config{}
|
l.cfg = &Config{}
|
||||||
}
|
}
|
||||||
return l
|
return &tracing.Hooks{
|
||||||
|
OnTxStart: l.OnTxStart,
|
||||||
|
OnExit: l.OnExit,
|
||||||
|
OnOpcode: l.OnOpcode,
|
||||||
|
OnFault: l.OnFault,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *JSONLogger) CaptureStart(env *vm.EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (l *jsonLogger) OnFault(pc uint64, op byte, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) {
|
||||||
l.env = env
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *JSONLogger) CaptureFault(pc uint64, op vm.OpCode, gas uint64, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
|
||||||
// TODO: Add rData to this interface as well
|
// TODO: Add rData to this interface as well
|
||||||
l.CaptureState(pc, op, gas, cost, scope, nil, depth, err)
|
l.OnOpcode(pc, op, gas, cost, scope, nil, depth, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureState outputs state information on the logger.
|
func (l *jsonLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||||
func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
memory := scope.MemoryData()
|
||||||
memory := scope.Memory
|
stack := scope.StackData()
|
||||||
stack := scope.Stack
|
|
||||||
|
|
||||||
log := StructLog{
|
log := StructLog{
|
||||||
Pc: pc,
|
Pc: pc,
|
||||||
Op: op,
|
Op: vm.OpCode(op),
|
||||||
Gas: gas,
|
Gas: gas,
|
||||||
GasCost: cost,
|
GasCost: cost,
|
||||||
MemorySize: memory.Len(),
|
MemorySize: len(memory),
|
||||||
Depth: depth,
|
Depth: depth,
|
||||||
RefundCounter: l.env.StateDB.GetRefund(),
|
RefundCounter: l.env.StateDB.GetRefund(),
|
||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
if l.cfg.EnableMemory {
|
if l.cfg.EnableMemory {
|
||||||
log.Memory = memory.Data()
|
log.Memory = memory
|
||||||
}
|
}
|
||||||
if !l.cfg.DisableStack {
|
if !l.cfg.DisableStack {
|
||||||
log.Stack = stack.Data()
|
log.Stack = stack
|
||||||
}
|
}
|
||||||
if l.cfg.EnableReturnData {
|
if l.cfg.EnableReturnData {
|
||||||
log.ReturnData = rData
|
log.ReturnData = rData
|
||||||
|
|
@ -78,8 +79,10 @@ func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco
|
||||||
l.encoder.Encode(log)
|
l.encoder.Encode(log)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureEnd is triggered at end of execution.
|
func (l *jsonLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||||
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
if depth > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
type endLog struct {
|
type endLog struct {
|
||||||
Output string `json:"output"`
|
Output string `json:"output"`
|
||||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||||
|
|
@ -92,11 +95,6 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||||
l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg})
|
l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *JSONLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
func (l *jsonLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
|
l.env = env
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *JSONLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
|
|
||||||
|
|
||||||
func (l *JSONLogger) CaptureTxStart(gasLimit uint64) {}
|
|
||||||
|
|
||||||
func (l *JSONLogger) CaptureTxEnd(restGas uint64) {}
|
|
||||||
|
|
|
||||||
|
|
@ -55,12 +55,12 @@ func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) {}
|
||||||
func TestStoreCapture(t *testing.T) {
|
func TestStoreCapture(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
logger = NewStructLogger(nil)
|
logger = NewStructLogger(nil)
|
||||||
env = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Tracer: logger})
|
env = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Tracer: logger.Hooks()})
|
||||||
contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 100000)
|
contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 100000)
|
||||||
)
|
)
|
||||||
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)}
|
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)}
|
||||||
var index common.Hash
|
var index common.Hash
|
||||||
logger.CaptureStart(env, common.Address{}, contract.Address(), false, nil, 0, nil)
|
logger.OnTxStart(env.GetVMContext(), nil, common.Address{})
|
||||||
_, err := env.Interpreter().Run(contract, []byte{}, false)
|
_, err := env.Interpreter().Run(contract, []byte{}, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
||||||
)
|
)
|
||||||
|
|
@ -46,20 +48,26 @@ func init() {
|
||||||
// 0xc281d19e-0: 1
|
// 0xc281d19e-0: 1
|
||||||
// }
|
// }
|
||||||
type fourByteTracer struct {
|
type fourByteTracer struct {
|
||||||
noopTracer
|
|
||||||
ids map[string]int // ids aggregates the 4byte ids found
|
ids map[string]int // ids aggregates the 4byte ids found
|
||||||
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
||||||
reason error // Textual reason for the interruption
|
reason error // Textual reason for the interruption
|
||||||
activePrecompiles []common.Address // Updated on CaptureStart based on given rules
|
activePrecompiles []common.Address // Updated on tx start based on given rules
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFourByteTracer returns a native go tracer which collects
|
// newFourByteTracer returns a native go tracer which collects
|
||||||
// 4 byte-identifiers of a tx, and implements vm.EVMLogger.
|
// 4 byte-identifiers of a tx, and implements vm.EVMLogger.
|
||||||
func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
|
func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (*tracers.Tracer, error) {
|
||||||
t := &fourByteTracer{
|
t := &fourByteTracer{
|
||||||
ids: make(map[string]int),
|
ids: make(map[string]int),
|
||||||
}
|
}
|
||||||
return t, nil
|
return &tracers.Tracer{
|
||||||
|
Hooks: &tracing.Hooks{
|
||||||
|
OnTxStart: t.OnTxStart,
|
||||||
|
OnEnter: t.OnEnter,
|
||||||
|
},
|
||||||
|
GetResult: t.GetResult,
|
||||||
|
Stop: t.Stop,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isPrecompiled returns whether the addr is a precompile. Logic borrowed from newJsTracer in eth/tracers/js/tracer.go
|
// isPrecompiled returns whether the addr is a precompile. Logic borrowed from newJsTracer in eth/tracers/js/tracer.go
|
||||||
|
|
@ -78,20 +86,14 @@ func (t *fourByteTracer) store(id []byte, size int) {
|
||||||
t.ids[key] += 1
|
t.ids[key] += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
func (t *fourByteTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
|
||||||
// Update list of precompiles based on current block
|
// Update list of precompiles based on current block
|
||||||
rules := env.ChainConfig().Rules(env.Context.BlockNumber)
|
rules := env.ChainConfig.Rules(env.BlockNumber)
|
||||||
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
||||||
|
|
||||||
// Save the outer calldata also
|
|
||||||
if len(input) >= 4 {
|
|
||||||
t.store(input[0:4], len(input)-4)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
// OnEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||||
func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
func (t *fourByteTracer) OnEnter(depth int, opcode byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
// Skip if tracing was interrupted
|
// Skip if tracing was interrupted
|
||||||
if t.interrupt.Load() {
|
if t.interrupt.Load() {
|
||||||
return
|
return
|
||||||
|
|
@ -99,6 +101,7 @@ func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to comm
|
||||||
if len(input) < 4 {
|
if len(input) < 4 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
op := vm.OpCode(opcode)
|
||||||
// primarily we want to avoid CREATE/CREATE2/SELFDESTRUCT
|
// primarily we want to avoid CREATE/CREATE2/SELFDESTRUCT
|
||||||
if op != vm.DELEGATECALL && op != vm.STATICCALL &&
|
if op != vm.DELEGATECALL && op != vm.STATICCALL &&
|
||||||
op != vm.CALL && op != vm.CALLCODE {
|
op != vm.CALL && op != vm.CALLCODE {
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,10 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/accounts/abi"
|
"github.com/XinFinOrg/XDPoSChain/accounts/abi"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go
|
//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go
|
||||||
|
|
@ -59,7 +60,8 @@ type callFrame struct {
|
||||||
Logs []callLog `json:"logs,omitempty" rlp:"optional"`
|
Logs []callLog `json:"logs,omitempty" rlp:"optional"`
|
||||||
// Placed at end on purpose. The RLP will be decoded to 0 instead of
|
// Placed at end on purpose. The RLP will be decoded to 0 instead of
|
||||||
// nil if there are non-empty elements after in the struct.
|
// nil if there are non-empty elements after in the struct.
|
||||||
Value *big.Int `json:"value,omitempty" rlp:"optional"`
|
Value *big.Int `json:"value,omitempty" rlp:"optional"`
|
||||||
|
revertedSnapshot bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f callFrame) TypeString() string {
|
func (f callFrame) TypeString() string {
|
||||||
|
|
@ -67,16 +69,17 @@ func (f callFrame) TypeString() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f callFrame) failed() bool {
|
func (f callFrame) failed() bool {
|
||||||
return len(f.Error) > 0
|
return len(f.Error) > 0 && f.revertedSnapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *callFrame) processOutput(output []byte, err error) {
|
func (f *callFrame) processOutput(output []byte, err error, reverted bool) {
|
||||||
output = common.CopyBytes(output)
|
output = common.CopyBytes(output)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
f.Output = output
|
f.Output = output
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
f.Error = err.Error()
|
f.Error = err.Error()
|
||||||
|
f.revertedSnapshot = reverted
|
||||||
if f.Type == vm.CREATE || f.Type == vm.CREATE2 {
|
if f.Type == vm.CREATE || f.Type == vm.CREATE2 {
|
||||||
f.To = nil
|
f.To = nil
|
||||||
}
|
}
|
||||||
|
|
@ -102,10 +105,10 @@ type callFrameMarshaling struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type callTracer struct {
|
type callTracer struct {
|
||||||
noopTracer
|
|
||||||
callstack []callFrame
|
callstack []callFrame
|
||||||
config callTracerConfig
|
config callTracerConfig
|
||||||
gasLimit uint64
|
gasLimit uint64
|
||||||
|
depth int
|
||||||
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
||||||
reason error // Textual reason for the interruption
|
reason error // Textual reason for the interruption
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +120,25 @@ type callTracerConfig struct {
|
||||||
|
|
||||||
// newCallTracer returns a native go tracer which tracks
|
// newCallTracer returns a native go tracer which tracks
|
||||||
// call frames of a tx, and implements vm.EVMLogger.
|
// call frames of a tx, and implements vm.EVMLogger.
|
||||||
func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
|
func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
|
||||||
|
t, err := newCallTracerObject(ctx, cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &tracers.Tracer{
|
||||||
|
Hooks: &tracing.Hooks{
|
||||||
|
OnTxStart: t.OnTxStart,
|
||||||
|
OnTxEnd: t.OnTxEnd,
|
||||||
|
OnEnter: t.OnEnter,
|
||||||
|
OnExit: t.OnExit,
|
||||||
|
OnLog: t.OnLog,
|
||||||
|
},
|
||||||
|
GetResult: t.GetResult,
|
||||||
|
Stop: t.Stop,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCallTracerObject(ctx *tracers.Context, cfg json.RawMessage) (*callTracer, error) {
|
||||||
var config callTracerConfig
|
var config callTracerConfig
|
||||||
if cfg != nil {
|
if cfg != nil {
|
||||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
|
|
@ -126,84 +147,13 @@ func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, e
|
||||||
}
|
}
|
||||||
// First callframe contains tx context info
|
// First callframe contains tx context info
|
||||||
// and is populated on start and end.
|
// and is populated on start and end.
|
||||||
return &callTracer{callstack: make([]callFrame, 1), config: config}, nil
|
return &callTracer{callstack: make([]callFrame, 0, 1), config: config}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
// OnEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||||
func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (t *callTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
toCopy := to
|
t.depth = depth
|
||||||
t.callstack[0] = callFrame{
|
if t.config.OnlyTopCall && depth > 0 {
|
||||||
Type: vm.CALL,
|
|
||||||
From: from,
|
|
||||||
To: &toCopy,
|
|
||||||
Input: common.CopyBytes(input),
|
|
||||||
Gas: t.gasLimit,
|
|
||||||
Value: value,
|
|
||||||
}
|
|
||||||
if create {
|
|
||||||
t.callstack[0].Type = vm.CREATE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
|
||||||
func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
|
||||||
t.callstack[0].processOutput(output, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
|
||||||
func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
|
||||||
// skip if the previous op caused an error
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Only logs need to be captured via opcode processing
|
|
||||||
if !t.config.WithLog {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Avoid processing nested calls when only caring about top call
|
|
||||||
if t.config.OnlyTopCall && depth > 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Skip if tracing was interrupted
|
|
||||||
if t.interrupt.Load() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch op {
|
|
||||||
case vm.LOG0, vm.LOG1, vm.LOG2, vm.LOG3, vm.LOG4:
|
|
||||||
size := int(op - vm.LOG0)
|
|
||||||
|
|
||||||
stack := scope.Stack
|
|
||||||
stackData := stack.Data()
|
|
||||||
|
|
||||||
// Don't modify the stack
|
|
||||||
mStart := stackData[len(stackData)-1]
|
|
||||||
mSize := stackData[len(stackData)-2]
|
|
||||||
topics := make([]common.Hash, size)
|
|
||||||
for i := 0; i < size; i++ {
|
|
||||||
topic := stackData[len(stackData)-2-(i+1)]
|
|
||||||
topics[i] = common.Hash(topic.Bytes32())
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := tracers.GetMemoryCopyPadded(scope.Memory, int64(mStart.Uint64()), int64(mSize.Uint64()))
|
|
||||||
if err != nil {
|
|
||||||
// mSize was unrealistically large
|
|
||||||
log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "callTracer", "offset", mStart, "size", mSize)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log := callLog{
|
|
||||||
Address: scope.Contract.Address(),
|
|
||||||
Topics: topics,
|
|
||||||
Data: hexutil.Bytes(data),
|
|
||||||
Position: hexutil.Uint(len(t.callstack[len(t.callstack)-1].Calls)),
|
|
||||||
}
|
|
||||||
t.callstack[len(t.callstack)-1].Logs = append(t.callstack[len(t.callstack)-1].Logs, log)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
|
||||||
func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
|
||||||
if t.config.OnlyTopCall {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Skip if tracing was interrupted
|
// Skip if tracing was interrupted
|
||||||
|
|
@ -213,48 +163,92 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.
|
||||||
|
|
||||||
toCopy := to
|
toCopy := to
|
||||||
call := callFrame{
|
call := callFrame{
|
||||||
Type: typ,
|
Type: vm.OpCode(typ),
|
||||||
From: from,
|
From: from,
|
||||||
To: &toCopy,
|
To: &toCopy,
|
||||||
Input: common.CopyBytes(input),
|
Input: common.CopyBytes(input),
|
||||||
Gas: gas,
|
Gas: gas,
|
||||||
Value: value,
|
Value: value,
|
||||||
}
|
}
|
||||||
|
if depth == 0 {
|
||||||
|
call.Gas = t.gasLimit
|
||||||
|
}
|
||||||
t.callstack = append(t.callstack, call)
|
t.callstack = append(t.callstack, call)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
// OnExit is called when EVM exits a scope, even if the scope didn't
|
||||||
// execute any code.
|
// execute any code.
|
||||||
func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
func (t *callTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||||
|
if depth == 0 {
|
||||||
|
t.captureEnd(output, gasUsed, err, reverted)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.depth = depth - 1
|
||||||
if t.config.OnlyTopCall {
|
if t.config.OnlyTopCall {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
size := len(t.callstack)
|
size := len(t.callstack)
|
||||||
if size <= 1 {
|
if size <= 1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// pop call
|
// Pop call.
|
||||||
call := t.callstack[size-1]
|
call := t.callstack[size-1]
|
||||||
t.callstack = t.callstack[:size-1]
|
t.callstack = t.callstack[:size-1]
|
||||||
size -= 1
|
size -= 1
|
||||||
|
|
||||||
call.GasUsed = gasUsed
|
call.GasUsed = gasUsed
|
||||||
call.processOutput(output, err)
|
call.processOutput(output, err, reverted)
|
||||||
|
// Nest call into parent.
|
||||||
t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
|
t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *callTracer) CaptureTxStart(gasLimit uint64) {
|
func (t *callTracer) captureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||||
t.gasLimit = gasLimit
|
if len(t.callstack) != 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.callstack[0].processOutput(output, err, reverted)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *callTracer) CaptureTxEnd(restGas uint64) {
|
func (t *callTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
t.callstack[0].GasUsed = t.gasLimit - restGas
|
t.gasLimit = tx.Gas()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *callTracer) OnTxEnd(receipt *types.Receipt, err error) {
|
||||||
|
// Error happened during tx validation.
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.callstack[0].GasUsed = receipt.GasUsed
|
||||||
if t.config.WithLog {
|
if t.config.WithLog {
|
||||||
// Logs are not emitted when the call fails
|
// Logs are not emitted when the call fails
|
||||||
clearFailedLogs(&t.callstack[0], false)
|
clearFailedLogs(&t.callstack[0], false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *callTracer) OnLog(log *types.Log) {
|
||||||
|
// Only logs need to be captured via opcode processing
|
||||||
|
if !t.config.WithLog {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Avoid processing nested calls when only caring about top call
|
||||||
|
if t.config.OnlyTopCall && t.depth > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Skip if tracing was interrupted
|
||||||
|
if t.interrupt.Load() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l := callLog{
|
||||||
|
Address: log.Address,
|
||||||
|
Topics: log.Topics,
|
||||||
|
Data: log.Data,
|
||||||
|
Position: hexutil.Uint(len(t.callstack[len(t.callstack)-1].Calls)),
|
||||||
|
}
|
||||||
|
t.callstack[len(t.callstack)-1].Logs = append(t.callstack[len(t.callstack)-1].Logs, l)
|
||||||
|
}
|
||||||
|
|
||||||
// GetResult returns the json-encoded nested list of call traces, and any
|
// GetResult returns the json-encoded nested list of call traces, and any
|
||||||
// error arising from the encoding or forceful termination (via `Stop`).
|
// error arising from the encoding or forceful termination (via `Stop`).
|
||||||
func (t *callTracer) GetResult() (json.RawMessage, error) {
|
func (t *callTracer) GetResult() (json.RawMessage, error) {
|
||||||
|
|
@ -266,7 +260,7 @@ func (t *callTracer) GetResult() (json.RawMessage, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return json.RawMessage(res), t.reason
|
return res, t.reason
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop terminates execution of the tracer at the first opportune moment.
|
// Stop terminates execution of the tracer at the first opportune moment.
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
||||||
)
|
)
|
||||||
|
|
@ -112,7 +114,7 @@ type flatCallTracer struct {
|
||||||
config flatCallTracerConfig
|
config flatCallTracerConfig
|
||||||
ctx *tracers.Context // Holds tracer context data
|
ctx *tracers.Context // Holds tracer context data
|
||||||
reason error // Textual reason for the interruption
|
reason error // Textual reason for the interruption
|
||||||
activePrecompiles []common.Address // Updated on CaptureStart based on given rules
|
activePrecompiles []common.Address // Updated on tx start based on given rules
|
||||||
}
|
}
|
||||||
|
|
||||||
type flatCallTracerConfig struct {
|
type flatCallTracerConfig struct {
|
||||||
|
|
@ -121,7 +123,7 @@ type flatCallTracerConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFlatCallTracer returns a new flatCallTracer.
|
// newFlatCallTracer returns a new flatCallTracer.
|
||||||
func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
|
func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
|
||||||
var config flatCallTracerConfig
|
var config flatCallTracerConfig
|
||||||
if cfg != nil {
|
if cfg != nil {
|
||||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
|
|
@ -131,45 +133,31 @@ func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Trace
|
||||||
|
|
||||||
// Create inner call tracer with default configuration, don't forward
|
// Create inner call tracer with default configuration, don't forward
|
||||||
// the OnlyTopCall or WithLog to inner for now
|
// the OnlyTopCall or WithLog to inner for now
|
||||||
tracer, err := tracers.DefaultDirectory.New("callTracer", ctx, nil)
|
t, err := newCallTracerObject(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
t, ok := tracer.(*callTracer)
|
|
||||||
if !ok {
|
ft := &flatCallTracer{tracer: t, ctx: ctx, config: config}
|
||||||
return nil, errors.New("internal error: embedded tracer has wrong type")
|
return &tracers.Tracer{
|
||||||
|
Hooks: &tracing.Hooks{
|
||||||
|
OnTxStart: ft.OnTxStart,
|
||||||
|
OnTxEnd: ft.OnTxEnd,
|
||||||
|
OnEnter: ft.OnEnter,
|
||||||
|
OnExit: ft.OnExit,
|
||||||
|
},
|
||||||
|
Stop: ft.Stop,
|
||||||
|
GetResult: ft.GetResult,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||||
|
func (t *flatCallTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
|
t.tracer.OnEnter(depth, typ, from, to, input, gas, value)
|
||||||
|
|
||||||
|
if depth == 0 {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return &flatCallTracer{tracer: t, ctx: ctx, config: config}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
|
||||||
func (t *flatCallTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
|
||||||
t.tracer.CaptureStart(env, from, to, create, input, gas, value)
|
|
||||||
// Update list of precompiles based on current block
|
|
||||||
rules := env.ChainConfig().Rules(env.Context.BlockNumber)
|
|
||||||
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
|
||||||
func (t *flatCallTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
|
||||||
t.tracer.CaptureEnd(output, gasUsed, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
|
||||||
func (t *flatCallTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
|
||||||
t.tracer.CaptureState(pc, op, gas, cost, scope, rData, depth, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaptureFault implements the EVMLogger interface to trace an execution fault.
|
|
||||||
func (t *flatCallTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
|
||||||
t.tracer.CaptureFault(pc, op, gas, cost, scope, depth, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
|
||||||
func (t *flatCallTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
|
||||||
t.tracer.CaptureEnter(typ, from, to, input, gas, value)
|
|
||||||
|
|
||||||
// Child calls must have a value, even if it's zero.
|
// Child calls must have a value, even if it's zero.
|
||||||
// Practically speaking, only STATICCALL has nil value. Set it to zero.
|
// Practically speaking, only STATICCALL has nil value. Set it to zero.
|
||||||
if t.tracer.callstack[len(t.tracer.callstack)-1].Value == nil && value == nil {
|
if t.tracer.callstack[len(t.tracer.callstack)-1].Value == nil && value == nil {
|
||||||
|
|
@ -177,11 +165,14 @@ func (t *flatCallTracer) CaptureEnter(typ vm.OpCode, from common.Address, to com
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
// OnExit is called when EVM exits a scope, even if the scope didn't
|
||||||
// execute any code.
|
// execute any code.
|
||||||
func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
func (t *flatCallTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||||
t.tracer.CaptureExit(output, gasUsed, err)
|
t.tracer.OnExit(depth, output, gasUsed, err, reverted)
|
||||||
|
|
||||||
|
if depth == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
// Parity traces don't include CALL/STATICCALLs to precompiles.
|
// Parity traces don't include CALL/STATICCALLs to precompiles.
|
||||||
// By default we remove them from the callstack.
|
// By default we remove them from the callstack.
|
||||||
if t.config.IncludePrecompiles {
|
if t.config.IncludePrecompiles {
|
||||||
|
|
@ -201,12 +192,15 @@ func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *flatCallTracer) CaptureTxStart(gasLimit uint64) {
|
func (t *flatCallTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
t.tracer.CaptureTxStart(gasLimit)
|
t.tracer.OnTxStart(env, tx, from)
|
||||||
|
// Update list of precompiles based on current block
|
||||||
|
rules := env.ChainConfig.Rules(env.BlockNumber)
|
||||||
|
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *flatCallTracer) CaptureTxEnd(restGas uint64) {
|
func (t *flatCallTracer) OnTxEnd(receipt *types.Receipt, err error) {
|
||||||
t.tracer.CaptureTxEnd(restGas)
|
t.tracer.OnTxEnd(receipt, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetResult returns an empty json object.
|
// GetResult returns an empty json object.
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
||||||
)
|
)
|
||||||
|
|
@ -17,7 +19,6 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type contractTracer struct {
|
type contractTracer struct {
|
||||||
env *vm.EVM
|
|
||||||
Addrs map[string]string
|
Addrs map[string]string
|
||||||
config contractTracerConfig
|
config contractTracerConfig
|
||||||
interrupt uint32 // Atomic flag to signal execution interruption
|
interrupt uint32 // Atomic flag to signal execution interruption
|
||||||
|
|
@ -29,7 +30,7 @@ type contractTracerConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContractTracer returns a native go tracer which tracks the contracr was created
|
// NewContractTracer returns a native go tracer which tracks the contracr was created
|
||||||
func NewContractTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
|
func NewContractTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
|
||||||
var config contractTracerConfig
|
var config contractTracerConfig
|
||||||
if cfg != nil {
|
if cfg != nil {
|
||||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
|
|
@ -46,28 +47,28 @@ func NewContractTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Trace
|
||||||
t.reason = fmt.Errorf("opcode %s not defined", t.config.OpCode)
|
t.reason = fmt.Errorf("opcode %s not defined", t.config.OpCode)
|
||||||
return nil, t.reason
|
return nil, t.reason
|
||||||
}
|
}
|
||||||
return t, nil
|
return &tracers.Tracer{
|
||||||
|
Hooks: &tracing.Hooks{
|
||||||
|
OnTxStart: t.OnTxStart,
|
||||||
|
OnTxEnd: t.OnTxEnd,
|
||||||
|
OnEnter: t.OnEnter,
|
||||||
|
OnExit: t.OnExit,
|
||||||
|
OnOpcode: t.OnOpcode,
|
||||||
|
OnFault: t.OnFault,
|
||||||
|
},
|
||||||
|
GetResult: t.GetResult,
|
||||||
|
Stop: t.Stop,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *contractTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (*contractTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
t.env = env
|
|
||||||
// When not searching for opcodes, record the contract address.
|
|
||||||
if create && t.config.OpCode == "" {
|
|
||||||
t.Addrs[addrToHex(to)] = ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *contractTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
func (*contractTracer) OnTxEnd(receipt *types.Receipt, err error) {}
|
||||||
}
|
|
||||||
|
|
||||||
func (*contractTracer) CaptureTxStart(gasLimit uint64) {}
|
func (t *contractTracer) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||||
|
|
||||||
func (*contractTracer) CaptureTxEnd(restGas uint64) {}
|
|
||||||
|
|
||||||
func (t *contractTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
|
||||||
// Skip if tracing was interrupted
|
// Skip if tracing was interrupted
|
||||||
if atomic.LoadUint32(&t.interrupt) > 0 {
|
if atomic.LoadUint32(&t.interrupt) > 0 {
|
||||||
t.env.Cancel()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// If the OpCode is empty , exit early.
|
// If the OpCode is empty , exit early.
|
||||||
|
|
@ -75,19 +76,24 @@ func (t *contractTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
targetOp := vm.StringToOp(t.config.OpCode)
|
targetOp := vm.StringToOp(t.config.OpCode)
|
||||||
|
op := vm.OpCode(opcode)
|
||||||
if op == targetOp {
|
if op == targetOp {
|
||||||
addr := scope.Contract.Address()
|
addr := scope.Address()
|
||||||
t.Addrs[addrToHex(addr)] = ""
|
t.Addrs[addrToHex(addr)] = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *contractTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
|
func (t *contractTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *contractTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
func (t *contractTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
|
create := vm.OpCode(typ) == vm.CREATE
|
||||||
|
if create && t.config.OpCode == "" {
|
||||||
|
t.Addrs[addrToHex(to)] = ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *contractTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
func (t *contractTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *contractTracer) GetResult() (json.RawMessage, error) {
|
func (t *contractTracer) GetResult() (json.RawMessage, error) {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -33,18 +34,18 @@ func init() {
|
||||||
// runs multiple tracers in one go.
|
// runs multiple tracers in one go.
|
||||||
type muxTracer struct {
|
type muxTracer struct {
|
||||||
names []string
|
names []string
|
||||||
tracers []tracers.Tracer
|
tracers []*tracers.Tracer
|
||||||
}
|
}
|
||||||
|
|
||||||
// newMuxTracer returns a new mux tracer.
|
// newMuxTracer returns a new mux tracer.
|
||||||
func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
|
func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
|
||||||
var config map[string]json.RawMessage
|
var config map[string]json.RawMessage
|
||||||
if cfg != nil {
|
if cfg != nil {
|
||||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
objects := make([]tracers.Tracer, 0, len(config))
|
objects := make([]*tracers.Tracer, 0, len(config))
|
||||||
names := make([]string, 0, len(config))
|
names := make([]string, 0, len(config))
|
||||||
for k, v := range config {
|
for k, v := range config {
|
||||||
t, err := tracers.DefaultDirectory.New(k, ctx, v)
|
t, err := tracers.DefaultDirectory.New(k, ctx, v)
|
||||||
|
|
@ -55,61 +56,120 @@ func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, er
|
||||||
names = append(names, k)
|
names = append(names, k)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &muxTracer{names: names, tracers: objects}, nil
|
t := &muxTracer{names: names, tracers: objects}
|
||||||
|
return &tracers.Tracer{
|
||||||
|
Hooks: &tracing.Hooks{
|
||||||
|
OnTxStart: t.OnTxStart,
|
||||||
|
OnTxEnd: t.OnTxEnd,
|
||||||
|
OnEnter: t.OnEnter,
|
||||||
|
OnExit: t.OnExit,
|
||||||
|
OnOpcode: t.OnOpcode,
|
||||||
|
OnFault: t.OnFault,
|
||||||
|
OnGasChange: t.OnGasChange,
|
||||||
|
OnBalanceChange: t.OnBalanceChange,
|
||||||
|
OnNonceChange: t.OnNonceChange,
|
||||||
|
OnCodeChange: t.OnCodeChange,
|
||||||
|
OnStorageChange: t.OnStorageChange,
|
||||||
|
OnLog: t.OnLog,
|
||||||
|
},
|
||||||
|
GetResult: t.GetResult,
|
||||||
|
Stop: t.Stop,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
func (t *muxTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||||
func (t *muxTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
|
||||||
for _, t := range t.tracers {
|
for _, t := range t.tracers {
|
||||||
t.CaptureStart(env, from, to, create, input, gas, value)
|
if t.OnOpcode != nil {
|
||||||
|
t.OnOpcode(pc, op, gas, cost, scope, rData, depth, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
func (t *muxTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
|
||||||
func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
|
||||||
for _, t := range t.tracers {
|
for _, t := range t.tracers {
|
||||||
t.CaptureEnd(output, gasUsed, err)
|
if t.OnFault != nil {
|
||||||
|
t.OnFault(pc, op, gas, cost, scope, depth, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
func (t *muxTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {
|
||||||
func (t *muxTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
|
||||||
for _, t := range t.tracers {
|
for _, t := range t.tracers {
|
||||||
t.CaptureState(pc, op, gas, cost, scope, rData, depth, err)
|
if t.OnGasChange != nil {
|
||||||
|
t.OnGasChange(old, new, reason)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureFault implements the EVMLogger interface to trace an execution fault.
|
func (t *muxTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
func (t *muxTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
|
|
||||||
for _, t := range t.tracers {
|
for _, t := range t.tracers {
|
||||||
t.CaptureFault(pc, op, gas, cost, scope, depth, err)
|
if t.OnEnter != nil {
|
||||||
|
t.OnEnter(depth, typ, from, to, input, gas, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
func (t *muxTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||||
func (t *muxTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
|
||||||
for _, t := range t.tracers {
|
for _, t := range t.tracers {
|
||||||
t.CaptureEnter(typ, from, to, input, gas, value)
|
if t.OnExit != nil {
|
||||||
|
t.OnExit(depth, output, gasUsed, err, reverted)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
func (t *muxTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
// execute any code.
|
|
||||||
func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
|
||||||
for _, t := range t.tracers {
|
for _, t := range t.tracers {
|
||||||
t.CaptureExit(output, gasUsed, err)
|
if t.OnTxStart != nil {
|
||||||
|
t.OnTxStart(env, tx, from)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *muxTracer) CaptureTxStart(gasLimit uint64) {
|
func (t *muxTracer) OnTxEnd(receipt *types.Receipt, err error) {
|
||||||
for _, t := range t.tracers {
|
for _, t := range t.tracers {
|
||||||
t.CaptureTxStart(gasLimit)
|
if t.OnTxEnd != nil {
|
||||||
|
t.OnTxEnd(receipt, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *muxTracer) CaptureTxEnd(restGas uint64) {
|
func (t *muxTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) {
|
||||||
for _, t := range t.tracers {
|
for _, t := range t.tracers {
|
||||||
t.CaptureTxEnd(restGas)
|
if t.OnBalanceChange != nil {
|
||||||
|
t.OnBalanceChange(a, prev, new, reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *muxTracer) OnNonceChange(a common.Address, prev, new uint64) {
|
||||||
|
for _, t := range t.tracers {
|
||||||
|
if t.OnNonceChange != nil {
|
||||||
|
t.OnNonceChange(a, prev, new)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *muxTracer) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
|
||||||
|
for _, t := range t.tracers {
|
||||||
|
if t.OnCodeChange != nil {
|
||||||
|
t.OnCodeChange(a, prevCodeHash, prev, codeHash, code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *muxTracer) OnStorageChange(a common.Address, k, prev, new common.Hash) {
|
||||||
|
for _, t := range t.tracers {
|
||||||
|
if t.OnStorageChange != nil {
|
||||||
|
t.OnStorageChange(a, k, prev, new)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *muxTracer) OnLog(log *types.Log) {
|
||||||
|
for _, t := range t.tracers {
|
||||||
|
if t.OnLog != nil {
|
||||||
|
t.OnLog(log)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,8 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -31,32 +32,59 @@ func init() {
|
||||||
|
|
||||||
type noopTracer struct{}
|
type noopTracer struct{}
|
||||||
|
|
||||||
func newNoopTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
|
// newNoopTracer returns a new noop tracer.
|
||||||
return &noopTracer{}, nil
|
func newNoopTracer(ctx *tracers.Context, _ json.RawMessage) (*tracers.Tracer, error) {
|
||||||
|
t := &noopTracer{}
|
||||||
|
return &tracers.Tracer{
|
||||||
|
Hooks: &tracing.Hooks{
|
||||||
|
OnTxStart: t.OnTxStart,
|
||||||
|
OnTxEnd: t.OnTxEnd,
|
||||||
|
OnEnter: t.OnEnter,
|
||||||
|
OnExit: t.OnExit,
|
||||||
|
OnOpcode: t.OnOpcode,
|
||||||
|
OnFault: t.OnFault,
|
||||||
|
OnGasChange: t.OnGasChange,
|
||||||
|
OnBalanceChange: t.OnBalanceChange,
|
||||||
|
OnNonceChange: t.OnNonceChange,
|
||||||
|
OnCodeChange: t.OnCodeChange,
|
||||||
|
OnStorageChange: t.OnStorageChange,
|
||||||
|
OnLog: t.OnLog,
|
||||||
|
},
|
||||||
|
GetResult: t.GetResult,
|
||||||
|
Stop: t.Stop,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *noopTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (t *noopTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
func (t *noopTracer) OnFault(pc uint64, op byte, gas, cost uint64, _ tracing.OpContext, depth int, err error) {
|
||||||
func (t *noopTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *noopTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
func (t *noopTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {}
|
||||||
|
|
||||||
|
func (t *noopTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *noopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
|
func (t *noopTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *noopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
func (*noopTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *noopTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
func (*noopTracer) OnTxEnd(receipt *types.Receipt, err error) {}
|
||||||
|
|
||||||
|
func (*noopTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*noopTracer) CaptureTxStart(gasLimit uint64) {}
|
func (*noopTracer) OnNonceChange(a common.Address, prev, new uint64) {}
|
||||||
|
|
||||||
func (*noopTracer) CaptureTxEnd(restGas uint64) {}
|
func (*noopTracer) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*noopTracer) OnStorageChange(a common.Address, k, prev, new common.Hash) {}
|
||||||
|
|
||||||
|
func (*noopTracer) OnLog(log *types.Log) {}
|
||||||
|
|
||||||
// GetResult returns an empty json object.
|
// GetResult returns an empty json object.
|
||||||
func (t *noopTracer) GetResult() (json.RawMessage, error) {
|
func (t *noopTracer) GetResult() (json.RawMessage, error) {
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,12 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/eth/tracers/internal"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -36,13 +39,14 @@ func init() {
|
||||||
tracers.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false)
|
tracers.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
type state = map[common.Address]*account
|
type stateMap = map[common.Address]*account
|
||||||
|
|
||||||
type account struct {
|
type account struct {
|
||||||
Balance *big.Int `json:"balance,omitempty"`
|
Balance *big.Int `json:"balance,omitempty"`
|
||||||
Code []byte `json:"code,omitempty"`
|
Code []byte `json:"code,omitempty"`
|
||||||
Nonce uint64 `json:"nonce,omitempty"`
|
Nonce uint64 `json:"nonce,omitempty"`
|
||||||
Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
|
Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
|
||||||
|
empty bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *account) exists() bool {
|
func (a *account) exists() bool {
|
||||||
|
|
@ -55,13 +59,10 @@ type accountMarshaling struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type prestateTracer struct {
|
type prestateTracer struct {
|
||||||
noopTracer
|
env *tracing.VMContext
|
||||||
env *vm.EVM
|
pre stateMap
|
||||||
pre state
|
post stateMap
|
||||||
post state
|
|
||||||
create bool
|
|
||||||
to common.Address
|
to common.Address
|
||||||
gasLimit uint64 // Amount of gas bought for the whole tx
|
|
||||||
config prestateTracerConfig
|
config prestateTracerConfig
|
||||||
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
||||||
reason error // Textual reason for the interruption
|
reason error // Textual reason for the interruption
|
||||||
|
|
@ -73,70 +74,33 @@ type prestateTracerConfig struct {
|
||||||
DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
|
DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
|
func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
|
||||||
var config prestateTracerConfig
|
var config prestateTracerConfig
|
||||||
if cfg != nil {
|
if cfg != nil {
|
||||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &prestateTracer{
|
t := &prestateTracer{
|
||||||
pre: state{},
|
pre: stateMap{},
|
||||||
post: state{},
|
post: stateMap{},
|
||||||
config: config,
|
config: config,
|
||||||
created: make(map[common.Address]bool),
|
created: make(map[common.Address]bool),
|
||||||
deleted: make(map[common.Address]bool),
|
deleted: make(map[common.Address]bool),
|
||||||
|
}
|
||||||
|
return &tracers.Tracer{
|
||||||
|
Hooks: &tracing.Hooks{
|
||||||
|
OnTxStart: t.OnTxStart,
|
||||||
|
OnTxEnd: t.OnTxEnd,
|
||||||
|
OnOpcode: t.OnOpcode,
|
||||||
|
},
|
||||||
|
GetResult: t.GetResult,
|
||||||
|
Stop: t.Stop,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
// OnOpcode implements the EVMLogger interface to trace a single step of VM execution.
|
||||||
func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
func (t *prestateTracer) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||||
t.env = env
|
|
||||||
t.create = create
|
|
||||||
t.to = to
|
|
||||||
|
|
||||||
t.lookupAccount(from)
|
|
||||||
t.lookupAccount(to)
|
|
||||||
t.lookupAccount(env.Context.Coinbase)
|
|
||||||
|
|
||||||
// The recipient balance includes the value transferred.
|
|
||||||
toBal := new(big.Int).Sub(t.pre[to].Balance, value)
|
|
||||||
t.pre[to].Balance = toBal
|
|
||||||
if env.ChainConfig().Rules(env.Context.BlockNumber).IsEIP158 && create {
|
|
||||||
t.pre[to].Nonce--
|
|
||||||
}
|
|
||||||
|
|
||||||
// The sender balance is after reducing: value and gasLimit.
|
|
||||||
// We need to re-add them to get the pre-tx balance.
|
|
||||||
fromBal := new(big.Int).Set(t.pre[from].Balance)
|
|
||||||
gasPrice := env.TxContext.GasPrice
|
|
||||||
consumedGas := new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(t.gasLimit))
|
|
||||||
fromBal.Add(fromBal, new(big.Int).Add(value, consumedGas))
|
|
||||||
t.pre[from].Balance = fromBal
|
|
||||||
t.pre[from].Nonce--
|
|
||||||
|
|
||||||
if create && t.config.DiffMode {
|
|
||||||
t.created[to] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
|
||||||
func (t *prestateTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
|
||||||
if t.config.DiffMode {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.create {
|
|
||||||
// Keep existing account prior to contract creation at that address
|
|
||||||
if s := t.pre[t.to]; s != nil && !s.exists() {
|
|
||||||
// Exclude newly created contract.
|
|
||||||
delete(t.pre, t.to)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
|
|
||||||
func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -144,10 +108,10 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
|
||||||
if t.interrupt.Load() {
|
if t.interrupt.Load() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
stack := scope.Stack
|
op := vm.OpCode(opcode)
|
||||||
stackData := stack.Data()
|
stackData := scope.StackData()
|
||||||
stackLen := len(stackData)
|
stackLen := len(stackData)
|
||||||
caller := scope.Contract.Address()
|
caller := scope.Address()
|
||||||
switch {
|
switch {
|
||||||
case stackLen >= 1 && (op == vm.SLOAD || op == vm.SSTORE):
|
case stackLen >= 1 && (op == vm.SLOAD || op == vm.SSTORE):
|
||||||
slot := common.Hash(stackData[stackLen-1].Bytes32())
|
slot := common.Hash(stackData[stackLen-1].Bytes32())
|
||||||
|
|
@ -169,7 +133,7 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
|
||||||
case stackLen >= 4 && op == vm.CREATE2:
|
case stackLen >= 4 && op == vm.CREATE2:
|
||||||
offset := stackData[stackLen-2]
|
offset := stackData[stackLen-2]
|
||||||
size := stackData[stackLen-3]
|
size := stackData[stackLen-3]
|
||||||
init, err := tracers.GetMemoryCopyPadded(scope.Memory, int64(offset.Uint64()), int64(size.Uint64()))
|
init, err := internal.GetMemoryCopyPadded(scope.MemoryData(), int64(offset.Uint64()), int64(size.Uint64()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "prestateTracer", "offset", offset, "size", size)
|
log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "prestateTracer", "offset", offset, "size", size)
|
||||||
return
|
return
|
||||||
|
|
@ -182,15 +146,62 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *prestateTracer) CaptureTxStart(gasLimit uint64) {
|
func (t *prestateTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
t.gasLimit = gasLimit
|
t.env = env
|
||||||
}
|
if tx.To() == nil {
|
||||||
|
t.to = crypto.CreateAddress(from, env.StateDB.GetNonce(from))
|
||||||
func (t *prestateTracer) CaptureTxEnd(restGas uint64) {
|
t.created[t.to] = true
|
||||||
if !t.config.DiffMode {
|
} else {
|
||||||
return
|
t.to = *tx.To()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.lookupAccount(from)
|
||||||
|
t.lookupAccount(t.to)
|
||||||
|
t.lookupAccount(env.Coinbase)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *prestateTracer) OnTxEnd(receipt *types.Receipt, err error) {
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if t.config.DiffMode {
|
||||||
|
t.processDiffState()
|
||||||
|
}
|
||||||
|
// the new created contracts' prestate were empty, so delete them
|
||||||
|
for a := range t.created {
|
||||||
|
// the created contract maybe exists in statedb before the creating tx
|
||||||
|
if s := t.pre[a]; s != nil && s.empty {
|
||||||
|
delete(t.pre, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult returns the json-encoded nested list of call traces, and any
|
||||||
|
// error arising from the encoding or forceful termination (via `Stop`).
|
||||||
|
func (t *prestateTracer) GetResult() (json.RawMessage, error) {
|
||||||
|
var res []byte
|
||||||
|
var err error
|
||||||
|
if t.config.DiffMode {
|
||||||
|
res, err = json.Marshal(struct {
|
||||||
|
Post stateMap `json:"post"`
|
||||||
|
Pre stateMap `json:"pre"`
|
||||||
|
}{t.post, t.pre})
|
||||||
|
} else {
|
||||||
|
res, err = json.Marshal(t.pre)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return json.RawMessage(res), t.reason
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop terminates execution of the tracer at the first opportune moment.
|
||||||
|
func (t *prestateTracer) Stop(err error) {
|
||||||
|
t.reason = err
|
||||||
|
t.interrupt.Store(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *prestateTracer) processDiffState() {
|
||||||
for addr, state := range t.pre {
|
for addr, state := range t.pre {
|
||||||
// The deleted account's state is pruned from `post` but kept in `pre`
|
// The deleted account's state is pruned from `post` but kept in `pre`
|
||||||
if _, ok := t.deleted[addr]; ok {
|
if _, ok := t.deleted[addr]; ok {
|
||||||
|
|
@ -240,38 +251,6 @@ func (t *prestateTracer) CaptureTxEnd(restGas uint64) {
|
||||||
delete(t.pre, addr)
|
delete(t.pre, addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// the new created contracts' prestate were empty, so delete them
|
|
||||||
for a := range t.created {
|
|
||||||
// the created contract maybe exists in statedb before the creating tx
|
|
||||||
if s := t.pre[a]; s != nil && !s.exists() {
|
|
||||||
delete(t.pre, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult returns the json-encoded nested list of call traces, and any
|
|
||||||
// error arising from the encoding or forceful termination (via `Stop`).
|
|
||||||
func (t *prestateTracer) GetResult() (json.RawMessage, error) {
|
|
||||||
var res []byte
|
|
||||||
var err error
|
|
||||||
if t.config.DiffMode {
|
|
||||||
res, err = json.Marshal(struct {
|
|
||||||
Post state `json:"post"`
|
|
||||||
Pre state `json:"pre"`
|
|
||||||
}{t.post, t.pre})
|
|
||||||
} else {
|
|
||||||
res, err = json.Marshal(t.pre)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return json.RawMessage(res), t.reason
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop terminates execution of the tracer at the first opportune moment.
|
|
||||||
func (t *prestateTracer) Stop(err error) {
|
|
||||||
t.reason = err
|
|
||||||
t.interrupt.Store(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookupAccount fetches details of an account and adds it to the prestate
|
// lookupAccount fetches details of an account and adds it to the prestate
|
||||||
|
|
@ -281,12 +260,16 @@ func (t *prestateTracer) lookupAccount(addr common.Address) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t.pre[addr] = &account{
|
acc := &account{
|
||||||
Balance: t.env.StateDB.GetBalance(addr),
|
Balance: t.env.StateDB.GetBalance(addr),
|
||||||
Nonce: t.env.StateDB.GetNonce(addr),
|
Nonce: t.env.StateDB.GetNonce(addr),
|
||||||
Code: t.env.StateDB.GetCode(addr),
|
Code: t.env.StateDB.GetCode(addr),
|
||||||
Storage: make(map[common.Hash]common.Hash),
|
Storage: make(map[common.Hash]common.Hash),
|
||||||
}
|
}
|
||||||
|
if !acc.exists() {
|
||||||
|
acc.empty = true
|
||||||
|
}
|
||||||
|
t.pre[addr] = acc
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookupStorage fetches the requested storage slot and adds
|
// lookupStorage fetches the requested storage slot and adds
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,8 @@ func BenchmarkTransactionTrace(b *testing.B) {
|
||||||
Code: []byte{},
|
Code: []byte{},
|
||||||
Balance: big.NewInt(500000000000000),
|
Balance: big.NewInt(500000000000000),
|
||||||
}
|
}
|
||||||
statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc)
|
state := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc)
|
||||||
|
|
||||||
// Create the tracer, the EVM environment and run it
|
// Create the tracer, the EVM environment and run it
|
||||||
tracer := logger.NewStructLogger(&logger.Config{
|
tracer := logger.NewStructLogger(&logger.Config{
|
||||||
Debug: false,
|
Debug: false,
|
||||||
|
|
@ -87,7 +88,7 @@ func BenchmarkTransactionTrace(b *testing.B) {
|
||||||
//EnableMemory: false,
|
//EnableMemory: false,
|
||||||
//EnableReturnData: false,
|
//EnableReturnData: false,
|
||||||
})
|
})
|
||||||
evm := vm.NewEVM(context, txContext, statedb, nil, params.AllEthashProtocolChanges, vm.Config{Tracer: tracer})
|
evm := vm.NewEVM(context, txContext, state, nil, params.AllEthashProtocolChanges, vm.Config{Tracer: tracer.Hooks()})
|
||||||
msg, err := core.TransactionToMessage(tx, signer, nil, nil, context.BaseFee)
|
msg, err := core.TransactionToMessage(tx, signer, nil, nil, context.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||||
|
|
@ -96,54 +97,16 @@ func BenchmarkTransactionTrace(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
snap := statedb.Snapshot()
|
snap := state.Snapshot()
|
||||||
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||||
_, err = st.TransitionDb(common.Address{})
|
_, err = st.TransitionDb(common.Address{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
statedb.RevertToSnapshot(snap)
|
state.RevertToSnapshot(snap)
|
||||||
if have, want := len(tracer.StructLogs()), 244752; have != want {
|
if have, want := len(tracer.StructLogs()), 244752; have != want {
|
||||||
b.Fatalf("trace wrong, want %d steps, have %d", want, have)
|
b.Fatalf("trace wrong, want %d steps, have %d", want, have)
|
||||||
}
|
}
|
||||||
tracer.Reset()
|
tracer.Reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMemCopying(t *testing.T) {
|
|
||||||
for i, tc := range []struct {
|
|
||||||
memsize int64
|
|
||||||
offset int64
|
|
||||||
size int64
|
|
||||||
wantErr string
|
|
||||||
wantSize int
|
|
||||||
}{
|
|
||||||
{0, 0, 100, "", 100}, // Should pad up to 100
|
|
||||||
{0, 100, 0, "", 0}, // No need to pad (0 size)
|
|
||||||
{100, 50, 100, "", 100}, // Should pad 100-150
|
|
||||||
{100, 50, 5, "", 5}, // Wanted range fully within memory
|
|
||||||
{100, -50, 0, "offset or size must not be negative", 0}, // Error
|
|
||||||
{0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Error
|
|
||||||
{10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Error
|
|
||||||
|
|
||||||
} {
|
|
||||||
mem := vm.NewMemory()
|
|
||||||
mem.Resize(uint64(tc.memsize))
|
|
||||||
cpy, err := GetMemoryCopyPadded(mem, tc.offset, tc.size)
|
|
||||||
if want := tc.wantErr; want != "" {
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("test %d: want '%v' have no error", i, want)
|
|
||||||
}
|
|
||||||
if have := err.Error(); want != have {
|
|
||||||
t.Fatalf("test %d: want '%v' have '%v'", i, want, have)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("test %d: unexpected error: %v", i, err)
|
|
||||||
}
|
|
||||||
if want, have := tc.wantSize, len(cpy); have != want {
|
|
||||||
t.Fatalf("test %d: want %v have %v", i, want, have)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
|
|
@ -542,41 +543,41 @@ type OverrideAccount struct {
|
||||||
type StateOverride map[common.Address]OverrideAccount
|
type StateOverride map[common.Address]OverrideAccount
|
||||||
|
|
||||||
// Apply overrides the fields of specified accounts into the given state.
|
// Apply overrides the fields of specified accounts into the given state.
|
||||||
func (diff *StateOverride) Apply(state *state.StateDB) error {
|
func (diff *StateOverride) Apply(statedb *state.StateDB) error {
|
||||||
if diff == nil {
|
if diff == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for addr, account := range *diff {
|
for addr, account := range *diff {
|
||||||
// Override account nonce.
|
// Override account nonce.
|
||||||
if account.Nonce != nil {
|
if account.Nonce != nil {
|
||||||
state.SetNonce(addr, uint64(*account.Nonce))
|
statedb.SetNonce(addr, uint64(*account.Nonce))
|
||||||
}
|
}
|
||||||
// Override account(contract) code.
|
// Override account(contract) code.
|
||||||
if account.Code != nil {
|
if account.Code != nil {
|
||||||
state.SetCode(addr, *account.Code)
|
statedb.SetCode(addr, *account.Code)
|
||||||
}
|
}
|
||||||
// Override account balance.
|
// Override account balance.
|
||||||
if account.Balance != nil {
|
if account.Balance != nil {
|
||||||
state.SetBalance(addr, (*big.Int)(*account.Balance))
|
statedb.SetBalance(addr, (*big.Int)(*account.Balance), tracing.BalanceChangeUnspecified)
|
||||||
}
|
}
|
||||||
if account.State != nil && account.StateDiff != nil {
|
if account.State != nil && account.StateDiff != nil {
|
||||||
return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex())
|
return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex())
|
||||||
}
|
}
|
||||||
// Replace entire state if caller requires.
|
// Replace entire state if caller requires.
|
||||||
if account.State != nil {
|
if account.State != nil {
|
||||||
state.SetStorage(addr, *account.State)
|
statedb.SetStorage(addr, *account.State)
|
||||||
}
|
}
|
||||||
// Apply state diff into specified accounts.
|
// Apply state diff into specified accounts.
|
||||||
if account.StateDiff != nil {
|
if account.StateDiff != nil {
|
||||||
for key, value := range *account.StateDiff {
|
for key, value := range *account.StateDiff {
|
||||||
state.SetState(addr, key, value)
|
statedb.SetState(addr, key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Now finalize the changes. Finalize is normally performed between transactions.
|
// Now finalize the changes. Finalize is normally performed between transactions.
|
||||||
// By using finalize, the overrides are semantically behaving as
|
// By using finalize, the overrides are semantically behaving as
|
||||||
// if they were created in a transaction just before the tracing occur.
|
// if they were created in a transaction just before the tracing occur.
|
||||||
state.Finalise(false)
|
statedb.Finalise(false)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1158,13 +1159,12 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
|
||||||
if blockOverrides != nil {
|
if blockOverrides != nil {
|
||||||
blockOverrides.Apply(&blockCtx)
|
blockOverrides.Apply(&blockCtx)
|
||||||
}
|
}
|
||||||
msg, err := args.ToMessage(b, header.Number, globalGasCap, blockCtx.BaseFee)
|
if err := args.CallDefaults(globalGasCap, blockCtx.BaseFee, b.ChainConfig().ChainId); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
msg := args.ToMessage(b, blockCtx.BaseFee)
|
||||||
msg.BalanceTokenFee = new(big.Int).SetUint64(msg.GasLimit)
|
msg.BalanceTokenFee = new(big.Int).SetUint64(msg.GasLimit)
|
||||||
msg.BalanceTokenFee.Mul(msg.BalanceTokenFee, msg.GasPrice)
|
msg.BalanceTokenFee.Mul(msg.BalanceTokenFee, msg.GasPrice)
|
||||||
|
|
||||||
evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vm.Config{NoBaseFee: true}, &blockCtx)
|
evm, vmError, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &vm.Config{NoBaseFee: true}, &blockCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -1178,8 +1178,7 @@ func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash
|
||||||
|
|
||||||
// Execute the message.
|
// Execute the message.
|
||||||
gp := new(core.GasPool).AddGas(gomath.MaxUint64)
|
gp := new(core.GasPool).AddGas(gomath.MaxUint64)
|
||||||
owner := common.Address{}
|
result, err := core.ApplyMessage(evm, msg, gp, common.Address{})
|
||||||
result, err := core.ApplyMessage(evm, msg, gp, owner)
|
|
||||||
if err := vmError(); err != nil {
|
if err := vmError(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1801,10 +1800,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
||||||
statedb := db.Copy()
|
statedb := db.Copy()
|
||||||
// Set the accesslist to the last al
|
// Set the accesslist to the last al
|
||||||
args.AccessList = &accessList
|
args.AccessList = &accessList
|
||||||
msg, err := args.ToMessage(b, block.Number(), b.RPCGasCap(), header.BaseFee)
|
msg := args.ToMessage(b, header.BaseFee)
|
||||||
if err != nil {
|
|
||||||
return nil, 0, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
|
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
|
||||||
var balanceTokenFee *big.Int
|
var balanceTokenFee *big.Int
|
||||||
|
|
@ -1815,7 +1811,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
||||||
|
|
||||||
// Apply the transaction with the access list tracer
|
// Apply the transaction with the access list tracer
|
||||||
tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles)
|
tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles)
|
||||||
config := vm.Config{Tracer: tracer, NoBaseFee: true}
|
config := vm.Config{Tracer: tracer.Hooks(), NoBaseFee: true}
|
||||||
vmenv, _, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &config, nil)
|
vmenv, _, err := b.GetEVM(ctx, msg, statedb, XDCxState, header, &config, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, err
|
return nil, 0, nil, err
|
||||||
|
|
@ -1823,7 +1819,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
|
||||||
// TODO: determine the value of owner
|
// TODO: determine the value of owner
|
||||||
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit), owner)
|
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit), owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.toTransaction().Hash(), err)
|
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.ToTransaction().Hash(), err)
|
||||||
}
|
}
|
||||||
if tracer.Equal(prevTracer) {
|
if tracer.Equal(prevTracer) {
|
||||||
return accessList, res.UsedGas, res.Err, nil
|
return accessList, res.UsedGas, res.Err, nil
|
||||||
|
|
@ -2115,7 +2111,7 @@ func (s *TransactionAPI) SendTransaction(ctx context.Context, args TransactionAr
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
// Assemble the transaction and sign with the wallet
|
// Assemble the transaction and sign with the wallet
|
||||||
tx := args.toTransaction()
|
tx := args.ToTransaction()
|
||||||
|
|
||||||
var chainID *big.Int
|
var chainID *big.Int
|
||||||
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
|
if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
|
||||||
|
|
@ -2137,7 +2133,7 @@ func (s *TransactionAPI) FillTransaction(ctx context.Context, args TransactionAr
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Assemble the transaction and obtain rlp
|
// Assemble the transaction and obtain rlp
|
||||||
tx := args.toTransaction()
|
tx := args.ToTransaction()
|
||||||
data, err := tx.MarshalBinary()
|
data, err := tx.MarshalBinary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -3042,7 +3038,7 @@ func (s *TransactionAPI) SignTransaction(ctx context.Context, args TransactionAr
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Before actually sign the transaction, ensure the transaction fee is reasonable.
|
// Before actually sign the transaction, ensure the transaction fee is reasonable.
|
||||||
tx := args.toTransaction()
|
tx := args.ToTransaction()
|
||||||
if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil {
|
if err := checkTxFee(tx.GasPrice(), tx.Gas(), s.b.RPCTxFeeCap()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -3090,7 +3086,7 @@ func (s *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, g
|
||||||
if err := sendArgs.setDefaults(ctx, s.b, false); err != nil {
|
if err := sendArgs.setDefaults(ctx, s.b, false); err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
matchTx := sendArgs.toTransaction()
|
matchTx := sendArgs.ToTransaction()
|
||||||
|
|
||||||
// Before replacing the old transaction, ensure the _new_ transaction fee is reasonable.
|
// Before replacing the old transaction, ensure the _new_ transaction fee is reasonable.
|
||||||
var price = matchTx.GasPrice()
|
var price = matchTx.GasPrice()
|
||||||
|
|
@ -3121,7 +3117,7 @@ func (s *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, g
|
||||||
if gasLimit != nil && *gasLimit != 0 {
|
if gasLimit != nil && *gasLimit != 0 {
|
||||||
sendArgs.Gas = gasLimit
|
sendArgs.Gas = gasLimit
|
||||||
}
|
}
|
||||||
signedTx, err := s.sign(sendArgs.from(), sendArgs.toTransaction())
|
signedTx, err := s.sign(sendArgs.from(), sendArgs.ToTransaction())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -226,13 +226,91 @@ type AccountBackend interface {
|
||||||
AccountManager() *accounts.Manager
|
AccountManager() *accounts.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CallDefaults sanitizes the transaction arguments, often filling in zero values,
|
||||||
|
// for the purpose of eth_call class of RPC methods.
|
||||||
|
func (args *TransactionArgs) CallDefaults(globalGasCap uint64, baseFee *big.Int, chainID *big.Int) error {
|
||||||
|
// Reject invalid combinations of pre- and post-1559 fee styles
|
||||||
|
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
|
||||||
|
return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
||||||
|
}
|
||||||
|
if args.ChainID == nil {
|
||||||
|
args.ChainID = (*hexutil.Big)(chainID)
|
||||||
|
} else {
|
||||||
|
if have := (*big.Int)(args.ChainID); have.Cmp(chainID) != 0 {
|
||||||
|
return fmt.Errorf("chainId does not match node's (have=%v, want=%v)", have, chainID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if args.Gas == nil {
|
||||||
|
gas := globalGasCap
|
||||||
|
if gas == 0 {
|
||||||
|
gas = uint64(math.MaxUint64 / 2)
|
||||||
|
}
|
||||||
|
args.Gas = (*hexutil.Uint64)(&gas)
|
||||||
|
} else {
|
||||||
|
if globalGasCap > 0 && globalGasCap < uint64(*args.Gas) {
|
||||||
|
log.Warn("Caller gas above allowance, capping", "requested", args.Gas, "cap", globalGasCap)
|
||||||
|
args.Gas = (*hexutil.Uint64)(&globalGasCap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if args.Nonce == nil {
|
||||||
|
args.Nonce = new(hexutil.Uint64)
|
||||||
|
}
|
||||||
|
if args.Value == nil {
|
||||||
|
args.Value = new(hexutil.Big)
|
||||||
|
}
|
||||||
|
if baseFee == nil {
|
||||||
|
// If there's no basefee, then it must be a non-1559 execution
|
||||||
|
if args.GasPrice == nil {
|
||||||
|
args.GasPrice = new(hexutil.Big)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// A basefee is provided, necessitating 1559-type execution
|
||||||
|
if args.MaxFeePerGas == nil {
|
||||||
|
args.MaxFeePerGas = new(hexutil.Big)
|
||||||
|
}
|
||||||
|
if args.MaxPriorityFeePerGas == nil {
|
||||||
|
args.MaxPriorityFeePerGas = new(hexutil.Big)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ToMessage converts the transaction arguments to the Message type used by the
|
// ToMessage converts the transaction arguments to the Message type used by the
|
||||||
// core evm. This method is used in calls and traces that do not require a real
|
// core evm. This method is used in calls and traces that do not require a real
|
||||||
// live transaction.
|
// live transaction.
|
||||||
func (args *TransactionArgs) ToMessage(b AccountBackend, number *big.Int, globalGasCap uint64, baseFee *big.Int) (*core.Message, error) {
|
func (args *TransactionArgs) ToMessage(b AccountBackend, baseFee *big.Int) *core.Message {
|
||||||
// Reject invalid combinations of pre- and post-1559 fee styles
|
var (
|
||||||
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
|
gasPrice *big.Int
|
||||||
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
gasFeeCap *big.Int
|
||||||
|
gasTipCap *big.Int
|
||||||
|
)
|
||||||
|
if baseFee == nil {
|
||||||
|
gasPrice = args.GasPrice.ToInt()
|
||||||
|
gasFeeCap, gasTipCap = gasPrice, gasPrice
|
||||||
|
} else {
|
||||||
|
// A basefee is provided, necessitating 1559-type execution
|
||||||
|
if args.GasPrice != nil {
|
||||||
|
// User specified the legacy gas field, convert to 1559 gas typing
|
||||||
|
gasPrice = args.GasPrice.ToInt()
|
||||||
|
gasFeeCap, gasTipCap = gasPrice, gasPrice
|
||||||
|
} else {
|
||||||
|
// User specified 1559 gas fields (or none), use those
|
||||||
|
gasFeeCap = args.MaxFeePerGas.ToInt()
|
||||||
|
gasTipCap = args.MaxPriorityFeePerGas.ToInt()
|
||||||
|
// Backfill the legacy gasPrice for EVM execution, unless we're all zeroes
|
||||||
|
gasPrice = new(big.Int)
|
||||||
|
if gasFeeCap.BitLen() > 0 || gasTipCap.BitLen() > 0 {
|
||||||
|
gasPrice = gasPrice.Add(gasTipCap, baseFee)
|
||||||
|
if gasPrice.Cmp(gasFeeCap) > 0 {
|
||||||
|
gasPrice = gasFeeCap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var accessList types.AccessList
|
||||||
|
if args.AccessList != nil {
|
||||||
|
accessList = *args.AccessList
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sender address or use zero address if none specified.
|
// Set sender address or use zero address if none specified.
|
||||||
|
|
@ -245,84 +323,18 @@ func (args *TransactionArgs) ToMessage(b AccountBackend, number *big.Int, global
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set default gas & gas price if none were set
|
return &core.Message{
|
||||||
gas := globalGasCap
|
|
||||||
if args.Gas != nil {
|
|
||||||
gas = uint64(*args.Gas)
|
|
||||||
}
|
|
||||||
if gas == 0 {
|
|
||||||
gas = math.MaxUint64 / 2
|
|
||||||
}
|
|
||||||
if globalGasCap != 0 && globalGasCap < gas {
|
|
||||||
log.Warn("Caller gas above allowance, capping", "requested", gas, "cap", globalGasCap)
|
|
||||||
gas = globalGasCap
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
gasPrice *big.Int
|
|
||||||
gasFeeCap *big.Int
|
|
||||||
gasTipCap *big.Int
|
|
||||||
)
|
|
||||||
if baseFee == nil {
|
|
||||||
// If there's no basefee, then it must be a non-1559 execution
|
|
||||||
gasPrice = new(big.Int)
|
|
||||||
if args.GasPrice != nil {
|
|
||||||
gasPrice = args.GasPrice.ToInt()
|
|
||||||
}
|
|
||||||
if gasPrice.Sign() <= 0 {
|
|
||||||
gasPrice = new(big.Int).SetUint64(defaultGasPrice)
|
|
||||||
}
|
|
||||||
gasFeeCap, gasTipCap = gasPrice, gasPrice
|
|
||||||
} else {
|
|
||||||
// A basefee is provided, necessitating 1559-type execution
|
|
||||||
if args.GasPrice != nil {
|
|
||||||
// User specified the legacy gas field, convert to 1559 gas typing
|
|
||||||
gasPrice = args.GasPrice.ToInt()
|
|
||||||
gasFeeCap, gasTipCap = gasPrice, gasPrice
|
|
||||||
} else {
|
|
||||||
// User specified 1559 gas fields (or none), use those
|
|
||||||
gasFeeCap = new(big.Int)
|
|
||||||
if args.MaxFeePerGas != nil {
|
|
||||||
gasFeeCap = args.MaxFeePerGas.ToInt()
|
|
||||||
}
|
|
||||||
gasTipCap = new(big.Int)
|
|
||||||
if args.MaxPriorityFeePerGas != nil {
|
|
||||||
gasTipCap = args.MaxPriorityFeePerGas.ToInt()
|
|
||||||
}
|
|
||||||
// Backfill the legacy gasPrice for EVM execution, unless we're all zeroes
|
|
||||||
gasPrice = new(big.Int)
|
|
||||||
if gasFeeCap.BitLen() > 0 || gasTipCap.BitLen() > 0 {
|
|
||||||
gasPrice = gasPrice.Add(gasTipCap, baseFee)
|
|
||||||
if gasPrice.Cmp(gasFeeCap) > 0 {
|
|
||||||
gasPrice = gasFeeCap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
value := new(big.Int)
|
|
||||||
if args.Value != nil {
|
|
||||||
value = args.Value.ToInt()
|
|
||||||
}
|
|
||||||
data := args.data()
|
|
||||||
var accessList types.AccessList
|
|
||||||
if args.AccessList != nil {
|
|
||||||
accessList = *args.AccessList
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := &core.Message{
|
|
||||||
From: addr,
|
From: addr,
|
||||||
To: args.To,
|
To: args.To,
|
||||||
Nonce: 0,
|
Value: (*big.Int)(args.Value),
|
||||||
Value: value,
|
GasLimit: uint64(*args.Gas),
|
||||||
GasLimit: gas,
|
|
||||||
GasPrice: gasPrice,
|
GasPrice: gasPrice,
|
||||||
GasFeeCap: gasFeeCap,
|
GasFeeCap: gasFeeCap,
|
||||||
GasTipCap: gasTipCap,
|
GasTipCap: gasTipCap,
|
||||||
Data: data,
|
Data: args.data(),
|
||||||
AccessList: accessList,
|
AccessList: accessList,
|
||||||
SkipAccountChecks: true,
|
SkipAccountChecks: true,
|
||||||
}
|
}
|
||||||
return msg, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// toTransaction converts the arguments to a transaction.
|
// toTransaction converts the arguments to a transaction.
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package tests
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
@ -76,23 +77,24 @@ func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test failed, re-run with tracing enabled.
|
// Test failed, re-run with tracing enabled.
|
||||||
|
t.Error(err)
|
||||||
if gasLimit > traceErrorLimit {
|
if gasLimit > traceErrorLimit {
|
||||||
t.Log("gas limit too high for EVM trace")
|
t.Log("gas limit too high for EVM trace")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tracer := logger.NewStructLogger(nil)
|
buf := new(bytes.Buffer)
|
||||||
config.Tracer = tracer
|
w := bufio.NewWriter(buf)
|
||||||
|
config.Tracer = logger.NewJSONLogger(&logger.Config{}, w)
|
||||||
err2 := test(config)
|
err2 := test(config)
|
||||||
if !reflect.DeepEqual(err, err2) {
|
if !reflect.DeepEqual(err, err2) {
|
||||||
t.Errorf("different error for second run: %v", err2)
|
t.Errorf("different error for second run: %v", err2)
|
||||||
}
|
}
|
||||||
buf := new(bytes.Buffer)
|
w.Flush()
|
||||||
logger.WriteTrace(buf, tracer.StructLogs())
|
|
||||||
if buf.Len() == 0 {
|
if buf.Len() == 0 {
|
||||||
t.Log("no EVM operation logs generated")
|
t.Log("no EVM operation logs generated")
|
||||||
} else {
|
} else {
|
||||||
t.Log("EVM operation log:\n" + buf.String())
|
t.Log("EVM operation log:\n" + buf.String())
|
||||||
}
|
}
|
||||||
t.Logf("EVM output: %#x", tracer.Output())
|
// t.Logf("EVM output: 0x%x", tracer.Output())
|
||||||
t.Logf("EVM error: %v", tracer.Error())
|
// t.Logf("EVM error: %v", tracer.Error())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/tracing"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
|
|
@ -191,7 +192,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB
|
||||||
for addr, a := range accounts {
|
for addr, a := range accounts {
|
||||||
statedb.SetCode(addr, a.Code)
|
statedb.SetCode(addr, a.Code)
|
||||||
statedb.SetNonce(addr, a.Nonce)
|
statedb.SetNonce(addr, a.Nonce)
|
||||||
statedb.SetBalance(addr, a.Balance)
|
statedb.SetBalance(addr, a.Balance, tracing.BalanceChangeUnspecified)
|
||||||
for k, v := range a.Storage {
|
for k, v := range a.Storage {
|
||||||
statedb.SetState(addr, k, v)
|
statedb.SetState(addr, k, v)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue