diff --git a/factory/api/apiResolverFactory.go b/factory/api/apiResolverFactory.go index f60e73c162a..170d05ac839 100644 --- a/factory/api/apiResolverFactory.go +++ b/factory/api/apiResolverFactory.go @@ -742,6 +742,7 @@ func createAPIBlockProcessorArgs(args *ApiResolverArgs, apiTransactionHandler ex EnableEpochsHandler: args.CoreComponents.EnableEpochsHandler(), ProofsPool: args.DataComponents.Datapool().Proofs(), BlockChain: args.DataComponents.Blockchain(), + EnableRoundsHandler: args.CoreComponents.EnableRoundsHandler(), } return blockApiArgs, nil diff --git a/factory/interface.go b/factory/interface.go index 31b457e14bc..b594b3842e4 100644 --- a/factory/interface.go +++ b/factory/interface.go @@ -537,7 +537,7 @@ type LogsFacade interface { type ReceiptsRepository interface { SaveReceipts(holder common.ReceiptsHolder, header data.HeaderHandler, headerHash []byte) error SaveReceiptsForExecResult(holder common.ReceiptsHolder, execResult data.BaseExecutionResultHandler) error - LoadReceipts(header data.HeaderHandler, headerHash []byte) (common.ReceiptsHolder, error) + LoadReceipts(receiptsHash []byte, header data.HeaderHandler, headerHash []byte) (common.ReceiptsHolder, error) IsInterfaceNil() bool } diff --git a/integrationTests/testProcessorNodeWithTestWebServer.go b/integrationTests/testProcessorNodeWithTestWebServer.go index a5beac0117d..e7624d7bba2 100644 --- a/integrationTests/testProcessorNodeWithTestWebServer.go +++ b/integrationTests/testProcessorNodeWithTestWebServer.go @@ -270,6 +270,7 @@ func createFacadeComponents(tpn *TestProcessorNode) nodeFacade.ApiResolver { EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, ProofsPool: tpn.ProofsPool, BlockChain: tpn.BlockChain, + EnableRoundsHandler: tpn.EnableRoundsHandler, } blockAPIHandler, err := blockAPI.CreateAPIBlockProcessor(argsBlockAPI) log.LogIfError(err) diff --git a/node/chainSimulator/chainSimulator_test.go b/node/chainSimulator/chainSimulator_test.go index c95bc8d2e22..45b656d9ca0 100644 --- a/node/chainSimulator/chainSimulator_test.go +++ b/node/chainSimulator/chainSimulator_test.go @@ -9,6 +9,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" apiBlock "github.com/multiversx/mx-chain-core-go/data/api" + "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -585,6 +586,96 @@ func TestSimulator_SendTransactions(t *testing.T) { chainSimulatorCommon.CheckGenerateTransactions(t, chainSimulator) } +func TestSimulator_MoveBalanceCheckReceipt(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + chainSimulator, err := NewChainSimulator(ArgsChainSimulator{ + BypassTxSignatureCheck: true, + BypassCreateBlockTimeCheck: true, + TempDir: t.TempDir(), + PathToInitialConfig: defaultPathToInitialConfig, + NumOfShards: defaultNumOfShards, + RoundDurationInMillis: defaultRoundDurationInMillis, + SupernovaRoundDurationInMillis: defaultSupernovaRoundDurationInMillis, + RoundsPerEpoch: defaultRoundsPerEpoch, + SupernovaRoundsPerEpoch: defaultSupernovaRoundsPerEpoch, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: defaultMinNodesPerShard, + MetaChainMinNodes: defaultMetaChainMinNodes, + AlterConfigsFunction: func(cfg *config.Configs) { + cfg.EpochConfig.EnableEpochs.StakingV2EnableEpoch = 0 + cfg.EpochConfig.EnableEpochs.SupernovaEnableEpoch = uint32(2) + cfg.RoundConfig.RoundActivations[string(common.SupernovaRoundFlag)] = config.ActivationRoundByName{ + Round: "46", + } + }, + }) + require.Nil(t, err) + require.NotNil(t, chainSimulator) + + defer chainSimulator.Close() + + wallet0, err := chainSimulator.GenerateAndMintWalletAddress(0, chainSimulatorCommon.OneEGLD) + require.Nil(t, err) + err = chainSimulator.GenerateBlocks(1) + require.Nil(t, err) + + ftx := &transaction.Transaction{ + Nonce: 0, + Value: big.NewInt(1), + SndAddr: wallet0.Bytes, + RcvAddr: wallet0.Bytes, + Data: []byte(""), + GasLimit: 100_000, + GasPrice: 1_000_000_000, + ChainID: []byte(configs.ChainID), + Version: 1, + Signature: []byte("010101"), + } + + checkReceipts := func(te *testing.T, aB *apiBlock.Block, value string) { + called := false + for _, mb := range aB.MiniBlocks { + if mb.Type == block.ReceiptBlock.String() { + called = true + require.Equal(te, 1, len(mb.Receipts)) + require.Equal(te, value, mb.Receipts[0].Value.String()) + } + } + require.True(te, called) + } + + apiTx, err := chainSimulator.SendTxAndGenerateBlockTilTxIsExecuted(ftx, 10) + require.Nil(t, err) + require.NotNil(t, apiTx) + + blockWithTxs, err := chainSimulator.GetNodeHandler(0).GetFacadeHandler().GetBlockByNonce(apiTx.BlockNonce, apiBlock.BlockQueryOptions{ + WithTransactions: true, + WithLogs: true, + }) + require.Nil(t, err) + require.Equal(t, 2, len(blockWithTxs.MiniBlocks)) + checkReceipts(t, blockWithTxs, "50000000000000") + + err = chainSimulator.GenerateBlocks(50) + require.Nil(t, err) + + ftx.Nonce++ + apiTx, err = chainSimulator.SendTxAndGenerateBlockTilTxIsExecuted(ftx, 10) + require.Nil(t, err) + require.NotNil(t, apiTx) + + blockWithTxs, err = chainSimulator.GetNodeHandler(0).GetFacadeHandler().GetBlockByNonce(apiTx.BlockNonce, apiBlock.BlockQueryOptions{ + WithTransactions: true, + WithLogs: true, + }) + require.Nil(t, err) + require.Equal(t, 2, len(blockWithTxs.MiniBlocks)) + checkReceipts(t, blockWithTxs, "500000000000") +} + func TestSimulator_SentMoveBalanceNoGasForFee(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") diff --git a/node/external/blockAPI/apiBlockFactory_test.go b/node/external/blockAPI/apiBlockFactory_test.go index 934297b18ba..f0bb2bdd91f 100644 --- a/node/external/blockAPI/apiBlockFactory_test.go +++ b/node/external/blockAPI/apiBlockFactory_test.go @@ -48,6 +48,7 @@ func createMockArgsAPIBlockProc() *ArgAPIBlockProcessor { EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, ProofsPool: &dataRetrieverTestCommon.ProofsPoolMock{}, BlockChain: chainHandler, + EnableRoundsHandler: &testscommon.EnableRoundsHandlerStub{}, } } diff --git a/node/external/blockAPI/baseBlock.go b/node/external/blockAPI/baseBlock.go index 106e7c476f8..5faccf460c5 100644 --- a/node/external/blockAPI/baseBlock.go +++ b/node/external/blockAPI/baseBlock.go @@ -2,7 +2,6 @@ package blockAPI import ( "encoding/hex" - "errors" "fmt" "math/big" "strings" @@ -63,12 +62,13 @@ type baseAPIBlockProcessor struct { enableEpochsHandler common.EnableEpochsHandler proofsPool dataRetriever.ProofsPool blockchain data.ChainHandler + enableRoundsHandler common.EnableRoundsHandler } var log = logger.GetOrCreate("node/blockAPI") -func (bap *baseAPIBlockProcessor) getIntrashardMiniblocksFromReceiptsStorage(header data.HeaderHandler, headerHash []byte, options api.BlockQueryOptions) ([]*api.MiniBlock, error) { - receiptsHolder, err := bap.receiptsRepository.LoadReceipts(header, headerHash) +func (bap *baseAPIBlockProcessor) getIntrashardMiniblocksFromReceiptsStorage(receiptsHash []byte, header data.HeaderHandler, headerHash []byte, options api.BlockQueryOptions) ([]*api.MiniBlock, error) { + receiptsHolder, err := bap.receiptsRepository.LoadReceipts(receiptsHash, header, headerHash) if err != nil { return nil, err } @@ -234,7 +234,7 @@ func (bap *baseAPIBlockProcessor) getAndAttachTxsToMbByEpoch( case block.InvalidBlock: apiMiniblock.Transactions, err = bap.getTxsFromMiniblock(miniBlock, miniblockHash, header, transaction.TxTypeInvalid, dataRetriever.TransactionUnit, firstProcessedTxIndex, lastProcessedTxIndex) case block.ReceiptBlock: - apiMiniblock.Receipts, err = bap.getReceiptsFromMiniblock(miniBlock, header.GetEpoch()) + apiMiniblock.Receipts, err = bap.getReceiptsFromMiniblock(miniBlock, header.GetEpoch(), header.GetRound()) } if err != nil { @@ -251,8 +251,16 @@ func (bap *baseAPIBlockProcessor) getAndAttachTxsToMbByEpoch( return nil } -func (bap *baseAPIBlockProcessor) getReceiptsFromMiniblock(miniblock *block.MiniBlock, epoch uint32) ([]*transaction.ApiReceipt, error) { - storer, err := bap.store.GetStorer(dataRetriever.UnsignedTransactionUnit) +func (bap *baseAPIBlockProcessor) getReceiptsStorerUnitType(round uint64) dataRetriever.UnitType { + if bap.enableRoundsHandler.IsFlagEnabledInRound(common.SupernovaRoundFlag, round) { + return dataRetriever.ReceiptsUnit + } + return dataRetriever.UnsignedTransactionUnit +} + +func (bap *baseAPIBlockProcessor) getReceiptsFromMiniblock(miniblock *block.MiniBlock, epoch uint32, round uint64) ([]*transaction.ApiReceipt, error) { + unit := bap.getReceiptsStorerUnitType(round) + storer, err := bap.store.GetStorer(unit) if err != nil { return nil, err } @@ -727,14 +735,8 @@ func proofToAPIProof(proof data.HeaderProofHandler) *api.HeaderProof { func (bap *baseAPIBlockProcessor) addMbsAndNumTxsAsyncExecution(apiBlock *api.Block, blockHeader data.HeaderHandler, headerHash []byte, options api.BlockQueryOptions) error { executionResultBytes, err := bap.getFromStorerWithEpoch(dataRetriever.ExecutionResultsUnit, headerHash, blockHeader.GetEpoch()) if err != nil { - // It's possible to have a block without an execution result (transactions from block are not executed yet) - if errors.Is(err, dblookupext.ErrNotFoundInStorage) { - mbs, totalTxs, errG := bap.getMbsAndTxsIfMissingExecutionResult(blockHeader, options) - apiBlock.MiniBlocks = mbs - apiBlock.NumTxs = totalTxs - return errG - } - return err + // do not return a partial block if the execution result is missing + return errBlockNotFound } executionResultHandler, err := process.UnmarshalExecutionResult(bap.marshalizer, executionResultBytes) @@ -757,7 +759,8 @@ func (bap *baseAPIBlockProcessor) addMbsAndNumTxsAsyncExecution(apiBlock *api.Bl mbsBeforeExecutionAndCleanup := removeExecutedTxsFromMbs(mbsBeforeExecution, executedTxsMap) allMbs := append(mbsBeforeExecutionAndCleanup, mbsAfterExecution...) - intraMb, err := bap.getIntrashardMiniblocksFromReceiptsStorage(blockHeader, headerHash, options) + receiptsHash := executionResultHandler.GetReceiptsHash() + intraMb, err := bap.getIntrashardMiniblocksFromReceiptsStorage(receiptsHash, blockHeader, headerHash, options) if err != nil { return err } diff --git a/node/external/blockAPI/baseBlock_test.go b/node/external/blockAPI/baseBlock_test.go index 9cb459a75ab..17da7812424 100644 --- a/node/external/blockAPI/baseBlock_test.go +++ b/node/external/blockAPI/baseBlock_test.go @@ -18,7 +18,6 @@ import ( "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/holders" "github.com/multiversx/mx-chain-go/dataRetriever" - dblookupext2 "github.com/multiversx/mx-chain-go/dblookupext" "github.com/multiversx/mx-chain-go/node/mock" "github.com/multiversx/mx-chain-go/storage" "github.com/multiversx/mx-chain-go/testscommon" @@ -47,6 +46,7 @@ func createBaseBlockProcessor() *baseAPIBlockProcessor { logsFacade: &testscommon.LogsFacadeStub{}, receiptsRepository: &testscommon.ReceiptsRepositoryStub{}, enableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, + enableRoundsHandler: &testscommon.EnableRoundsHandlerStub{}, } } @@ -73,7 +73,7 @@ func TestBaseBlockGetIntraMiniblocksSCRS(t *testing.T) { _ = storer.Put(scrHash, scResultBytes) baseAPIBlockProc.receiptsRepository = &testscommon.ReceiptsRepositoryStub{ - LoadReceiptsCalled: func(header data.HeaderHandler, headerHash []byte) (common.ReceiptsHolder, error) { + LoadReceiptsCalled: func(_ []byte, header data.HeaderHandler, headerHash []byte) (common.ReceiptsHolder, error) { return holders.NewReceiptsHolder([]*block.MiniBlock{miniblock}), nil }, } @@ -89,7 +89,7 @@ func TestBaseBlockGetIntraMiniblocksSCRS(t *testing.T) { } blockHeader := &block.Header{ReceiptsHash: []byte("aaaa"), Epoch: 0} - intraMbs, err := baseAPIBlockProc.getIntrashardMiniblocksFromReceiptsStorage(blockHeader, []byte{}, api.BlockQueryOptions{WithTransactions: true}) + intraMbs, err := baseAPIBlockProc.getIntrashardMiniblocksFromReceiptsStorage(blockHeader.GetReceiptsHash(), blockHeader, []byte{}, api.BlockQueryOptions{WithTransactions: true}) require.Nil(t, err) require.Equal(t, &api.MiniBlock{ Hash: "f4add7b23eb83cf290422b0f6b770e3007b8ed3cd9683797fc90c8b4881f27bd", @@ -134,7 +134,7 @@ func TestBaseBlockGetIntraMiniblocksReceipts(t *testing.T) { _ = storer.Put(receiptHash, receiptBytes) baseAPIBlockProc.receiptsRepository = &testscommon.ReceiptsRepositoryStub{ - LoadReceiptsCalled: func(header data.HeaderHandler, headerHash []byte) (common.ReceiptsHolder, error) { + LoadReceiptsCalled: func(_ []byte, header data.HeaderHandler, headerHash []byte) (common.ReceiptsHolder, error) { return holders.NewReceiptsHolder([]*block.MiniBlock{miniblock}), nil }, } @@ -154,7 +154,7 @@ func TestBaseBlockGetIntraMiniblocksReceipts(t *testing.T) { } blockHeader := &block.Header{ReceiptsHash: []byte("aaaa"), Epoch: 0} - intraMbs, err := baseAPIBlockProc.getIntrashardMiniblocksFromReceiptsStorage(blockHeader, []byte{}, api.BlockQueryOptions{WithTransactions: true}) + intraMbs, err := baseAPIBlockProc.getIntrashardMiniblocksFromReceiptsStorage(blockHeader.GetReceiptsHash(), blockHeader, []byte{}, api.BlockQueryOptions{WithTransactions: true}) require.Nil(t, err) require.Equal(t, &api.MiniBlock{ Hash: "596545f64319f2fcf8e0ebae06f40f3353d603f6070255588a48018c7b30c951", @@ -905,64 +905,19 @@ func TestBaseAPIBlockProcessor_AddMbsAndNumTxsAsyncExecutionBasedOnExecutionResu t.Parallel() baseAPIBlockProc := createBaseBlockProcessor() - baseAPIBlockProc.txStatusComputer = &mock.StatusComputerStub{ - ComputeStatusWhenInStorageKnowingMiniblockCalled: func(mbType block.Type, tx *transaction.ApiTransactionResult) (transaction.TxStatus, error) { - return transaction.TxStatusPending, nil - }, - } blockHeader := &block.Header{ Nonce: 100, Round: 1000, Epoch: 5, - MiniBlockHeaders: []block.MiniBlockHeader{ - { - Hash: []byte("mb_hash_1"), - SenderShardID: 0, - ReceiverShardID: 1, - TxCount: 2, - }, - }, } - // Create miniblock data - mb1 := &block.MiniBlock{ - TxHashes: [][]byte{ - []byte("tx_hash_1"), - []byte("tx_hash_2"), - }, - } - mbBytes, _ := baseAPIBlockProc.marshalizer.Marshal(mb1) - - tx1 := &transaction.Transaction{ - Nonce: 1, - } - tx1Bytes, _ := baseAPIBlockProc.marshalizer.Marshal(tx1) - baseAPIBlockProc.store = &storageMocks.ChainStorerStub{ GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { return &storageMocks.StorerStub{ GetFromEpochCalled: func(key []byte, epoch uint32) ([]byte, error) { - if string(key) == "header_hash" { - return nil, dblookupext2.ErrNotFoundInStorage - } - if string(key) == "mb_hash_1" { - return mbBytes, nil - } return nil, errors.New("not found") }, - GetBulkFromEpochCalled: func(keys [][]byte, epoch uint32) ([]data.KeyValuePair, error) { - return []data.KeyValuePair{ - { - Key: []byte("tx_hash_1"), - Value: tx1Bytes, - }, - { - Key: []byte("tx_hash_2"), - Value: tx1Bytes, - }, - }, nil - }, }, nil }, } @@ -988,15 +943,7 @@ func TestBaseAPIBlockProcessor_AddMbsAndNumTxsAsyncExecutionBasedOnExecutionResu []byte("header_hash"), api.BlockQueryOptions{WithTransactions: true}, ) - - require.NoError(t, err) - require.NotNil(t, apiBlock.MiniBlocks) - require.Equal(t, 1, len(apiBlock.MiniBlocks)) - require.Equal(t, 2, len(apiBlock.MiniBlocks[0].Transactions)) - // All transactions should have pending status when no execution result is found - for _, tx := range apiBlock.MiniBlocks[0].Transactions { - require.Equal(t, transaction.TxStatusPending, tx.Status) - } + require.Equal(t, errBlockNotFound, err) } func TestBaseAPIBlockProcessor_AddMbsAndNumTxsAsyncExecutionBasedOnExecutionResult_UnmarshalError(t *testing.T) { diff --git a/node/external/blockAPI/blockArgs.go b/node/external/blockAPI/blockArgs.go index 4646ed1f2f8..bf9f7a2f91d 100644 --- a/node/external/blockAPI/blockArgs.go +++ b/node/external/blockAPI/blockArgs.go @@ -32,6 +32,7 @@ type ArgAPIBlockProcessor struct { AccountsRepository state.AccountsRepository ScheduledTxsExecutionHandler process.ScheduledTxsExecutionHandler EnableEpochsHandler common.EnableEpochsHandler + EnableRoundsHandler common.EnableRoundsHandler ProofsPool dataRetriever.ProofsPool BlockChain data.ChainHandler } diff --git a/node/external/blockAPI/check.go b/node/external/blockAPI/check.go index c1e9e404a56..fdea291a129 100644 --- a/node/external/blockAPI/check.go +++ b/node/external/blockAPI/check.go @@ -69,6 +69,9 @@ func checkNilArg(arg *ArgAPIBlockProcessor) error { if check.IfNil(arg.BlockChain) { return process.ErrNilBlockChain } + if check.IfNil(arg.EnableRoundsHandler) { + return process.ErrNilEnableRoundsHandler + } return core.CheckHandlerCompatibility(arg.EnableEpochsHandler, []core.EnableEpochFlag{ common.RefactorPeersMiniBlocksFlag, diff --git a/node/external/blockAPI/interface.go b/node/external/blockAPI/interface.go index 68279d3df49..3a8495b6499 100644 --- a/node/external/blockAPI/interface.go +++ b/node/external/blockAPI/interface.go @@ -46,6 +46,6 @@ type logsFacade interface { } type receiptsRepository interface { - LoadReceipts(header data.HeaderHandler, headerHash []byte) (common.ReceiptsHolder, error) + LoadReceipts(receiptsHash []byte, header data.HeaderHandler, headerHash []byte) (common.ReceiptsHolder, error) IsInterfaceNil() bool } diff --git a/node/external/blockAPI/metaBlock.go b/node/external/blockAPI/metaBlock.go index 5f74e12f128..1a34dce633f 100644 --- a/node/external/blockAPI/metaBlock.go +++ b/node/external/blockAPI/metaBlock.go @@ -42,6 +42,7 @@ func newMetaApiBlockProcessor(arg *ArgAPIBlockProcessor, emptyReceiptsHash []byt enableEpochsHandler: arg.EnableEpochsHandler, proofsPool: arg.ProofsPool, blockchain: arg.BlockChain, + enableRoundsHandler: arg.EnableRoundsHandler, }, } } @@ -176,43 +177,6 @@ func (mbp *metaAPIBlockProcessor) convertMetaBlockBytesToAPIBlock(hash []byte, b return nil, err } - numOfTxs := uint32(0) - miniblocks := make([]*api.MiniBlock, 0) - for _, mb := range blockHeader.GetMiniBlockHeaderHandlers() { - if mb.GetTypeInt32() == int32(block.PeerBlock) { - continue - } - - numOfTxs += mb.GetTxCount() - - miniblockAPI := &api.MiniBlock{ - Hash: hex.EncodeToString(mb.GetHash()), - Type: block.ProcessingType(mb.GetProcessingType()).String(), - SourceShard: mb.GetSenderShardID(), - DestinationShard: mb.GetReceiverShardID(), - } - if options.WithTransactions { - miniBlockCopy := mb - err = mbp.getAndAttachTxsToMb(miniBlockCopy, blockHeader, miniblockAPI, options) - if err != nil { - return nil, err - } - } - - miniblocks = append(miniblocks, miniblockAPI) - } - - intraMb, err := mbp.getIntrashardMiniblocksFromReceiptsStorage(blockHeader, hash, options) - if err != nil { - return nil, err - } - - if len(intraMb) > 0 { - miniblocks = append(miniblocks, intraMb...) - } - - miniblocks = filterOutDuplicatedMiniblocks(miniblocks) - notarizedBlocks := make([]*api.NotarizedBlock, 0, len(blockHeader.GetShardInfoHandlers())) for _, shardData := range blockHeader.GetShardInfoHandlers() { notarizedBlock := &api.NotarizedBlock{ @@ -237,9 +201,7 @@ func (mbp *metaAPIBlockProcessor) convertMetaBlockBytesToAPIBlock(hash []byte, b Shard: core.MetachainShardId, Hash: hex.EncodeToString(hash), PrevBlockHash: hex.EncodeToString(blockHeader.GetPrevHash()), - NumTxs: numOfTxs, NotarizedBlocks: notarizedBlocks, - MiniBlocks: miniblocks, AccumulatedFees: blockHeader.GetAccumulatedFees().String(), DeveloperFees: blockHeader.GetDeveloperFees().String(), AccumulatedFeesInEpoch: blockHeader.GetAccumulatedFeesInEpoch().String(), @@ -320,7 +282,7 @@ func (mbp *metaAPIBlockProcessor) addMbsAndNumTxsV1(apiBlock *api.Block, blockHe return err } - intraMb, err := mbp.getIntrashardMiniblocksFromReceiptsStorage(blockHeader, headerHash, options) + intraMb, err := mbp.getIntrashardMiniblocksFromReceiptsStorage(blockHeader.GetReceiptsHash(), blockHeader, headerHash, options) if err != nil { return err } diff --git a/node/external/blockAPI/shardBlock.go b/node/external/blockAPI/shardBlock.go index c8cf8d6d5f9..43f1d15b95f 100644 --- a/node/external/blockAPI/shardBlock.go +++ b/node/external/blockAPI/shardBlock.go @@ -42,6 +42,7 @@ func newShardApiBlockProcessor(arg *ArgAPIBlockProcessor, emptyReceiptsHash []by enableEpochsHandler: arg.EnableEpochsHandler, proofsPool: arg.ProofsPool, blockchain: arg.BlockChain, + enableRoundsHandler: arg.EnableRoundsHandler, }, } } @@ -178,45 +179,6 @@ func (sbp *shardAPIBlockProcessor) convertShardBlockBytesToAPIBlock(hash []byte, return nil, err } - numOfTxs := uint32(0) - miniblocks := make([]*api.MiniBlock, 0) - - for _, mb := range blockHeader.GetMiniBlockHeaderHandlers() { - if block.Type(mb.GetTypeInt32()) == block.PeerBlock { - continue - } - - numOfTxs += mb.GetTxCount() - - miniblockAPI := &api.MiniBlock{ - Hash: hex.EncodeToString(mb.GetHash()), - Type: block.Type(mb.GetTypeInt32()).String(), - SourceShard: mb.GetSenderShardID(), - DestinationShard: mb.GetReceiverShardID(), - ProcessingType: block.ProcessingType(mb.GetProcessingType()).String(), - ConstructionState: block.MiniBlockState(mb.GetConstructionState()).String(), - IndexOfFirstTxProcessed: mb.GetIndexOfFirstTxProcessed(), - IndexOfLastTxProcessed: mb.GetIndexOfLastTxProcessed(), - } - if options.WithTransactions { - miniBlockCopy := mb - err = sbp.getAndAttachTxsToMb(miniBlockCopy, blockHeader, miniblockAPI, options) - if err != nil { - return nil, err - } - } - - miniblocks = append(miniblocks, miniblockAPI) - } - - intraMb, err := sbp.getIntrashardMiniblocksFromReceiptsStorage(blockHeader, hash, options) - if err != nil { - return nil, err - } - - miniblocks = append(miniblocks, intraMb...) - miniblocks = filterOutDuplicatedMiniblocks(miniblocks) - timestampSec, timestampMs, err := common.GetHeaderTimestamps(blockHeader, sbp.enableEpochsHandler) if err != nil { return nil, err @@ -229,8 +191,6 @@ func (sbp *shardAPIBlockProcessor) convertShardBlockBytesToAPIBlock(hash []byte, Shard: blockHeader.GetShardID(), Hash: hex.EncodeToString(hash), PrevBlockHash: hex.EncodeToString(blockHeader.GetPrevHash()), - NumTxs: numOfTxs, - MiniBlocks: miniblocks, AccumulatedFees: blockHeader.GetAccumulatedFees().String(), DeveloperFees: blockHeader.GetDeveloperFees().String(), Timestamp: int64(timestampSec), @@ -321,7 +281,7 @@ func (sbp *shardAPIBlockProcessor) addMbsAndNumTxsV1(apiBlock *api.Block, blockH miniblocks = append(miniblocks, miniblockAPI) } - intraMb, err := sbp.getIntrashardMiniblocksFromReceiptsStorage(blockHeader, headerHash, options) + intraMb, err := sbp.getIntrashardMiniblocksFromReceiptsStorage(blockHeader.GetReceiptsHash(), blockHeader, headerHash, options) if err != nil { return err } diff --git a/process/receipts/receiptsRepository.go b/process/receipts/receiptsRepository.go index 3f0fdcdd3b7..822adf53214 100644 --- a/process/receipts/receiptsRepository.go +++ b/process/receipts/receiptsRepository.go @@ -135,8 +135,8 @@ func getReceiptHashFromBaseExecutionResult(execResult data.BaseExecutionResultHa } // LoadReceipts loads the receipts, given a block header -func (repository *receiptsRepository) LoadReceipts(header data.HeaderHandler, headerHash []byte) (common.ReceiptsHolder, error) { - storageKey := repository.decideStorageKey(header.GetReceiptsHash(), headerHash) +func (repository *receiptsRepository) LoadReceipts(receiptsHash []byte, header data.HeaderHandler, headerHash []byte) (common.ReceiptsHolder, error) { + storageKey := repository.decideStorageKey(receiptsHash, headerHash) batchBytes, err := repository.storer.GetFromEpoch(storageKey, header.GetEpoch()) if err != nil { diff --git a/process/receipts/receiptsRepository_test.go b/process/receipts/receiptsRepository_test.go index 4179540d93d..bb949f648d6 100644 --- a/process/receipts/receiptsRepository_test.go +++ b/process/receipts/receiptsRepository_test.go @@ -213,19 +213,19 @@ func TestReceiptsRepository_LoadReceipts(t *testing.T) { _ = store.Put(dataRetriever.ReceiptsUnit, nonEmptyReceiptsHash, receiptsAtKeyReceiptsHashBytes) t.Run("when header.GetReceiptsHash() == emptyReceiptsHash", func(t *testing.T) { - loaded, err := repository.LoadReceipts(&block.Header{ReceiptsHash: emptyReceiptsHash}, headerHash) + loaded, err := repository.LoadReceipts(emptyReceiptsHash, &block.Header{ReceiptsHash: emptyReceiptsHash}, headerHash) require.Nil(t, err) require.Equal(t, receiptsAtKeyHeaderHash, loaded) }) t.Run("when header.GetReceiptsHash() != emptyReceiptsHash", func(t *testing.T) { - loaded, err := repository.LoadReceipts(&block.Header{ReceiptsHash: nonEmptyReceiptsHash}, headerHash) + loaded, err := repository.LoadReceipts(nonEmptyReceiptsHash, &block.Header{ReceiptsHash: nonEmptyReceiptsHash}, headerHash) require.Nil(t, err) require.Equal(t, receiptsAtKeyReceiptsHash, loaded) }) t.Run("when no receipts for given header", func(t *testing.T) { - loadedHolder, err := repository.LoadReceipts(&block.Header{ReceiptsHash: emptyReceiptsHash}, []byte("abba")) + loadedHolder, err := repository.LoadReceipts(emptyReceiptsHash, &block.Header{ReceiptsHash: emptyReceiptsHash}, []byte("abba")) require.Nil(t, err) require.Equal(t, createEmptyReceiptsHolder(), loadedHolder) }) @@ -261,7 +261,7 @@ func TestReceiptsRepository_NoPanicOnSaveOrLoadWhenBadStorage(t *testing.T) { t.Run("load from bad storage", func(t *testing.T) { header := &block.Header{ReceiptsHash: []byte("aaaa")} - loaded, err := repository.LoadReceipts(header, []byte("bbbb")) + loaded, err := repository.LoadReceipts(header.ReceiptsHash, header, []byte("bbbb")) require.NotNil(t, err) require.ErrorIs(t, err, errCannotLoadReceipts) require.Nil(t, loaded) diff --git a/storage/pruning/fullHistoryPruningStorer.go b/storage/pruning/fullHistoryPruningStorer.go index 90671f89dae..9ad4d3634a8 100644 --- a/storage/pruning/fullHistoryPruningStorer.go +++ b/storage/pruning/fullHistoryPruningStorer.go @@ -81,6 +81,14 @@ func (fhps *FullHistoryPruningStorer) GetFromEpoch(key []byte, epoch uint32) ([] // GetBulkFromEpoch will search a bulk of keys in the persister for the given epoch // doesn't return an error if a key or any isn't found func (fhps *FullHistoryPruningStorer) GetBulkFromEpoch(keys [][]byte, epoch uint32) ([]data.KeyValuePair, error) { + res, err := fhps.searchBulkInEpoch(keys, epoch) + if err == nil && len(res) > 0 { + return res, nil + } + return fhps.searchBulkInEpoch(keys, epoch+1) +} + +func (fhps *FullHistoryPruningStorer) searchBulkInEpoch(keys [][]byte, epoch uint32) ([]data.KeyValuePair, error) { persister, err := fhps.getOrOpenPersister(epoch) if err != nil { return nil, err diff --git a/storage/pruning/fullHistoryPruningStorer_test.go b/storage/pruning/fullHistoryPruningStorer_test.go index d1274499bb9..1102297b056 100644 --- a/storage/pruning/fullHistoryPruningStorer_test.go +++ b/storage/pruning/fullHistoryPruningStorer_test.go @@ -204,6 +204,36 @@ func TestNewFullHistoryPruningStorer_GetBulkFromEpoch(t *testing.T) { assert.Equal(t, expected, res) } +func TestNewFullHistoryPruningStorer_GetBulkFromMultipleEpochs(t *testing.T) { + t.Parallel() + + args := getDefaultArgs() + fhArgs := pruning.FullHistoryStorerArgs{ + StorerArgs: args, + NumOfOldActivePersisters: 5, + } + fhps, _ := pruning.NewFullHistoryPruningStorer(fhArgs) + testVal0, testVal1 := []byte("value0"), []byte("value1") + testKey0, testKey1 := []byte("key0"), []byte("key1") + testEpoch := uint32(7) + + _ = fhps.PutInEpoch(testKey0, testVal0, testEpoch+1) + _ = fhps.PutInEpoch(testKey1, testVal1, testEpoch+1) + + // clean cache + fhps.ClearCache() + + res, err := fhps.GetBulkFromEpoch([][]byte{testKey0, testKey1}, testEpoch) + assert.Nil(t, err) + + expected := []data.KeyValuePair{ + {Key: testKey0, Value: testVal0}, + {Key: testKey1, Value: testVal1}, + } + assert.Equal(t, expected, res) + +} + func TestNewFullHistoryPruningStorer_GetBulkFromEpochShouldNotLoadFromCache(t *testing.T) { t.Parallel() diff --git a/testscommon/receiptsRepositoryStub.go b/testscommon/receiptsRepositoryStub.go index 2c6b8daecd4..094e2165c26 100644 --- a/testscommon/receiptsRepositoryStub.go +++ b/testscommon/receiptsRepositoryStub.go @@ -11,7 +11,7 @@ import ( type ReceiptsRepositoryStub struct { SaveReceiptsCalled func(holder common.ReceiptsHolder, header data.HeaderHandler, headerHash []byte) error SaveReceiptsForExecResultCalled func(holder common.ReceiptsHolder, execResult data.BaseExecutionResultHandler) error - LoadReceiptsCalled func(header data.HeaderHandler, headerHash []byte) (common.ReceiptsHolder, error) + LoadReceiptsCalled func(receiptsHash []byte, header data.HeaderHandler, headerHash []byte) (common.ReceiptsHolder, error) } // SaveReceipts - @@ -33,9 +33,9 @@ func (stub *ReceiptsRepositoryStub) SaveReceiptsForExecResult(holder common.Rece } // LoadReceipts - -func (stub *ReceiptsRepositoryStub) LoadReceipts(header data.HeaderHandler, headerHash []byte) (common.ReceiptsHolder, error) { +func (stub *ReceiptsRepositoryStub) LoadReceipts(receiptsHash []byte, header data.HeaderHandler, headerHash []byte) (common.ReceiptsHolder, error) { if stub.LoadReceiptsCalled != nil { - return stub.LoadReceiptsCalled(header, headerHash) + return stub.LoadReceiptsCalled(receiptsHash, header, headerHash) } return holders.NewReceiptsHolder(nil), nil