diff --git a/turbo/jsonrpc/corner_cases_support_test.go b/turbo/jsonrpc/corner_cases_support_test.go index 63838a5b7cd..e3dc5828732 100644 --- a/turbo/jsonrpc/corner_cases_support_test.go +++ b/turbo/jsonrpc/corner_cases_support_test.go @@ -74,8 +74,4 @@ func TestNotFoundMustReturnNil(t *testing.T) { j, err := api.GetBlockTransactionCountByNumber(ctx, 10_000) require.Nil(j) require.Nil(err) - - k, err := api.GetBadBlocks(ctx) - require.Nil(k) - require.Nil(err) } diff --git a/turbo/jsonrpc/debug_api.go b/turbo/jsonrpc/debug_api.go index 960ed1fe25f..07c2379455a 100644 --- a/turbo/jsonrpc/debug_api.go +++ b/turbo/jsonrpc/debug_api.go @@ -29,8 +29,10 @@ import ( "github.com/erigontech/erigon-lib/kv" "github.com/erigontech/erigon-lib/kv/order" "github.com/erigontech/erigon-lib/kv/rawdbv3" + "github.com/erigontech/erigon-lib/log/v3" "github.com/erigontech/erigon-lib/rlp" "github.com/erigontech/erigon-lib/types/accounts" + "github.com/erigontech/erigon/core/rawdb" "github.com/erigontech/erigon/core/state" "github.com/erigontech/erigon/eth/stagedsync/stages" tracersConfig "github.com/erigontech/erigon/eth/tracers/config" @@ -62,6 +64,7 @@ type PrivateDebugAPI interface { GetRawHeader(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (hexutility.Bytes, error) GetRawBlock(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (hexutility.Bytes, error) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]hexutility.Bytes, error) + GetBadBlocks(ctx context.Context) ([]map[string]interface{}, error) } // PrivateDebugAPIImpl is implementation of the PrivateDebugAPI interface based on remote Db access @@ -446,3 +449,39 @@ func (api *PrivateDebugAPIImpl) GetRawReceipts(ctx context.Context, blockNrOrHas } return result, nil } + +func (api *PrivateDebugAPIImpl) GetBadBlocks(ctx context.Context) ([]map[string]interface{}, error) { + tx, err := api.db.BeginTemporalRo(ctx) + if err != nil { + return nil, err + } + defer tx.Rollback() + + blocks, err := rawdb.GetLatestBadBlocks(tx) + if err != nil || len(blocks) == 0 { + return nil, err + } + + results := make([]map[string]interface{}, 0, len(blocks)) + for _, block := range blocks { + var blockRlp string + if rlpBytes, err := rlp.EncodeToBytes(block); err != nil { + blockRlp = err.Error() // hack + } else { + blockRlp = fmt.Sprintf("%#x", rlpBytes) + } + + blockJson, err := ethapi.RPCMarshalBlock(block, true, true, nil) + if err != nil { + log.Error("Failed to marshal block", "err", err) + blockJson = map[string]interface{}{} + } + results = append(results, map[string]interface{}{ + "hash": block.Hash(), + "block": blockRlp, + "rlp": blockJson, + }) + } + + return results, nil +} diff --git a/turbo/jsonrpc/debug_api_test.go b/turbo/jsonrpc/debug_api_test.go index d6b55c27804..0f5f3cf753c 100644 --- a/turbo/jsonrpc/debug_api_test.go +++ b/turbo/jsonrpc/debug_api_test.go @@ -18,7 +18,9 @@ package jsonrpc import ( "bytes" + "context" "encoding/json" + "math/big" "reflect" "testing" @@ -26,6 +28,8 @@ import ( jsoniter "github.com/json-iterator/go" "github.com/stretchr/testify/require" + "github.com/erigontech/erigon-lib/common/u256" + "github.com/erigontech/erigon-lib/crypto" "github.com/erigontech/erigon-lib/log/v3" "github.com/erigontech/erigon-lib/common" @@ -35,8 +39,10 @@ import ( "github.com/erigontech/erigon-lib/kv/rawdbv3" "github.com/erigontech/erigon-lib/kv/stream" "github.com/erigontech/erigon/cmd/rpcdaemon/rpcdaemontest" + "github.com/erigontech/erigon/core/rawdb" "github.com/erigontech/erigon/core/types" tracersConfig "github.com/erigontech/erigon/eth/tracers/config" + "github.com/erigontech/erigon/params" "github.com/erigontech/erigon/rpc" "github.com/erigontech/erigon/rpc/rpccfg" "github.com/erigontech/erigon/turbo/adapter/ethapi" @@ -524,3 +530,68 @@ func TestAccountAt(t *testing.T) { require.Equal(0, int(results.Nonce)) }) } + +func TestGetBadBlocks(t *testing.T) { + m, _, _ := rpcdaemontest.CreateTestSentry(t) + api := NewPrivateDebugAPI(newBaseApiForTest(m), m.DB, 5000000) + ctx := context.Background() + + require := require.New(t) + var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + testAddr := crypto.PubkeyToAddress(testKey.PublicKey) + + mustSign := func(tx types.Transaction, s types.Signer) types.Transaction { + r, err := types.SignTx(tx, s, testKey) + require.NoError(err) + return r + } + + tx, err := m.DB.BeginRw(ctx) + if err != nil { + t.Errorf("could not begin read write transaction: %s", err) + } + + putBlock := func(number uint64) common.Hash { + // prepare db so it works with our test + signer1 := types.MakeSigner(params.MainnetChainConfig, number, number-1) + body := &types.Body{ + Transactions: []types.Transaction{ + mustSign(types.NewTransaction(number, testAddr, u256.Num1, 1, u256.Num1, nil), *signer1), + mustSign(types.NewTransaction(number+1, testAddr, u256.Num1, 2, u256.Num1, nil), *signer1), + }, + Uncles: []*types.Header{{Extra: []byte("test header")}}, + } + + header := &types.Header{Number: big.NewInt(int64(number))} + require.NoError(rawdb.WriteCanonicalHash(tx, header.Hash(), number)) + require.NoError(rawdb.WriteHeader(tx, header)) + require.NoError(rawdb.WriteBody(tx, header.Hash(), number, body)) + + return header.Hash() + } + + number := *rawdb.ReadCurrentBlockNumber(tx) + + // put some blocks + i := number + for i <= number+6 { + putBlock(i) + i++ + } + hash1 := putBlock(i) + hash2 := putBlock(i + 1) + hash3 := putBlock(i + 2) + hash4 := putBlock(i + 3) + require.NoError(rawdb.TruncateCanonicalHash(tx, i, true)) // trim since i + + tx.Commit() + + data, err := api.GetBadBlocks(ctx) + require.NoError(err) + + require.Len(data, 4) + require.Equal(data[0]["hash"], hash4) + require.Equal(data[1]["hash"], hash3) + require.Equal(data[2]["hash"], hash2) + require.Equal(data[3]["hash"], hash1) +} diff --git a/turbo/jsonrpc/eth_api.go b/turbo/jsonrpc/eth_api.go index 5b27ad313c1..6973307e9d2 100644 --- a/turbo/jsonrpc/eth_api.go +++ b/turbo/jsonrpc/eth_api.go @@ -61,7 +61,6 @@ type EthAPI interface { // Block related (proposed file: ./eth_blocks.go) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) GetBlockByHash(ctx context.Context, hash rpc.BlockNumberOrHash, fullTx bool) (map[string]interface{}, error) - GetBadBlocks(ctx context.Context) ([]map[string]interface{}, error) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*hexutil.Uint, error) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) (*hexutil.Uint, error) diff --git a/turbo/jsonrpc/eth_block.go b/turbo/jsonrpc/eth_block.go index 67fb105343d..bc5c22937ae 100644 --- a/turbo/jsonrpc/eth_block.go +++ b/turbo/jsonrpc/eth_block.go @@ -30,7 +30,6 @@ import ( "github.com/erigontech/erigon-lib/kv" "github.com/erigontech/erigon-lib/kv/rawdbv3" "github.com/erigontech/erigon-lib/log/v3" - "github.com/erigontech/erigon-lib/rlp" "github.com/erigontech/erigon/core" "github.com/erigontech/erigon/core/rawdb" "github.com/erigontech/erigon/core/state" @@ -362,42 +361,6 @@ func (api *APIImpl) GetBlockByHash(ctx context.Context, numberOrHash rpc.BlockNu return response, err } -func (api *APIImpl) GetBadBlocks(ctx context.Context) ([]map[string]interface{}, error) { - tx, err := api.db.BeginTemporalRo(ctx) - if err != nil { - return nil, err - } - defer tx.Rollback() - - blocks, err := rawdb.GetLatestBadBlocks(tx) - if err != nil || len(blocks) == 0 { - return nil, err - } - - results := make([]map[string]interface{}, 0, len(blocks)) - for _, block := range blocks { - var blockRlp string - if rlpBytes, err := rlp.EncodeToBytes(block); err != nil { - blockRlp = err.Error() // hack - } else { - blockRlp = fmt.Sprintf("%#x", rlpBytes) - } - - blockJson, err := ethapi.RPCMarshalBlock(block, true, true, nil) - if err != nil { - log.Error("Failed to marshal block", "err", err) - blockJson = map[string]interface{}{} - } - results = append(results, map[string]interface{}{ - "hash": block.Hash(), - "block": blockRlp, - "rlp": blockJson, - }) - } - - return results, nil -} - // GetBlockTransactionCountByNumber implements eth_getBlockTransactionCountByNumber. Returns the number of transactions in a block given the block's block number. func (api *APIImpl) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*hexutil.Uint, error) { tx, err := api.db.BeginTemporalRo(ctx) diff --git a/turbo/jsonrpc/eth_block_test.go b/turbo/jsonrpc/eth_block_test.go index 84d49b621c3..d9edc58ab4b 100644 --- a/turbo/jsonrpc/eth_block_test.go +++ b/turbo/jsonrpc/eth_block_test.go @@ -22,11 +22,8 @@ import ( "testing" "github.com/erigontech/erigon-lib/common/hexutil" - "github.com/erigontech/erigon-lib/common/u256" - "github.com/erigontech/erigon-lib/crypto" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/erigontech/erigon-lib/common" txpool "github.com/erigontech/erigon-lib/gointerfaces/txpoolproto" @@ -38,7 +35,6 @@ import ( "github.com/erigontech/erigon/cmd/rpcdaemon/rpcdaemontest" "github.com/erigontech/erigon/core/rawdb" "github.com/erigontech/erigon/core/types" - "github.com/erigontech/erigon/params" "github.com/erigontech/erigon/rpc" "github.com/erigontech/erigon/rpc/rpccfg" "github.com/erigontech/erigon/turbo/rpchelper" @@ -323,68 +319,3 @@ func TestGetBlockTransactionCountByNumber_ZeroTx(t *testing.T) { assert.Equal(t, expectedAmount, *txCount) } - -func TestGetBadBlocks(t *testing.T) { - m, _, _ := rpcdaemontest.CreateTestSentry(t) - api := NewEthAPI(newBaseApiForTest(m), m.DB, nil, nil, nil, 5000000, 1e18, 100_000, false, 100_000, 128, log.New()) - ctx := context.Background() - - require := require.New(t) - var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - testAddr := crypto.PubkeyToAddress(testKey.PublicKey) - - mustSign := func(tx types.Transaction, s types.Signer) types.Transaction { - r, err := types.SignTx(tx, s, testKey) - require.NoError(err) - return r - } - - tx, err := m.DB.BeginRw(ctx) - if err != nil { - t.Errorf("could not begin read write transaction: %s", err) - } - - putBlock := func(number uint64) common.Hash { - // prepare db so it works with our test - signer1 := types.MakeSigner(params.MainnetChainConfig, number, number-1) - body := &types.Body{ - Transactions: []types.Transaction{ - mustSign(types.NewTransaction(number, testAddr, u256.Num1, 1, u256.Num1, nil), *signer1), - mustSign(types.NewTransaction(number+1, testAddr, u256.Num1, 2, u256.Num1, nil), *signer1), - }, - Uncles: []*types.Header{{Extra: []byte("test header")}}, - } - - header := &types.Header{Number: big.NewInt(int64(number))} - require.NoError(rawdb.WriteCanonicalHash(tx, header.Hash(), number)) - require.NoError(rawdb.WriteHeader(tx, header)) - require.NoError(rawdb.WriteBody(tx, header.Hash(), number, body)) - - return header.Hash() - } - - number := *rawdb.ReadCurrentBlockNumber(tx) - - // put some blocks - i := number - for i <= number+6 { - putBlock(i) - i++ - } - hash1 := putBlock(i) - hash2 := putBlock(i + 1) - hash3 := putBlock(i + 2) - hash4 := putBlock(i + 3) - require.NoError(rawdb.TruncateCanonicalHash(tx, i, true)) // trim since i - - tx.Commit() - - data, err := api.GetBadBlocks(ctx) - require.NoError(err) - - require.Len(data, 4) - require.Equal(data[0]["hash"], hash4) - require.Equal(data[1]["hash"], hash3) - require.Equal(data[2]["hash"], hash2) - require.Equal(data[3]["hash"], hash1) -}