Skip to content
Merged
28 changes: 6 additions & 22 deletions rpc/jsonrpc/debug_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -699,26 +699,16 @@ func (api *DebugAPIImpl) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.B
if block == nil {
return nil, nil
}
receipts, err := api.getReceipts(ctx, tx, block)
chainConfig, err := api.chainConfig(ctx, tx)
if err != nil {
return nil, err
}
chainConfig, err := api.chainConfig(ctx, tx)
receipts, borReceipt, err := api.getReceiptsWithBor(ctx, tx, chainConfig, block)
if err != nil {
return nil, err
}
if chainConfig.Bor != nil {
events, err := api.bridgeReader.Events(ctx, block.Hash(), blockNum)
if err != nil {
return nil, err
}
if len(events) != 0 {
borReceipt, err := api.borReceiptGenerator.GenerateBorReceipt(ctx, tx, block, events, chainConfig)
if err != nil {
return nil, err
}
receipts = append(receipts, borReceipt)
}
if borReceipt != nil {
receipts = append(receipts, borReceipt)
}

result := make([]hexutil.Bytes, len(receipts))
Expand Down Expand Up @@ -807,18 +797,12 @@ func (api *DebugAPIImpl) GetRawTransaction(ctx context.Context, txnHash common.H
}
}

txNumMin, err := api._txNumReader.Min(ctx, tx, blockNum)
txnIndex, err := api.txnIndexInBlock(ctx, tx, blockNum, txNum, isBorStateSyncTx)
if err != nil {
return nil, err
}

if txNumMin+1 > txNum && !isBorStateSyncTx {
return nil, fmt.Errorf("uint underflow txnums error txNum: %d, txNumMin: %d, blockNum: %d", txNum, txNumMin, blockNum)
}

var txnIndex = txNum - txNumMin - 1

txn, err := api._txnReader.TxnByIdxInBlock(ctx, tx, blockNum, int(txnIndex))
txn, err := api._txnReader.TxnByIdxInBlock(ctx, tx, blockNum, txnIndex)
if err != nil {
return nil, err
}
Expand Down
34 changes: 34 additions & 0 deletions rpc/jsonrpc/debug_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,40 @@ func TestGetRawTransaction(t *testing.T) {
require.True(testedOnce, "Test flow didn't touch the target flow")
}

func TestGetRawReceipts(t *testing.T) {
m, _, _ := rpcdaemontest.CreateTestExecModule(t)
api := NewPrivateDebugAPI(newBaseApiForTest(m), m.DB, nil, 5000000, false)
ctx := context.Background()

require := require.New(t)
tx, err := m.DB.BeginTemporalRo(ctx)
require.NoError(err)
defer tx.Rollback()
number := *rawdb.ReadCurrentBlockNumber(tx)

testedNonEmpty := false
for i := uint64(0); i <= number; i++ {
block, err := api.blockByNumberWithSenders(ctx, tx, i)
require.NoError(err)
receipts, err := api.getReceipts(ctx, tx, block)
require.NoError(err)
expected := make([]hexutil.Bytes, len(receipts))
for j, receipt := range receipts {
b, err := receipt.MarshalBinary()
require.NoError(err)
expected[j] = b
}

raw, err := api.GetRawReceipts(ctx, rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(i)))
require.NoError(err)
require.Equal(expected, raw)
if len(raw) > 0 {
testedNonEmpty = true
}
}
require.True(testedNonEmpty, "test chain has no receipts")
}

