From 601eece49cda42d8007f51c0ecce614765a5213a Mon Sep 17 00:00:00 2001 From: miiu Date: Fri, 20 Mar 2026 09:55:46 +0200 Subject: [PATCH 01/13] fixes api block --- factory/api/apiResolverFactory.go | 1 + factory/interface.go | 2 +- node/chainSimulator/chainSimulator_test.go | 76 +++++++++++++++++++ .../external/blockAPI/apiBlockFactory_test.go | 1 + node/external/blockAPI/baseBlock.go | 22 ++++-- node/external/blockAPI/baseBlock_test.go | 9 ++- node/external/blockAPI/blockArgs.go | 1 + node/external/blockAPI/check.go | 3 + node/external/blockAPI/interface.go | 2 +- node/external/blockAPI/metaBlock.go | 42 +--------- node/external/blockAPI/shardBlock.go | 44 +---------- process/receipts/receiptsRepository.go | 4 +- process/receipts/receiptsRepository_test.go | 8 +- testscommon/receiptsRepositoryStub.go | 6 +- 14 files changed, 118 insertions(+), 103 deletions(-) diff --git a/factory/api/apiResolverFactory.go b/factory/api/apiResolverFactory.go index f60e73c162a..127ada05cd4 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(), + EnableRoundHandler: 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/node/chainSimulator/chainSimulator_test.go b/node/chainSimulator/chainSimulator_test.go index c95bc8d2e22..d83ff5e0161 100644 --- a/node/chainSimulator/chainSimulator_test.go +++ b/node/chainSimulator/chainSimulator_test.go @@ -585,6 +585,82 @@ func TestSimulator_SendTransactions(t *testing.T) { chainSimulatorCommon.CheckGenerateTransactions(t, chainSimulator) } +func TestSimilator_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"), + } + + 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)) + + 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)) +} + 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..69ed41e5744 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, + EnableRoundHandler: &testscommon.EnableRoundsHandlerStub{}, } } diff --git a/node/external/blockAPI/baseBlock.go b/node/external/blockAPI/baseBlock.go index 106e7c476f8..60688ec64a0 100644 --- a/node/external/blockAPI/baseBlock.go +++ b/node/external/blockAPI/baseBlock.go @@ -63,12 +63,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 +235,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 +252,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 } @@ -757,7 +766,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..997f675b715 100644 --- a/node/external/blockAPI/baseBlock_test.go +++ b/node/external/blockAPI/baseBlock_test.go @@ -47,6 +47,7 @@ func createBaseBlockProcessor() *baseAPIBlockProcessor { logsFacade: &testscommon.LogsFacadeStub{}, receiptsRepository: &testscommon.ReceiptsRepositoryStub{}, enableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, + enableRoundsHandler: &testscommon.EnableRoundsHandlerStub{}, } } @@ -73,7 +74,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 +90,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 +135,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 +155,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", diff --git a/node/external/blockAPI/blockArgs.go b/node/external/blockAPI/blockArgs.go index 4646ed1f2f8..dca0035bddf 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 + EnableRoundHandler common.EnableRoundsHandler ProofsPool dataRetriever.ProofsPool BlockChain data.ChainHandler } diff --git a/node/external/blockAPI/check.go b/node/external/blockAPI/check.go index c1e9e404a56..1ba8ca6b8a4 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.EnableRoundHandler) { + 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..c09d083444d 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.EnableRoundHandler, }, } } @@ -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..d3f059302ed 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.EnableRoundHandler, }, } } @@ -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/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 From 71ff4368b0dae64d2a47a2c5a1af13c402cbfcf9 Mon Sep 17 00:00:00 2001 From: miiu Date: Fri, 20 Mar 2026 10:16:53 +0200 Subject: [PATCH 02/13] extra checks --- node/chainSimulator/chainSimulator_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/node/chainSimulator/chainSimulator_test.go b/node/chainSimulator/chainSimulator_test.go index d83ff5e0161..79ed6d002e4 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" @@ -634,6 +635,18 @@ func TestSimilator_MoveBalanceCheckReceipt(t *testing.T) { 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(t, called) + } + apiTx, err := chainSimulator.SendTxAndGenerateBlockTilTxIsExecuted(ftx, 10) require.Nil(t, err) require.NotNil(t, apiTx) @@ -644,6 +657,7 @@ func TestSimilator_MoveBalanceCheckReceipt(t *testing.T) { }) require.Nil(t, err) require.Equal(t, 2, len(blockWithTxs.MiniBlocks)) + checkReceipts(t, blockWithTxs, "50000000000000") err = chainSimulator.GenerateBlocks(50) require.Nil(t, err) @@ -659,6 +673,7 @@ func TestSimilator_MoveBalanceCheckReceipt(t *testing.T) { }) require.Nil(t, err) require.Equal(t, 2, len(blockWithTxs.MiniBlocks)) + checkReceipts(t, blockWithTxs, "500000000000") } func TestSimulator_SentMoveBalanceNoGasForFee(t *testing.T) { From 415cacd1239355d1efbbb4f52964c03ee9fd3d8a Mon Sep 17 00:00:00 2001 From: miiu Date: Tue, 24 Mar 2026 10:43:32 +0200 Subject: [PATCH 03/13] extra logs for testing --- node/external/blockAPI/baseBlock.go | 5 +++++ node/external/transactionAPI/apiTransactionResults.go | 1 + 2 files changed, 6 insertions(+) diff --git a/node/external/blockAPI/baseBlock.go b/node/external/blockAPI/baseBlock.go index 60688ec64a0..93599c6988a 100644 --- a/node/external/blockAPI/baseBlock.go +++ b/node/external/blockAPI/baseBlock.go @@ -104,6 +104,11 @@ func (bap *baseAPIBlockProcessor) convertMiniblockFromReceiptsStorageToApiMinibl } if options.WithTransactions { + log.Warn("number of transactions in receipts", "num", len(miniblock.TxHashes)) + for _, txHash := range miniblock.TxHashes { + log.Warn("transaction hash", "txhash", txHash) + } + firstProcessed := int32(0) lastProcessed := int32(len(miniblock.TxHashes) - 1) diff --git a/node/external/transactionAPI/apiTransactionResults.go b/node/external/transactionAPI/apiTransactionResults.go index cee8205b3c4..d94feb2e66a 100644 --- a/node/external/transactionAPI/apiTransactionResults.go +++ b/node/external/transactionAPI/apiTransactionResults.go @@ -72,6 +72,7 @@ func (arp *apiTransactionResultsProcessor) putResultsInTransaction(hash []byte, } if len(resultsHashes.ReceiptsHash) > 0 { + log.Warn("apiTransactionResultsProcessor.putResultsInTransaction", "receiptHash", resultsHashes.ReceiptsHash) return arp.putReceiptInTransaction(tx, resultsHashes.ReceiptsHash, epoch, round) } From 051f3dfc51f82797370e1feadab624f97b7b8c70 Mon Sep 17 00:00:00 2001 From: miiu Date: Tue, 24 Mar 2026 11:26:06 +0200 Subject: [PATCH 04/13] extra logs for testing --- node/external/blockAPI/baseBlock.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/node/external/blockAPI/baseBlock.go b/node/external/blockAPI/baseBlock.go index 93599c6988a..4a638812083 100644 --- a/node/external/blockAPI/baseBlock.go +++ b/node/external/blockAPI/baseBlock.go @@ -270,6 +270,7 @@ func (bap *baseAPIBlockProcessor) getReceiptsFromMiniblock(miniblock *block.Mini if err != nil { return nil, err } + log.Warn("getReceiptsFromMiniblock", "epoch", epoch, "unit", unit) start := time.Now() marshalledReceipts, err := storer.GetBulkFromEpoch(miniblock.TxHashes, epoch) @@ -278,6 +279,19 @@ func (bap *baseAPIBlockProcessor) getReceiptsFromMiniblock(miniblock *block.Mini } logging.LogAPIActionDurationIfNeeded(start, "GetBulkFromEpoch") + log.Warn("num marshalledReceipts", "len", len(marshalledReceipts)) + for _, receiptHash := range miniblock.TxHashes { + res, errG := storer.GetFromEpoch(receiptHash, epoch) + if errG != nil { + log.Warn("storer.GetFromEpoch", "err", errG) + } + receipt, errUnmarshal := bap.apiTransactionHandler.UnmarshalReceipt(res) + if errUnmarshal != nil { + log.Warn("storer.UnmarshalReceipt", "err", errUnmarshal) + } + fmt.Println(receipt.Value.String(), receipt.TxHash) + } + apiReceipts := make([]*transaction.ApiReceipt, 0) for _, pair := range marshalledReceipts { receipt, errUnmarshal := bap.apiTransactionHandler.UnmarshalReceipt(pair.Value) From 561852778fdb32ed734522758cd1501e4ce41844 Mon Sep 17 00:00:00 2001 From: miiu Date: Tue, 24 Mar 2026 11:41:53 +0200 Subject: [PATCH 05/13] fix fix --- node/external/blockAPI/baseBlock.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/node/external/blockAPI/baseBlock.go b/node/external/blockAPI/baseBlock.go index 4a638812083..8bd9704397f 100644 --- a/node/external/blockAPI/baseBlock.go +++ b/node/external/blockAPI/baseBlock.go @@ -279,6 +279,7 @@ func (bap *baseAPIBlockProcessor) getReceiptsFromMiniblock(miniblock *block.Mini } logging.LogAPIActionDurationIfNeeded(start, "GetBulkFromEpoch") + apiReceipts := make([]*transaction.ApiReceipt, 0) log.Warn("num marshalledReceipts", "len", len(marshalledReceipts)) for _, receiptHash := range miniblock.TxHashes { res, errG := storer.GetFromEpoch(receiptHash, epoch) @@ -289,10 +290,10 @@ func (bap *baseAPIBlockProcessor) getReceiptsFromMiniblock(miniblock *block.Mini if errUnmarshal != nil { log.Warn("storer.UnmarshalReceipt", "err", errUnmarshal) } - fmt.Println(receipt.Value.String(), receipt.TxHash) + apiReceipts = append(apiReceipts, receipt) + log.Warn("add receipt", "receipt", receipt.TxHash) } - apiReceipts := make([]*transaction.ApiReceipt, 0) for _, pair := range marshalledReceipts { receipt, errUnmarshal := bap.apiTransactionHandler.UnmarshalReceipt(pair.Value) if errUnmarshal != nil { From b3b2757c3807b1574d90d40af1a826967f008b95 Mon Sep 17 00:00:00 2001 From: miiu Date: Tue, 24 Mar 2026 12:08:59 +0200 Subject: [PATCH 06/13] one more check --- node/external/blockAPI/baseBlock.go | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/node/external/blockAPI/baseBlock.go b/node/external/blockAPI/baseBlock.go index 8bd9704397f..05fe248e33d 100644 --- a/node/external/blockAPI/baseBlock.go +++ b/node/external/blockAPI/baseBlock.go @@ -281,19 +281,26 @@ func (bap *baseAPIBlockProcessor) getReceiptsFromMiniblock(miniblock *block.Mini apiReceipts := make([]*transaction.ApiReceipt, 0) log.Warn("num marshalledReceipts", "len", len(marshalledReceipts)) - for _, receiptHash := range miniblock.TxHashes { - res, errG := storer.GetFromEpoch(receiptHash, epoch) - if errG != nil { - log.Warn("storer.GetFromEpoch", "err", errG) - } - receipt, errUnmarshal := bap.apiTransactionHandler.UnmarshalReceipt(res) - if errUnmarshal != nil { - log.Warn("storer.UnmarshalReceipt", "err", errUnmarshal) + if len(marshalledReceipts) == 0 { + marshalledReceipts, err = storer.GetBulkFromEpoch(miniblock.TxHashes, epoch+1) + if err != nil { + return nil, fmt.Errorf("%w: %v", errCannotLoadReceipts, err) } - apiReceipts = append(apiReceipts, receipt) - log.Warn("add receipt", "receipt", receipt.TxHash) } + //for _, receiptHash := range miniblock.TxHashes { + // res, errG := storer.GetFromEpoch(receiptHash, epoch) + // if errG != nil { + // log.Warn("storer.GetFromEpoch", "err", errG) + // } + // receipt, errUnmarshal := bap.apiTransactionHandler.UnmarshalReceipt(res) + // if errUnmarshal != nil { + // log.Warn("storer.UnmarshalReceipt", "err", errUnmarshal) + // } + // apiReceipts = append(apiReceipts, receipt) + // log.Warn("add receipt", "receipt", receipt.TxHash) + //} + for _, pair := range marshalledReceipts { receipt, errUnmarshal := bap.apiTransactionHandler.UnmarshalReceipt(pair.Value) if errUnmarshal != nil { From b542cf9b260e01831db0151c1f594012fd476e94 Mon Sep 17 00:00:00 2001 From: miiu Date: Tue, 24 Mar 2026 13:47:15 +0200 Subject: [PATCH 07/13] proper fix --- node/external/blockAPI/baseBlock.go | 27 ------------------- .../transactionAPI/apiTransactionResults.go | 1 - storage/pruning/fullHistoryPruningStorer.go | 8 ++++++ 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/node/external/blockAPI/baseBlock.go b/node/external/blockAPI/baseBlock.go index 05fe248e33d..60688ec64a0 100644 --- a/node/external/blockAPI/baseBlock.go +++ b/node/external/blockAPI/baseBlock.go @@ -104,11 +104,6 @@ func (bap *baseAPIBlockProcessor) convertMiniblockFromReceiptsStorageToApiMinibl } if options.WithTransactions { - log.Warn("number of transactions in receipts", "num", len(miniblock.TxHashes)) - for _, txHash := range miniblock.TxHashes { - log.Warn("transaction hash", "txhash", txHash) - } - firstProcessed := int32(0) lastProcessed := int32(len(miniblock.TxHashes) - 1) @@ -270,7 +265,6 @@ func (bap *baseAPIBlockProcessor) getReceiptsFromMiniblock(miniblock *block.Mini if err != nil { return nil, err } - log.Warn("getReceiptsFromMiniblock", "epoch", epoch, "unit", unit) start := time.Now() marshalledReceipts, err := storer.GetBulkFromEpoch(miniblock.TxHashes, epoch) @@ -280,27 +274,6 @@ func (bap *baseAPIBlockProcessor) getReceiptsFromMiniblock(miniblock *block.Mini logging.LogAPIActionDurationIfNeeded(start, "GetBulkFromEpoch") apiReceipts := make([]*transaction.ApiReceipt, 0) - log.Warn("num marshalledReceipts", "len", len(marshalledReceipts)) - if len(marshalledReceipts) == 0 { - marshalledReceipts, err = storer.GetBulkFromEpoch(miniblock.TxHashes, epoch+1) - if err != nil { - return nil, fmt.Errorf("%w: %v", errCannotLoadReceipts, err) - } - } - - //for _, receiptHash := range miniblock.TxHashes { - // res, errG := storer.GetFromEpoch(receiptHash, epoch) - // if errG != nil { - // log.Warn("storer.GetFromEpoch", "err", errG) - // } - // receipt, errUnmarshal := bap.apiTransactionHandler.UnmarshalReceipt(res) - // if errUnmarshal != nil { - // log.Warn("storer.UnmarshalReceipt", "err", errUnmarshal) - // } - // apiReceipts = append(apiReceipts, receipt) - // log.Warn("add receipt", "receipt", receipt.TxHash) - //} - for _, pair := range marshalledReceipts { receipt, errUnmarshal := bap.apiTransactionHandler.UnmarshalReceipt(pair.Value) if errUnmarshal != nil { diff --git a/node/external/transactionAPI/apiTransactionResults.go b/node/external/transactionAPI/apiTransactionResults.go index d94feb2e66a..cee8205b3c4 100644 --- a/node/external/transactionAPI/apiTransactionResults.go +++ b/node/external/transactionAPI/apiTransactionResults.go @@ -72,7 +72,6 @@ func (arp *apiTransactionResultsProcessor) putResultsInTransaction(hash []byte, } if len(resultsHashes.ReceiptsHash) > 0 { - log.Warn("apiTransactionResultsProcessor.putResultsInTransaction", "receiptHash", resultsHashes.ReceiptsHash) return arp.putReceiptInTransaction(tx, resultsHashes.ReceiptsHash, epoch, round) } 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 From 225c7316ecf1df6e607fde8265d94993b21de356 Mon Sep 17 00:00:00 2001 From: miiu Date: Tue, 24 Mar 2026 14:32:13 +0200 Subject: [PATCH 08/13] uni tests --- .../pruning/fullHistoryPruningStorer_test.go | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) 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() From 839cef7280a2adfa75dd4c0cf6086038809b2b3a Mon Sep 17 00:00:00 2001 From: miiu Date: Tue, 24 Mar 2026 14:35:29 +0200 Subject: [PATCH 09/13] fixes after review --- factory/api/apiResolverFactory.go | 2 +- node/chainSimulator/chainSimulator_test.go | 4 ++-- node/external/blockAPI/apiBlockFactory_test.go | 2 +- node/external/blockAPI/blockArgs.go | 2 +- node/external/blockAPI/check.go | 2 +- node/external/blockAPI/metaBlock.go | 2 +- node/external/blockAPI/shardBlock.go | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/factory/api/apiResolverFactory.go b/factory/api/apiResolverFactory.go index 127ada05cd4..170d05ac839 100644 --- a/factory/api/apiResolverFactory.go +++ b/factory/api/apiResolverFactory.go @@ -742,7 +742,7 @@ func createAPIBlockProcessorArgs(args *ApiResolverArgs, apiTransactionHandler ex EnableEpochsHandler: args.CoreComponents.EnableEpochsHandler(), ProofsPool: args.DataComponents.Datapool().Proofs(), BlockChain: args.DataComponents.Blockchain(), - EnableRoundHandler: args.CoreComponents.EnableRoundsHandler(), + EnableRoundsHandler: args.CoreComponents.EnableRoundsHandler(), } return blockApiArgs, nil diff --git a/node/chainSimulator/chainSimulator_test.go b/node/chainSimulator/chainSimulator_test.go index 79ed6d002e4..45b656d9ca0 100644 --- a/node/chainSimulator/chainSimulator_test.go +++ b/node/chainSimulator/chainSimulator_test.go @@ -586,7 +586,7 @@ func TestSimulator_SendTransactions(t *testing.T) { chainSimulatorCommon.CheckGenerateTransactions(t, chainSimulator) } -func TestSimilator_MoveBalanceCheckReceipt(t *testing.T) { +func TestSimulator_MoveBalanceCheckReceipt(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") } @@ -644,7 +644,7 @@ func TestSimilator_MoveBalanceCheckReceipt(t *testing.T) { require.Equal(te, value, mb.Receipts[0].Value.String()) } } - require.True(t, called) + require.True(te, called) } apiTx, err := chainSimulator.SendTxAndGenerateBlockTilTxIsExecuted(ftx, 10) diff --git a/node/external/blockAPI/apiBlockFactory_test.go b/node/external/blockAPI/apiBlockFactory_test.go index 69ed41e5744..f0bb2bdd91f 100644 --- a/node/external/blockAPI/apiBlockFactory_test.go +++ b/node/external/blockAPI/apiBlockFactory_test.go @@ -48,7 +48,7 @@ func createMockArgsAPIBlockProc() *ArgAPIBlockProcessor { EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, ProofsPool: &dataRetrieverTestCommon.ProofsPoolMock{}, BlockChain: chainHandler, - EnableRoundHandler: &testscommon.EnableRoundsHandlerStub{}, + EnableRoundsHandler: &testscommon.EnableRoundsHandlerStub{}, } } diff --git a/node/external/blockAPI/blockArgs.go b/node/external/blockAPI/blockArgs.go index dca0035bddf..bf9f7a2f91d 100644 --- a/node/external/blockAPI/blockArgs.go +++ b/node/external/blockAPI/blockArgs.go @@ -32,7 +32,7 @@ type ArgAPIBlockProcessor struct { AccountsRepository state.AccountsRepository ScheduledTxsExecutionHandler process.ScheduledTxsExecutionHandler EnableEpochsHandler common.EnableEpochsHandler - EnableRoundHandler common.EnableRoundsHandler + EnableRoundsHandler common.EnableRoundsHandler ProofsPool dataRetriever.ProofsPool BlockChain data.ChainHandler } diff --git a/node/external/blockAPI/check.go b/node/external/blockAPI/check.go index 1ba8ca6b8a4..fdea291a129 100644 --- a/node/external/blockAPI/check.go +++ b/node/external/blockAPI/check.go @@ -69,7 +69,7 @@ func checkNilArg(arg *ArgAPIBlockProcessor) error { if check.IfNil(arg.BlockChain) { return process.ErrNilBlockChain } - if check.IfNil(arg.EnableRoundHandler) { + if check.IfNil(arg.EnableRoundsHandler) { return process.ErrNilEnableRoundsHandler } diff --git a/node/external/blockAPI/metaBlock.go b/node/external/blockAPI/metaBlock.go index c09d083444d..1a34dce633f 100644 --- a/node/external/blockAPI/metaBlock.go +++ b/node/external/blockAPI/metaBlock.go @@ -42,7 +42,7 @@ func newMetaApiBlockProcessor(arg *ArgAPIBlockProcessor, emptyReceiptsHash []byt enableEpochsHandler: arg.EnableEpochsHandler, proofsPool: arg.ProofsPool, blockchain: arg.BlockChain, - enableRoundsHandler: arg.EnableRoundHandler, + enableRoundsHandler: arg.EnableRoundsHandler, }, } } diff --git a/node/external/blockAPI/shardBlock.go b/node/external/blockAPI/shardBlock.go index d3f059302ed..43f1d15b95f 100644 --- a/node/external/blockAPI/shardBlock.go +++ b/node/external/blockAPI/shardBlock.go @@ -42,7 +42,7 @@ func newShardApiBlockProcessor(arg *ArgAPIBlockProcessor, emptyReceiptsHash []by enableEpochsHandler: arg.EnableEpochsHandler, proofsPool: arg.ProofsPool, blockchain: arg.BlockChain, - enableRoundsHandler: arg.EnableRoundHandler, + enableRoundsHandler: arg.EnableRoundsHandler, }, } } From 37ff48391d253b0e12a18eb4588c75866ee17126 Mon Sep 17 00:00:00 2001 From: miiu Date: Tue, 24 Mar 2026 14:50:58 +0200 Subject: [PATCH 10/13] fix unit tests --- integrationTests/testProcessorNodeWithTestWebServer.go | 1 + 1 file changed, 1 insertion(+) 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) From e04f85c56d0942156750118d4aae02abd0ac98c8 Mon Sep 17 00:00:00 2001 From: miiu Date: Wed, 25 Mar 2026 14:35:14 +0200 Subject: [PATCH 11/13] do not return a partial block if the execution result is missing --- node/external/blockAPI/baseBlock.go | 11 +---- node/external/blockAPI/baseBlock_test.go | 56 +----------------------- 2 files changed, 3 insertions(+), 64 deletions(-) diff --git a/node/external/blockAPI/baseBlock.go b/node/external/blockAPI/baseBlock.go index 60688ec64a0..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" @@ -736,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) diff --git a/node/external/blockAPI/baseBlock_test.go b/node/external/blockAPI/baseBlock_test.go index 997f675b715..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" @@ -906,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 }, } @@ -989,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) { From e2d249a12f8209cdbf9e38a0f6e530166a3d240b Mon Sep 17 00:00:00 2001 From: miiu Date: Thu, 26 Mar 2026 11:49:55 +0200 Subject: [PATCH 12/13] fix failing test --- ntp/syncTime_test.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ntp/syncTime_test.go b/ntp/syncTime_test.go index 2cfed00e24e..245a21c3370 100644 --- a/ntp/syncTime_test.go +++ b/ntp/syncTime_test.go @@ -425,7 +425,7 @@ func TestSyncTime_ForceSync(t *testing.T) { t.Run("ForceSync should work", func(t *testing.T) { t.Parallel() - numCalls := 0 + var numCalls atomic.Uint32 st := ntp.NewSyncTime( config.NTPConfig{ @@ -434,7 +434,7 @@ func TestSyncTime_ForceSync(t *testing.T) { OutOfBoundsThreshold: 2, }, func(options ntp.NTPOptions, hostIndex int) (*beevikNtp.Response, error) { - numCalls++ + numCalls.Add(1) time.Sleep(1 * time.Millisecond) @@ -449,12 +449,15 @@ func TestSyncTime_ForceSync(t *testing.T) { st.ForceSync() - time.Sleep(time.Duration(ntp.NumRequestsFromHost+5) * time.Millisecond) + require.Eventually(t, func() bool { + return st.ClockOffset() == time.Millisecond && + numCalls.Load() == uint32(ntp.NumRequestsFromHost) + }, time.Second, 5*time.Millisecond) expClockOffset := 1 * time.Millisecond assert.Equal(t, expClockOffset, st.ClockOffset()) - require.Equal(t, ntp.NumRequestsFromHost, numCalls) + require.Equal(t, ntp.NumRequestsFromHost, int(numCalls.Load())) }) t.Run("TriggerSync should not trigger multiple times", func(t *testing.T) { From 336b0e97335d4de026a5b453ef8a887bd6aa73eb Mon Sep 17 00:00:00 2001 From: miiu Date: Thu, 26 Mar 2026 12:13:49 +0200 Subject: [PATCH 13/13] unit test --- node/external/blockAPI/apiBlockFactory_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/node/external/blockAPI/apiBlockFactory_test.go b/node/external/blockAPI/apiBlockFactory_test.go index f0bb2bdd91f..4f39d8a3c2c 100644 --- a/node/external/blockAPI/apiBlockFactory_test.go +++ b/node/external/blockAPI/apiBlockFactory_test.go @@ -211,6 +211,15 @@ func TestCreateAPIBlockProcessorNilArgs(t *testing.T) { _, err := CreateAPIBlockProcessor(arguments) assert.Equal(t, process.ErrNilBlockChain, err) }) + t.Run("NilEnableRoundsHandler", func(t *testing.T) { + t.Parallel() + + arguments := createMockArgsAPIBlockProc() + arguments.EnableRoundsHandler = nil + + _, err := CreateAPIBlockProcessor(arguments) + assert.Equal(t, process.ErrNilEnableRoundsHandler, err) + }) } func TestGetBlockByHash_KeyNotFound(t *testing.T) {