Files
evm/internal/ethapi/api_extra.go

214 lines
7.1 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ethapi
import (
"context"
"errors"
"fmt"
"math/big"
"github.com/luxfi/evm/commontype"
"github.com/luxfi/evm/core"
"github.com/luxfi/evm/params"
"github.com/luxfi/evm/params/extras"
"github.com/luxfi/evm/rpc"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/common/hexutil"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/rlp"
)
func (s *BlockChainAPI) GetChainConfig(ctx context.Context) *params.ChainConfigWithUpgradesJSON {
return params.ToWithUpgradesJSON(s.b.ChainConfig())
}
type DetailedExecutionResult struct {
UsedGas uint64 `json:"gas"` // Total used gas but include the refunded gas
ErrCode int `json:"errCode"` // EVM error code
Err string `json:"err"` // Any error encountered during the execution(listed in core/vm/errors.go)
ReturnData hexutil.Bytes `json:"returnData"` // Data from evm(function result or data supplied with revert opcode)
}
// CallDetailed performs the same call as Call, but returns the full context
func (s *BlockChainAPI) CallDetailed(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride) (*DetailedExecutionResult, error) {
result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, nil, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
if err != nil {
return nil, err
}
reply := &DetailedExecutionResult{
UsedGas: result.UsedGas,
ReturnData: result.ReturnData,
}
if result.Err != nil {
if err, ok := result.Err.(rpc.Error); ok {
reply.ErrCode = err.ErrorCode()
}
reply.Err = result.Err.Error()
}
// If the result contains a revert reason, try to unpack and return it.
if len(result.Revert()) > 0 {
err := newRevertError(result.Revert())
reply.ErrCode = err.ErrorCode()
reply.Err = err.Error()
}
return reply, nil
}
// Note: this API is moved directly from ./eth/api.go to ensure that it is available under an API that is enabled by
// default without duplicating the code and serving the same API in the original location as well without creating a
// cyclic import.
//
// BadBlockArgs represents the entries in the list returned when bad blocks are queried.
type BadBlockArgs struct {
Hash common.Hash `json:"hash"`
Block map[string]interface{} `json:"block"`
RLP string `json:"rlp"`
Reason *core.BadBlockReason `json:"reason"`
}
// GetBadBlocks returns a list of the last 'bad blocks' that the client has seen on the network
// and returns them as a JSON list of block hashes.
func (s *BlockChainAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs, error) {
var (
badBlocks, reasons = s.b.BadBlocks()
results = make([]*BadBlockArgs, 0, len(badBlocks))
)
for i, block := range badBlocks {
var (
blockRlp string
blockJSON map[string]interface{}
)
if rlpBytes, err := rlp.EncodeToBytes(block); err != nil {
blockRlp = err.Error() // Surface encoding error as the RLP string
} else {
blockRlp = fmt.Sprintf("%#x", rlpBytes)
}
blockJSON = RPCMarshalBlock(block, true, true, s.b.ChainConfig())
results = append(results, &BadBlockArgs{
Hash: block.Hash(),
RLP: blockRlp,
Block: blockJSON,
Reason: reasons[i],
})
}
return results, nil
}
type FeeConfigResult struct {
FeeConfig commontype.FeeConfig `json:"feeConfig"`
LastChangedAt *big.Int `json:"lastChangedAt,omitempty"`
}
func (s *BlockChainAPI) FeeConfig(ctx context.Context, blockNrOrHash *rpc.BlockNumberOrHash) (*FeeConfigResult, error) {
var (
header *types.Header
err error
)
if blockNrOrHash == nil {
header = s.b.CurrentHeader()
} else {
header, err = s.b.HeaderByNumberOrHash(ctx, *blockNrOrHash)
if err != nil {
return nil, err
}
}
feeConfig, lastChangedAt, err := s.b.GetFeeConfigAt(header)
if err != nil {
return nil, err
}
return &FeeConfigResult{FeeConfig: feeConfig, LastChangedAt: lastChangedAt}, nil
}
// GetActivePrecompilesAt returns the active precompile configs at the given block timestamp.
// DEPRECATED: Use GetActiveRulesAt instead.
func (s *BlockChainAPI) GetActivePrecompilesAt(ctx context.Context, blockTimestamp *uint64) extras.Precompiles {
var timestamp uint64
if blockTimestamp == nil {
timestamp = s.b.CurrentHeader().Time
} else {
timestamp = *blockTimestamp
}
return params.GetExtra(s.b.ChainConfig()).EnabledStatefulPrecompiles(timestamp)
}
type ActivePrecompilesResult struct {
Timestamp uint64 `json:"timestamp"`
}
type ActiveRulesResult struct {
EthRules params.Rules `json:"ethRules"`
LuxRules extras.LuxRules `json:"luxRules"`
ActivePrecompiles map[string]ActivePrecompilesResult `json:"precompiles"`
}
// GetActiveRulesAt returns the active rules at the given block timestamp.
func (s *BlockChainAPI) GetActiveRulesAt(ctx context.Context, blockTimestamp *uint64) ActiveRulesResult {
var timestamp uint64
if blockTimestamp == nil {
timestamp = s.b.CurrentHeader().Time
} else {
timestamp = *blockTimestamp
}
rules := s.b.ChainConfig().Rules(common.Big0, params.IsMergeTODO, timestamp)
extrasRules := params.GetExtrasRules(rules, s.b.ChainConfig(), timestamp)
res := ActiveRulesResult{
EthRules: rules,
LuxRules: extrasRules.LuxRules,
}
res.ActivePrecompiles = make(map[string]ActivePrecompilesResult)
for addr, precompileConfig := range extrasRules.Precompiles {
if precompileConfig.Timestamp() == nil {
continue
}
res.ActivePrecompiles[addr.Hex()] = ActivePrecompilesResult{
Timestamp: *precompileConfig.Timestamp(),
}
}
return res
}
// stateQueryBlockNumberAllowed returns a nil error if:
// - the node is configured to accept any state query (the query window is zero)
// - the block given has its number within the query window before the last accepted block.
// This query window is set to [core.TipBufferSize] when running in a non-archive mode.
//
// Otherwise, it returns a non-nil error containing block number information.
func (s *BlockChainAPI) stateQueryBlockNumberAllowed(blockNumOrHash rpc.BlockNumberOrHash) (err error) {
queryWindow := s.b.HistoricalProofQueryWindow()
if s.b.IsArchive() && queryWindow == 0 {
return nil
}
lastAcceptedNumber := s.b.LastAcceptedBlock().NumberU64()
var number uint64
if blockNumOrHash.BlockNumber != nil {
number = uint64(blockNumOrHash.BlockNumber.Int64())
} else if blockHash, ok := blockNumOrHash.Hash(); ok {
block, err := s.b.BlockByHash(context.Background(), blockHash)
if err != nil {
return fmt.Errorf("failed to get block from hash: %s", err)
} else if block == nil {
return fmt.Errorf("block from hash %s doesn't exist", blockHash)
}
number = block.NumberU64()
} else {
return errors.New("block number or hash not provided")
}
var oldestAllowed uint64
if lastAcceptedNumber > queryWindow {
oldestAllowed = lastAcceptedNumber - queryWindow
}
if number >= oldestAllowed {
return nil
}
return fmt.Errorf("block number %d is before the oldest allowed block number %d (window of %d blocks)",
number, oldestAllowed, queryWindow)
}