func TestExecutionWitness(t *testing.T) {
// Enable historical commitment schema so the test aggregator maintains per-block history.
previousSchema := statecfg.Schema
Expand Down
92 changes: 39 additions & 53 deletions rpc/jsonrpc/erigon_receipts.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ import (
"context"
"errors"
"fmt"
"math/big"

"github.com/RoaringBitmap/roaring/v2"

"github.com/erigontech/erigon/common"
"github.com/erigontech/erigon/common/hexutil"
"github.com/erigontech/erigon/common/log/v3"
"github.com/erigontech/erigon/db/kv"
"github.com/erigontech/erigon/db/kv/order"
"github.com/erigontech/erigon/db/kv/rawdbv3"
"github.com/erigontech/erigon/execution/exec"
Expand Down Expand Up @@ -77,6 +79,37 @@ func (api *ErigonImpl) GetLogsByHash(ctx context.Context, hash common.Hash) ([][
return logs, nil
}

// logRangeLatestOnly resolves a filter range where the only negative tag accepted for
// FromBlock/ToBlock is "latest"; other tags (pending, safe, finalized) are rejected.
func logRangeLatestOnly(tx kv.Tx, crit filters.FilterCriteria) (begin, end uint64, err error) {
latest, err := rpchelper.GetLatestBlockNumber(tx)
if err != nil {
return 0, 0, err
}
begin, err = resolveLogBound(crit.FromBlock, 0, "FromBlock")
if err != nil {
return 0, 0, err
}
end, err = resolveLogBound(crit.ToBlock, latest, "ToBlock")
if err != nil {
return 0, 0, err
}
return begin, end, nil
}

func resolveLogBound(bound *big.Int, def uint64, name string) (uint64, error) {
switch {
case bound == nil:
return def, nil
case bound.Sign() >= 0:
return bound.Uint64(), nil
case bound.IsInt64() && bound.Int64() == int64(rpc.LatestBlockNumber):
return def, nil
default:
return 0, &rpc.CustomError{Message: fmt.Sprintf("negative value for %s: %v", name, bound), Code: rpc.ErrCodeInvalidParams}
}
}

// GetLogs implements erigon_getLogs. Returns an array of logs matching a given filter object.
func (api *ErigonImpl) GetLogs(ctx context.Context, crit filters.FilterCriteria) (types.ErigonLogs, error) {
var begin, end uint64
Expand All @@ -97,28 +130,11 @@ func (api *ErigonImpl) GetLogs(ctx context.Context, crit filters.FilterCriteria)
end = header.Number.Uint64()

} else {
// Convert the RPC block numbers into internal representations
latest, err := rpchelper.GetLatestBlockNumber(tx)
var err error
begin, end, err = logRangeLatestOnly(tx, crit)
if err != nil {
return nil, err
}

begin = 0
if crit.FromBlock != nil {
if crit.FromBlock.Sign() >= 0 {
begin = crit.FromBlock.Uint64()
} else if !crit.FromBlock.IsInt64() || crit.FromBlock.Int64() != int64(rpc.LatestBlockNumber) {
return nil, &rpc.CustomError{Message: fmt.Sprintf("negative value for FromBlock: %v", crit.FromBlock), Code: rpc.ErrCodeInvalidParams}
}
}
end = latest
if crit.ToBlock != nil {
if crit.ToBlock.Sign() >= 0 {
end = crit.ToBlock.Uint64()
} else if !crit.ToBlock.IsInt64() || crit.ToBlock.Int64() != int64(rpc.LatestBlockNumber) {
return nil, &rpc.CustomError{Message: fmt.Sprintf("negative value for ToBlock: %v", crit.ToBlock), Code: rpc.ErrCodeInvalidParams}
}
}
}

if len(crit.Topics) > maxTopics {
Expand Down Expand Up @@ -192,28 +208,10 @@ func (api *ErigonImpl) GetLatestLogs(ctx context.Context, crit filters.FilterCri
begin = header.Number.Uint64()
end = header.Number.Uint64()
} else {
// Convert the RPC block numbers into internal representations
latest, err := rpchelper.GetLatestBlockNumber(tx)
begin, end, err = logRangeLatestOnly(tx, crit)
if err != nil {
return nil, err
}

begin = 0
if crit.FromBlock != nil {
if crit.FromBlock.Sign() >= 0 {
begin = crit.FromBlock.Uint64()
} else if !crit.FromBlock.IsInt64() || crit.FromBlock.Int64() != int64(rpc.LatestBlockNumber) {
return nil, &rpc.CustomError{Message: fmt.Sprintf("negative value for FromBlock: %v", crit.FromBlock), Code: rpc.ErrCodeInvalidParams}
}
}
end = latest
if crit.ToBlock != nil {
if crit.ToBlock.Sign() >= 0 {
end = crit.ToBlock.Uint64()
} else if !crit.ToBlock.IsInt64() || crit.ToBlock.Int64() != int64(rpc.LatestBlockNumber) {
return nil, &rpc.CustomError{Message: fmt.Sprintf("negative value for ToBlock: %v", crit.ToBlock), Code: rpc.ErrCodeInvalidParams}
}
}
}
if end < begin {
return nil, &rpc.CustomError{Message: fmt.Sprintf("invalid block range: end (%d) < begin (%d)", end, begin), Code: rpc.ErrCodeInvalidParams}
Expand Down Expand Up @@ -420,7 +418,7 @@ func (api *ErigonImpl) GetBlockReceiptsByBlockHash(ctx context.Context, cannonic
if err != nil {
return nil, err
}
receipts, err := api.getReceipts(ctx, tx, block)
receipts, borReceipt, err := api.getReceiptsWithBor(ctx, tx, chainConfig, block)
if err != nil {
return nil, err
}
Expand All @@ -431,20 +429,8 @@ func (api *ErigonImpl) GetBlockReceiptsByBlockHash(ctx context.Context, cannonic
result = append(result, ethutils.MarshalReceipt(receipt, txn, chainConfig, block.HeaderNoCopy(), txn.Hash(), true, false))
}

if chainConfig.Bor != nil {
events, err := api.bridgeReader.Events(ctx, block.Hash(), blockNum)
if err != nil {
return nil, err
}

if len(events) != 0 {
borReceipt, err := api.borReceiptGenerator.GenerateBorReceipt(ctx, tx, block, events, chainConfig)
if err != nil {
return nil, err
}

result = append(result, ethutils.MarshalReceipt(borReceipt, bortypes.NewBorTransaction(), chainConfig, block.HeaderNoCopy(), borReceipt.TxHash, false, false))
}
if borReceipt != nil {
result = append(result, ethutils.MarshalReceipt(borReceipt, bortypes.NewBorTransaction(), chainConfig, block.HeaderNoCopy(), borReceipt.TxHash, false, false))
}

return result, nil
Expand Down
38 changes: 38 additions & 0 deletions rpc/jsonrpc/eth_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,44 @@ func (api *BaseAPI) txnLookup(ctx context.Context, tx kv.Tx, txnHash common.Hash
return api._txnReader.TxnLookup(ctx, overlayTx, txnHash)
}

func (api *BaseAPI) txnLookupWithBorFallback(ctx context.Context, tx kv.Tx, txnHash common.Hash, chainConfig *chain.Config) (blockNum uint64, txNum uint64, isBorStateSyncTxn bool, ok bool, err error) {
blockNum, txNum, ok, err = api.txnLookup(ctx, tx, txnHash)
if err != nil {
return 0, 0, false, false, err
}
if ok {
return blockNum, txNum, false, true, nil
}
if chainConfig.Bor == nil {
return 0, 0, false, false, nil
}
blockNum, ok, err = api.bridgeReader.EventTxnLookup(ctx, txnHash)
if err != nil {
return 0, 0, false, false, err
}
if !ok {
return 0, 0, false, false, nil
}
return blockNum, txNum, true, true, nil
}

// txnIndexInBlock derives the in-block txn index from a global txNum. Bor state sync
// txns are not part of the block body, so they yield the -1 sentinel and the consistency
// check is skipped (their txNum comes from a missed lookup).
func (api *BaseAPI) txnIndexInBlock(ctx context.Context, tx kv.Tx, blockNum, txNum uint64, isBorStateSyncTxn bool) (int, error) {
txNumMin, err := api._txNumReader.Min(ctx, tx, blockNum)
if err != nil {
return 0, err
}
if isBorStateSyncTxn {
return -1, nil
}
if txNumMin+1 > txNum {
return 0, fmt.Errorf("uint underflow txnums error txNum: %d, txNumMin: %d, blockNum: %d", txNum, txNumMin, blockNum)
}
return int(txNum - txNumMin - 1), nil
}

func (api *BaseAPI) blockByNumberWithSenders(ctx context.Context, tx kv.Tx, number uint64) (*types.Block, error) {
blockNumber, hash, _, err := rpchelper.GetBlockNumber(ctx, rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(number)), tx, api._blockReader, api.filters)
if err != nil {
Expand Down
30 changes: 5 additions & 25 deletions rpc/jsonrpc/eth_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"context"
"errors"
"fmt"
"sync/atomic"
"time"

"github.com/erigontech/erigon/common"
Expand Down Expand Up @@ -75,14 +74,10 @@ func (api *APIImpl) CallBundle(ctx context.Context, txHashes []common.Hash, stat
return nil, err
}

txNumMin, err := api._txNumReader.Min(ctx, tx, blockNumber)
txnIndex, err := api.txnIndexInBlock(ctx, tx, blockNumber, txNum, false)
if err != nil {
return nil, err
}
if txNumMin+1 > txNum {
return nil, fmt.Errorf("uint underflow txnums error txNum: %d, txNumMin: %d, blockNum: %d", txNum, txNumMin, blockNumber)
}
txnIndex := int(txNum - txNumMin - 1)
txn, err := api._txnReader.TxnByIdxInBlock(ctx, tx, blockNumber, txnIndex)
if err != nil {
return nil, err
Expand Down Expand Up @@ -147,30 +142,15 @@ func (api *APIImpl) CallBundle(ctx context.Context, txHashes []common.Hash, stat
// Get a new instance of the EVM
evm := vm.NewEVM(blockCtx, txCtx, ibs, chainConfig, vm.Config{})

// evmPtr is updated atomically each time evm is recreated in the loop,
// so the AfterFunc callback always cancels the current instance.
var evmPtr atomic.Pointer[vm.EVM]
evmPtr.Store(evm)

timeoutMilliSeconds := int64(5000)
if timeoutMilliSecondsPtr != nil {
timeoutMilliSeconds = *timeoutMilliSecondsPtr
}
timeout := time.Millisecond * time.Duration(timeoutMilliSeconds)
// Setup context so it may be cancelled the call has completed
// or, in case of unmetered gas, setup a context with a timeout.
var cancel context.CancelFunc
if timeout > 0 {
ctx, cancel = context.WithTimeout(ctx, timeout)
} else {
ctx, cancel = context.WithCancel(ctx)
}
// Make sure the context is cancelled when the call has completed
// this makes sure resources are cleaned up.
defer cancel()

stop := context.AfterFunc(ctx, func() { evmPtr.Load().Cancel() })
defer stop()
_, storeEVM, cleanup := setupEVMTimeout(ctx, timeout)
defer cleanup()
storeEVM(evm)

// Setup the gas pool (also for unmetered requests)
// and apply the message.
Expand All @@ -189,7 +169,7 @@ func (api *APIImpl) CallBundle(ctx context.Context, txHashes []common.Hash, stat
msg.SetCheckGas(false)
// Recreate EVM with the correct txCtx for this transaction
evm = vm.NewEVM(blockCtx, protocol.NewEVMTxContext(msg), ibs, chainConfig, vm.Config{})
evmPtr.Store(evm)
storeEVM(evm)
// Execute the transaction message
result, err := protocol.ApplyMessage(evm, msg, gp, true /* refunds */, false /* gasBailout */, engine)
if err != nil {
Expand Down
Loading
Loading