diff --git a/jsonrpc/debug_endpoint.go b/jsonrpc/debug_endpoint.go index b7e18ce937..f66d5d0b25 100644 --- a/jsonrpc/debug_endpoint.go +++ b/jsonrpc/debug_endpoint.go @@ -34,6 +34,9 @@ type debugBlockchainStore interface { // GetHeaderByNumber gets a header using the provided number GetHeaderByNumber(uint64) (*types.Header, bool) + // GetReceiptsByHash returns the receipts by block hash + GetReceiptsByHash(types.Hash) ([]*types.Receipt, error) + // ReadTxLookup returns a block hash in which a given txn was mined ReadTxLookup(txnHash types.Hash) (uint64, bool) @@ -295,6 +298,32 @@ func (d *Debug) GetRawTransaction(txHash types.Hash) (interface{}, error) { ) } +// GetRawReceipts retrieves the binary-encoded receipts of a single block. +func (d *Debug) GetRawReceipts(filter BlockNumberOrHash) (interface{}, error) { + return d.throttling.AttemptRequest( + context.Background(), + func() (interface{}, error) { + header, err := GetHeaderFromBlockNumberOrHash(filter, d.store) + if err != nil { + return nil, err + } + + receipts, err := d.store.GetReceiptsByHash(header.Hash) + if err != nil { + return nil, err + } + + result := make([][]byte, len(receipts)) + + for i, receipt := range receipts { + result[i] = receipt.MarshalRLP() + } + + return result, nil + }, + ) +} + func (d *Debug) traceBlock( block *types.Block, config *TraceConfig, diff --git a/jsonrpc/debug_endpoint_test.go b/jsonrpc/debug_endpoint_test.go index 9de5a4280b..b39e04c9e3 100644 --- a/jsonrpc/debug_endpoint_test.go +++ b/jsonrpc/debug_endpoint_test.go @@ -7,7 +7,6 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/0xPolygon/polygon-edge/helper/hex" @@ -19,6 +18,7 @@ import ( type debugEndpointMockStore struct { headerFn func() *types.Header getHeaderByNumberFn func(uint64) (*types.Header, bool) + getReceiptsByHashFn func(types.Hash) ([]*types.Receipt, error) readTxLookupFn func(types.Hash) (uint64, bool) getPendingTxFn func(types.Hash) (*types.Transaction, bool) getBlockByHashFn func(types.Hash, bool) (*types.Block, bool) @@ -38,6 +38,10 @@ func (s *debugEndpointMockStore) GetHeaderByNumber(num uint64) (*types.Header, b return s.getHeaderByNumberFn(num) } +func (s *debugEndpointMockStore) GetReceiptsByHash(hash types.Hash) ([]*types.Receipt, error) { + return s.getReceiptsByHashFn(hash) +} + func (s *debugEndpointMockStore) ReadTxLookup(txnHash types.Hash) (uint64, bool) { return s.readTxLookupFn(txnHash) } @@ -183,7 +187,7 @@ func TestDebugTraceConfigDecode(t *testing.T) { t.Run(test.input, func(t *testing.T) { result := TraceConfig{} - assert.NoError( + require.NoError( t, json.Unmarshal( []byte(test.input), @@ -191,7 +195,7 @@ func TestDebugTraceConfigDecode(t *testing.T) { ), ) - assert.Equal( + require.Equal( t, test.expected, result, @@ -220,13 +224,13 @@ func TestTraceBlockByNumber(t *testing.T) { return testLatestHeader }, getBlockByNumberFn: func(num uint64, full bool) (*types.Block, bool) { - assert.Equal(t, testLatestHeader.Number, num) - assert.True(t, full) + require.Equal(t, testLatestHeader.Number, num) + require.True(t, full) return testLatestBlock, true }, traceBlockFn: func(block *types.Block, tracer tracer.Tracer) ([]interface{}, error) { - assert.Equal(t, testLatestBlock, block) + require.Equal(t, testLatestBlock, block) return testTraceResults, nil }, @@ -240,13 +244,13 @@ func TestTraceBlockByNumber(t *testing.T) { config: &TraceConfig{}, store: &debugEndpointMockStore{ getBlockByNumberFn: func(num uint64, full bool) (*types.Block, bool) { - assert.Equal(t, testHeader10.Number, num) - assert.True(t, full) + require.Equal(t, testHeader10.Number, num) + require.True(t, full) return testBlock10, true }, traceBlockFn: func(block *types.Block, tracer tracer.Tracer) ([]interface{}, error) { - assert.Equal(t, testBlock10, block) + require.Equal(t, testBlock10, block) return testTraceResults, nil }, @@ -260,8 +264,8 @@ func TestTraceBlockByNumber(t *testing.T) { config: &TraceConfig{}, store: &debugEndpointMockStore{ getBlockByNumberFn: func(num uint64, full bool) (*types.Block, bool) { - assert.Equal(t, testGenesisHeader.Number, num) - assert.True(t, full) + require.Equal(t, testGenesisHeader.Number, num) + require.True(t, full) return testGenesisBlock, true }, @@ -275,8 +279,8 @@ func TestTraceBlockByNumber(t *testing.T) { config: &TraceConfig{}, store: &debugEndpointMockStore{ getBlockByNumberFn: func(num uint64, full bool) (*types.Block, bool) { - assert.Equal(t, uint64(11), num) - assert.True(t, full) + require.Equal(t, uint64(11), num) + require.True(t, full) return nil, false }, @@ -293,15 +297,14 @@ func TestTraceBlockByNumber(t *testing.T) { t.Parallel() endpoint := NewDebug(test.store, 100000) - res, err := endpoint.TraceBlockByNumber(test.blockNumber, test.config) - assert.Equal(t, test.result, res) + require.Equal(t, test.result, res) if test.err { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } @@ -324,13 +327,13 @@ func TestTraceBlockByHash(t *testing.T) { config: &TraceConfig{}, store: &debugEndpointMockStore{ getBlockByHashFn: func(hash types.Hash, full bool) (*types.Block, bool) { - assert.Equal(t, testHeader10.Hash, hash) - assert.True(t, full) + require.Equal(t, testHeader10.Hash, hash) + require.True(t, full) return testBlock10, true }, traceBlockFn: func(block *types.Block, tracer tracer.Tracer) ([]interface{}, error) { - assert.Equal(t, testBlock10, block) + require.Equal(t, testBlock10, block) return testTraceResults, nil }, @@ -344,8 +347,8 @@ func TestTraceBlockByHash(t *testing.T) { config: &TraceConfig{}, store: &debugEndpointMockStore{ getBlockByHashFn: func(hash types.Hash, full bool) (*types.Block, bool) { - assert.Equal(t, testHash11, hash) - assert.True(t, full) + require.Equal(t, testHash11, hash) + require.True(t, full) return nil, false }, @@ -362,15 +365,14 @@ func TestTraceBlockByHash(t *testing.T) { t.Parallel() endpoint := NewDebug(test.store, 100000) - res, err := endpoint.TraceBlockByHash(test.blockHash, test.config) - assert.Equal(t, test.result, res) + require.Equal(t, test.result, res) if test.err { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } @@ -396,7 +398,7 @@ func TestTraceBlock(t *testing.T) { config: &TraceConfig{}, store: &debugEndpointMockStore{ traceBlockFn: func(block *types.Block, tracer tracer.Tracer) ([]interface{}, error) { - assert.Equal(t, testLatestBlock, block) + require.Equal(t, testLatestBlock, block) return testTraceResults, nil }, @@ -421,15 +423,14 @@ func TestTraceBlock(t *testing.T) { t.Parallel() endpoint := NewDebug(test.store, 100000) - res, err := endpoint.TraceBlock(test.input, test.config) - assert.Equal(t, test.result, res) + require.Equal(t, test.result, res) if test.err { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } @@ -456,7 +457,7 @@ func TestTraceBlockFromFile(t *testing.T) { config: &TraceConfig{}, store: &debugEndpointMockStore{ traceBlockFn: func(block *types.Block, tracer tracer.Tracer) ([]interface{}, error) { - assert.Equal(t, testLatestBlock, block) + require.Equal(t, testLatestBlock, block) return testTraceResults, nil }, @@ -498,12 +499,12 @@ func TestTraceBlockFromFile(t *testing.T) { res, err := endpoint.TraceBlockFromFile("testfile.txt", test.config) - assert.Equal(t, test.result, res) + require.Equal(t, test.result, res) if test.err { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } @@ -533,19 +534,19 @@ func TestTraceTransaction(t *testing.T) { config: &TraceConfig{}, store: &debugEndpointMockStore{ readTxLookupFn: func(hash types.Hash) (uint64, bool) { - assert.Equal(t, testTxHash1, hash) + require.Equal(t, testTxHash1, hash) return testBlock10.Number(), true }, getBlockByNumberFn: func(number uint64, full bool) (*types.Block, bool) { - assert.Equal(t, testBlock10.Number(), number) - assert.True(t, full) + require.Equal(t, testBlock10.Number(), number) + require.True(t, full) return blockWithTx, true }, traceTxnFn: func(block *types.Block, txHash types.Hash, tracer tracer.Tracer) (interface{}, error) { - assert.Equal(t, blockWithTx, block) - assert.Equal(t, testTxHash1, txHash) + require.Equal(t, blockWithTx, block) + require.Equal(t, testTxHash1, txHash) return testTraceResult, nil }, @@ -559,7 +560,7 @@ func TestTraceTransaction(t *testing.T) { config: &TraceConfig{}, store: &debugEndpointMockStore{ readTxLookupFn: func(hash types.Hash) (uint64, bool) { - assert.Equal(t, testTxHash1, hash) + require.Equal(t, testTxHash1, hash) return 0, false }, @@ -573,13 +574,13 @@ func TestTraceTransaction(t *testing.T) { config: &TraceConfig{}, store: &debugEndpointMockStore{ readTxLookupFn: func(hash types.Hash) (uint64, bool) { - assert.Equal(t, testTxHash1, hash) + require.Equal(t, testTxHash1, hash) return testBlock10.Number(), true }, getBlockByNumberFn: func(number uint64, full bool) (*types.Block, bool) { - assert.Equal(t, testBlock10.Number(), number) - assert.True(t, full) + require.Equal(t, testBlock10.Number(), number) + require.True(t, full) return nil, false }, @@ -593,13 +594,13 @@ func TestTraceTransaction(t *testing.T) { config: &TraceConfig{}, store: &debugEndpointMockStore{ readTxLookupFn: func(hash types.Hash) (uint64, bool) { - assert.Equal(t, testTxHash1, hash) + require.Equal(t, testTxHash1, hash) return testBlock10.Number(), true }, getBlockByNumberFn: func(number uint64, full bool) (*types.Block, bool) { - assert.Equal(t, testBlock10.Number(), number) - assert.True(t, full) + require.Equal(t, testBlock10.Number(), number) + require.True(t, full) return testBlock10, true }, @@ -613,13 +614,13 @@ func TestTraceTransaction(t *testing.T) { config: &TraceConfig{}, store: &debugEndpointMockStore{ readTxLookupFn: func(hash types.Hash) (uint64, bool) { - assert.Equal(t, testTxHash1, hash) + require.Equal(t, testTxHash1, hash) return testBlock10.Number(), true }, getBlockByNumberFn: func(number uint64, full bool) (*types.Block, bool) { - assert.Equal(t, testBlock10.Number(), number) - assert.True(t, full) + require.Equal(t, testBlock10.Number(), number) + require.True(t, full) return &types.Block{ Header: testGenesisHeader, @@ -641,15 +642,14 @@ func TestTraceTransaction(t *testing.T) { t.Parallel() endpoint := NewDebug(test.store, 100000) - res, err := endpoint.TraceTransaction(test.txHash, test.config) - assert.Equal(t, test.result, res) + require.Equal(t, test.result, res) if test.err { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } @@ -717,13 +717,13 @@ func TestTraceCall(t *testing.T) { config: &TraceConfig{}, store: &debugEndpointMockStore{ getHeaderByNumberFn: func(num uint64) (*types.Header, bool) { - assert.Equal(t, testBlock10.Number(), num) + require.Equal(t, testBlock10.Number(), num) return testHeader10, true }, traceCallFn: func(tx *types.Transaction, header *types.Header, tracer tracer.Tracer) (interface{}, error) { - assert.Equal(t, decodedTx, tx) - assert.Equal(t, testHeader10, header) + require.Equal(t, decodedTx, tx) + require.Equal(t, testHeader10, header) return testTraceResult, nil }, @@ -746,8 +746,8 @@ func TestTraceCall(t *testing.T) { config: &TraceConfig{}, store: &debugEndpointMockStore{ getBlockByHashFn: func(hash types.Hash, full bool) (*types.Block, bool) { - assert.Equal(t, testHeader10.Hash, hash) - assert.False(t, full) + require.Equal(t, testHeader10.Hash, hash) + require.False(t, full) return nil, false }, @@ -792,15 +792,14 @@ func TestTraceCall(t *testing.T) { t.Parallel() endpoint := NewDebug(test.store, 100000) - res, err := endpoint.TraceCall(test.arg, test.filter, test.config) - assert.Equal(t, test.result, res) + require.Equal(t, test.result, res) if test.err { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } @@ -870,15 +869,14 @@ func TestGetRawBlock(t *testing.T) { t.Parallel() endpoint := NewDebug(test.store, 100000) - res, err := endpoint.GetRawBlock(test.filter) - assert.Equal(t, test.result, res) + require.Equal(t, test.result, res) if test.err { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } @@ -925,15 +923,14 @@ func TestGetRawHeader(t *testing.T) { t.Parallel() endpoint := NewDebug(test.store, 100000) - res, err := endpoint.GetRawHeader(test.filter) - assert.Equal(t, test.result, res) + require.Equal(t, test.result, res) if test.err { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } @@ -960,13 +957,13 @@ func TestGetRawTransaction(t *testing.T) { txHash: testTxHash1, store: &debugEndpointMockStore{ readTxLookupFn: func(hash types.Hash) (uint64, bool) { - assert.Equal(t, testTxHash1, hash) + require.Equal(t, testTxHash1, hash) return testBlock10.Number(), true }, getBlockByNumberFn: func(number uint64, full bool) (*types.Block, bool) { - assert.Equal(t, testBlock10.Number(), number) - assert.True(t, full) + require.Equal(t, testBlock10.Number(), number) + require.True(t, full) return blockWithTx, true }, @@ -978,12 +975,12 @@ func TestGetRawTransaction(t *testing.T) { txHash: testTxHash1, store: &debugEndpointMockStore{ readTxLookupFn: func(hash types.Hash) (uint64, bool) { - assert.Equal(t, testTxHash1, hash) + require.Equal(t, testTxHash1, hash) return 0, false }, getPendingTxFn: func(hash types.Hash) (*types.Transaction, bool) { - assert.Equal(t, testTxHash1, hash) + require.Equal(t, testTxHash1, hash) return nil, false }, @@ -995,18 +992,18 @@ func TestGetRawTransaction(t *testing.T) { txHash: testTxHash1, store: &debugEndpointMockStore{ readTxLookupFn: func(hash types.Hash) (uint64, bool) { - assert.Equal(t, testTxHash1, hash) + require.Equal(t, testTxHash1, hash) return testBlock10.Number(), true }, getBlockByNumberFn: func(number uint64, full bool) (*types.Block, bool) { - assert.Equal(t, testBlock10.Number(), number) - assert.True(t, full) + require.Equal(t, testBlock10.Number(), number) + require.True(t, full) return nil, false }, getPendingTxFn: func(hash types.Hash) (*types.Transaction, bool) { - assert.Equal(t, testTxHash1, hash) + require.Equal(t, testTxHash1, hash) return nil, false }, @@ -1018,18 +1015,18 @@ func TestGetRawTransaction(t *testing.T) { txHash: testTxHash1, store: &debugEndpointMockStore{ readTxLookupFn: func(hash types.Hash) (uint64, bool) { - assert.Equal(t, testTxHash1, hash) + require.Equal(t, testTxHash1, hash) return testBlock10.Number(), true }, getBlockByNumberFn: func(number uint64, full bool) (*types.Block, bool) { - assert.Equal(t, testBlock10.Number(), number) - assert.True(t, full) + require.Equal(t, testBlock10.Number(), number) + require.True(t, full) return testBlock10, true }, getPendingTxFn: func(hash types.Hash) (*types.Transaction, bool) { - assert.Equal(t, testTxHash1, hash) + require.Equal(t, testTxHash1, hash) return nil, false }, @@ -1041,12 +1038,12 @@ func TestGetRawTransaction(t *testing.T) { txHash: testTxHash1, store: &debugEndpointMockStore{ readTxLookupFn: func(hash types.Hash) (uint64, bool) { - assert.Equal(t, testTxHash1, hash) + require.Equal(t, testTxHash1, hash) return 0, false }, getPendingTxFn: func(hash types.Hash) (*types.Transaction, bool) { - assert.Equal(t, testTxHash1, hash) + require.Equal(t, testTxHash1, hash) return blockWithTx.Transactions[0], true }, @@ -1062,10 +1059,106 @@ func TestGetRawTransaction(t *testing.T) { t.Parallel() endpoint := NewDebug(test.store, 100000) - res, _ := endpoint.GetRawTransaction(test.txHash) - assert.Equal(t, test.result, res) + require.Equal(t, test.result, res) + }) + } +} + +func TestGetRawReceipts(t *testing.T) { + t.Parallel() + + const ( + cumulativeGasUsed = 28000 + gasUsed = 26000 + ) + + rec := createTestReceipt(nil, cumulativeGasUsed, gasUsed, testTx1.Hash()) + receipts := []*types.Receipt{rec} + + tests := []struct { + name string + filter BlockNumberOrHash + store *debugEndpointMockStore + result interface{} + returnErr string + err bool + }{ + { + name: "HeaderNotFound", + filter: EarliestBlockNumberOrHash, + store: &debugEndpointMockStore{ + getHeaderByNumberFn: func(num uint64) (*types.Header, bool) { + require.Equal(t, uint64(0), num) + + return nil, false + }, + }, + + returnErr: "failed to get the header of block", + result: nil, + err: true, + }, + { + name: "ReceiptsNotFound", + filter: LatestBlockNumberOrHash, + + store: &debugEndpointMockStore{ + headerFn: func() *types.Header { + return testLatestBlock.Header + }, + getBlockByHashFn: func(hash types.Hash, full bool) (*types.Block, bool) { + return testLatestBlock, true + }, + getReceiptsByHashFn: func(hash types.Hash) ([]*types.Receipt, error) { + return nil, fmt.Errorf("receipts not found") + }, + }, + + returnErr: "receipts not found", + result: nil, + err: true, + }, + { + name: "Success", + filter: LatestBlockNumberOrHash, + store: &debugEndpointMockStore{ + headerFn: func() *types.Header { + return testLatestBlock.Header + }, + + getBlockByHashFn: func(hash types.Hash, full bool) (*types.Block, bool) { + return testLatestBlock, true + }, + + getReceiptsByHashFn: func(hash types.Hash) ([]*types.Receipt, error) { + return receipts, nil + }, + }, + + returnErr: "", + result: [][]byte{rec.MarshalRLP()}, + err: false, + }, + } + + for _, test := range tests { + test := test + + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + endpoint := NewDebug(test.store, 100000) + res, err := endpoint.GetRawReceipts(test.filter) + + require.Equal(t, test.result, res) + + if test.err { + require.ErrorContains(t, err, test.returnErr) + } else { + require.NoError(t, err) + } }) } } @@ -1087,8 +1180,8 @@ func Test_newTracer(t *testing.T) { cancel() }) - assert.NotNil(t, tracer) - assert.NoError(t, err) + require.NotNil(t, tracer) + require.NoError(t, err) }) t.Run("should return error if arg is nil", func(t *testing.T) { @@ -1096,9 +1189,9 @@ func Test_newTracer(t *testing.T) { tracer, cancel, err := newTracer(nil) - assert.Nil(t, tracer) - assert.Nil(t, cancel) - assert.ErrorIs(t, ErrNoConfig, err) + require.Nil(t, tracer) + require.Nil(t, cancel) + require.ErrorIs(t, ErrNoConfig, err) }) t.Run("GetResult should return errExecutionTimeout if timeout happens", func(t *testing.T) { @@ -1117,14 +1210,14 @@ func Test_newTracer(t *testing.T) { cancel() }) - assert.NoError(t, err) + require.NoError(t, err) // wait until timeout time.Sleep(100 * time.Millisecond) res, err := tracer.GetResult() - assert.Nil(t, res) - assert.Equal(t, ErrExecutionTimeout, err) + require.Nil(t, res) + require.Equal(t, ErrExecutionTimeout, err) }) t.Run("GetResult should not return if cancel is called beforre timeout", func(t *testing.T) { @@ -1139,14 +1232,14 @@ func Test_newTracer(t *testing.T) { Timeout: &timeout, }) - assert.NoError(t, err) + require.NoError(t, err) cancel() res, err := tracer.GetResult() - assert.NotNil(t, res) - assert.NoError(t, err) + require.NotNil(t, res) + require.NoError(t, err) }) t.Run("should disable everything if struct logs are disabled", func(t *testing.T) { @@ -1164,12 +1257,12 @@ func Test_newTracer(t *testing.T) { cancel() }) - assert.NoError(t, err) + require.NoError(t, err) st, ok := tracer.(*structtracer.StructTracer) require.True(t, ok) - assert.Equal(t, structtracer.Config{ + require.Equal(t, structtracer.Config{ EnableMemory: false, EnableStack: false, EnableStorage: false,