From 0761ec597b4d609c3ae718eb8387cba4e25703b6 Mon Sep 17 00:00:00 2001 From: BeniaminDrasovean Date: Wed, 24 Jun 2026 15:34:53 +0300 Subject: [PATCH 1/4] when transitioning to Andromeda, request missing proofs --- process/block/baseProcess.go | 9 ++- process/block/baseProcess_test.go | 106 +++++++++++++++++++++++++----- 2 files changed, 96 insertions(+), 19 deletions(-) diff --git a/process/block/baseProcess.go b/process/block/baseProcess.go index 49dde1f2e2..72f298c616 100644 --- a/process/block/baseProcess.go +++ b/process/block/baseProcess.go @@ -1216,10 +1216,15 @@ func (bp *baseProcessor) requestMissingFinalityAttestingHeaders( } for index := range headers { - bp.hdrsForCurrBlock.hdrHashAndInfo[string(headersHashes[index])] = &hdrInfo{ - hdr: headers[index], + hdrHash := headersHashes[index] + hdr := headers[index] + + bp.hdrsForCurrBlock.hdrHashAndInfo[string(hdrHash)] = &hdrInfo{ + hdr: hdr, usedInBlock: false, } + + bp.requestProofIfNeeded(hdrHash, hdr) } } diff --git a/process/block/baseProcess_test.go b/process/block/baseProcess_test.go index ebe8b2473e..72f72062e7 100644 --- a/process/block/baseProcess_test.go +++ b/process/block/baseProcess_test.go @@ -2515,23 +2515,34 @@ func TestBaseProcessor_CheckScheduledData(t *testing.T) { createProcessorAndHeader := func(t *testing.T) (interface { CheckScheduledData(data.HeaderHandler) error - }, *block.HeaderV2) { t.Helper(); coreComponents, dataComponents, bootstrapComponents, statusComponents := createComponentHolderMocks(); coreComponents.EnableEpochsHandlerField = enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.ScheduledMiniBlocksFlag); arguments := CreateMockArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents); arguments.ArgBaseProcessor.AccountsDB[state.UserAccountsState] = &stateMock.AccountsStub{ - RootHashCalled: func() ([]byte, error) { - return []byte("scheduled-root"), nil - }, - }; arguments.ArgBaseProcessor.ScheduledTxsExecutionHandler = &testscommon.ScheduledTxsExecutionStub{ - GetScheduledGasAndFeesCalled: func() scheduled.GasAndFees { - return scheduledGasAndFees - }, - }; processor, err := blproc.NewShardProcessor(arguments); require.NoError(t, err); header := &block.HeaderV2{ - Header: &block.Header{}, - ScheduledRootHash: []byte("scheduled-root"), - ScheduledAccumulatedFees: big.NewInt(11), - ScheduledDeveloperFees: big.NewInt(12), - ScheduledGasProvided: 13, - ScheduledGasPenalized: 14, - ScheduledGasRefunded: 15, - }; return processor, header } + }, *block.HeaderV2) { + t.Helper() + coreComponents, dataComponents, bootstrapComponents, statusComponents := createComponentHolderMocks() + coreComponents.EnableEpochsHandlerField = enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.ScheduledMiniBlocksFlag) + arguments := CreateMockArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.ArgBaseProcessor.AccountsDB[state.UserAccountsState] = &stateMock.AccountsStub{ + RootHashCalled: func() ([]byte, error) { + return []byte("scheduled-root"), nil + }, + } + arguments.ArgBaseProcessor.ScheduledTxsExecutionHandler = &testscommon.ScheduledTxsExecutionStub{ + GetScheduledGasAndFeesCalled: func() scheduled.GasAndFees { + return scheduledGasAndFees + }, + } + processor, err := blproc.NewShardProcessor(arguments) + require.NoError(t, err) + header := &block.HeaderV2{ + Header: &block.Header{}, + ScheduledRootHash: []byte("scheduled-root"), + ScheduledAccumulatedFees: big.NewInt(11), + ScheduledDeveloperFees: big.NewInt(12), + ScheduledGasProvided: 13, + ScheduledGasPenalized: 14, + ScheduledGasRefunded: 15, + } + return processor, header + } t.Run("should work when scheduled data matches", func(t *testing.T) { t.Parallel() @@ -3818,3 +3829,64 @@ func TestBaseProcessor_DisplayHeader(t *testing.T) { require.Equal(t, 23, len(lines)) }) } + +func TestMetaProcessor_requestMissingFinalityAttestingShardHeaders_AttestationHeaderPresentProofMissingShouldRequestProof(t *testing.T) { + t.Parallel() + + noOfShards := uint32(2) + td := createTestData() + proofRequests := make(chan requestedProof, 1) + + arguments := createMetaProcessorArguments(t, noOfShards) + coreComponents, ok := arguments.CoreComponents.(*mock.CoreComponentsMock) + require.True(t, ok) + coreComponents.EnableEpochsHandlerField = &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return flag == common.AndromedaFlag + }, + } + + poolsHolder, ok := arguments.DataComponents.Datapool().(*dataRetrieverMock.PoolsHolderMock) + require.True(t, ok) + poolsHolder.SetHeadersPool(createPoolsHolderForHeaderRequests()) + + requestHandler, ok := arguments.ArgBaseProcessor.RequestHandler.(*testscommon.RequestHandlerStub) + require.True(t, ok) + requestHandler.RequestEquivalentProofByHashCalled = func(headerShard uint32, headerHash []byte) { + proofRequests <- requestedProof{shardID: headerShard, hash: append([]byte(nil), headerHash...)} + } + + mp, err := blproc.NewMetaProcessor(*arguments) + require.NoError(t, err) + + referenced := td[0].referencedHeaderData + attestation := td[0].attestationHeaderData + + mp.SetShardBlockFinality(1) + mp.SetHighestHdrNonceForCurrentBlock(referenced.header.GetShardID(), referenced.header.GetNonce()) + mp.SetLastNotarizedHeaderForShard(referenced.header.GetShardID(), &blproc.LastNotarizedHeaderInfo{ + Header: referenced.header, + Hash: referenced.headerHash, + NotarizedBasedOnProof: false, + HasProof: false, + }) + + mp.GetDataPool().Headers().AddHeader(attestation.headerHash, attestation.header) + + missingFinalityHeaders := mp.RequestMissingFinalityAttestingShardHeaders() + + require.Equal(t, uint32(0), missingFinalityHeaders) + + select { + case requested := <-proofRequests: + require.Equal(t, attestation.header.GetShardID(), requested.shardID) + require.Equal(t, attestation.headerHash, requested.hash) + case <-time.After(100 * time.Millisecond): + require.Fail(t, "missing equivalent proof request for attestation header") + } +} + +type requestedProof struct { + shardID uint32 + hash []byte +} From 235f4057643abd918302d624358543cae254376f Mon Sep 17 00:00:00 2001 From: BeniaminDrasovean Date: Thu, 25 Jun 2026 17:05:54 +0300 Subject: [PATCH 2/4] fix after review --- process/block/baseProcess.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/process/block/baseProcess.go b/process/block/baseProcess.go index 72f298c616..13c0a20c60 100644 --- a/process/block/baseProcess.go +++ b/process/block/baseProcess.go @@ -1216,15 +1216,12 @@ func (bp *baseProcessor) requestMissingFinalityAttestingHeaders( } for index := range headers { - hdrHash := headersHashes[index] - hdr := headers[index] - - bp.hdrsForCurrBlock.hdrHashAndInfo[string(hdrHash)] = &hdrInfo{ - hdr: hdr, + bp.hdrsForCurrBlock.hdrHashAndInfo[string(headersHashes[index])] = &hdrInfo{ + hdr: headers[index], usedInBlock: false, } - bp.requestProofIfNeeded(hdrHash, hdr) + bp.requestProofIfNeeded(headersHashes[index], headers[index]) } } From cb5ed518571353199d4c7d6fe1d109f8c07b7dc8 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Fri, 3 Jul 2026 16:10:49 +0300 Subject: [PATCH 3/4] update after merge --- process/block/baseProcess_test.go | 61 ------------ process/block/headerForBlock/export_test.go | 20 ++++ .../block/headerForBlock/headersForBlock.go | 2 + .../headerForBlock/headersForBlock_test.go | 92 +++++++++++++++++++ 4 files changed, 114 insertions(+), 61 deletions(-) diff --git a/process/block/baseProcess_test.go b/process/block/baseProcess_test.go index de401ca53c..3032f7f202 100644 --- a/process/block/baseProcess_test.go +++ b/process/block/baseProcess_test.go @@ -7151,64 +7151,3 @@ func TestCheckEWLSizeAndReset(t *testing.T) { sp.CheckEWLSizeAndReset() }) } - -func TestMetaProcessor_requestMissingFinalityAttestingShardHeaders_AttestationHeaderPresentProofMissingShouldRequestProof(t *testing.T) { - t.Parallel() - - noOfShards := uint32(2) - td := createTestData() - proofRequests := make(chan requestedProof, 1) - - arguments := createMetaProcessorArguments(t, noOfShards) - coreComponents, ok := arguments.CoreComponents.(*mock.CoreComponentsMock) - require.True(t, ok) - coreComponents.EnableEpochsHandlerField = &enableEpochsHandlerMock.EnableEpochsHandlerStub{ - IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { - return flag == common.AndromedaFlag - }, - } - - poolsHolder, ok := arguments.DataComponents.Datapool().(*dataRetrieverMock.PoolsHolderMock) - require.True(t, ok) - poolsHolder.SetHeadersPool(createPoolsHolderForHeaderRequests()) - - requestHandler, ok := arguments.ArgBaseProcessor.RequestHandler.(*testscommon.RequestHandlerStub) - require.True(t, ok) - requestHandler.RequestEquivalentProofByHashCalled = func(headerShard uint32, headerHash []byte) { - proofRequests <- requestedProof{shardID: headerShard, hash: append([]byte(nil), headerHash...)} - } - - mp, err := blproc.NewMetaProcessor(*arguments) - require.NoError(t, err) - - referenced := td[0].referencedHeaderData - attestation := td[0].attestationHeaderData - - mp.SetShardBlockFinality(1) - mp.SetHighestHdrNonceForCurrentBlock(referenced.header.GetShardID(), referenced.header.GetNonce()) - mp.SetLastNotarizedHeaderForShard(referenced.header.GetShardID(), &blproc.LastNotarizedHeaderInfo{ - Header: referenced.header, - Hash: referenced.headerHash, - NotarizedBasedOnProof: false, - HasProof: false, - }) - - mp.GetDataPool().Headers().AddHeader(attestation.headerHash, attestation.header) - - missingFinalityHeaders := mp.RequestMissingFinalityAttestingShardHeaders() - - require.Equal(t, uint32(0), missingFinalityHeaders) - - select { - case requested := <-proofRequests: - require.Equal(t, attestation.header.GetShardID(), requested.shardID) - require.Equal(t, attestation.headerHash, requested.hash) - case <-time.After(100 * time.Millisecond): - require.Fail(t, "missing equivalent proof request for attestation header") - } -} - -type requestedProof struct { - shardID uint32 - hash []byte -} diff --git a/process/block/headerForBlock/export_test.go b/process/block/headerForBlock/export_test.go index f1547738d9..eeb43c67f6 100644 --- a/process/block/headerForBlock/export_test.go +++ b/process/block/headerForBlock/export_test.go @@ -48,3 +48,23 @@ func (hfb *headersForBlock) ComputeExistingAndRequestMissingShardHeaders(metaBlo func (hfb *headersForBlock) UpdateLastNotarizedBlockForShard(hdr data.ShardHeaderHandler, headerHash []byte) { hfb.updateLastNotarizedBlockForShard(hdr, headerHash) } + +// SetLastNotarizedHeaderForShard - +func (hfb *headersForBlock) SetLastNotarizedHeaderForShard(shardID uint32, info LastNotarizedHeaderInfoHandler) { + hfb.lastNotarizedShardHeaders[shardID] = info +} + +// SetHighestHdrNonceForCurrentBlock - +func (hfb *headersForBlock) SetHighestHdrNonceForCurrentBlock(shardID uint32, nonce uint64) { + hfb.highestHdrNonce[shardID] = nonce +} + +// SetShardBlockFinality - +func (hfb *headersForBlock) SetShardBlockFinality(finality uint32) { + hfb.blockFinality = finality +} + +// RequestMissingFinalityAttestingShardHeaders - +func (hfb *headersForBlock) RequestMissingFinalityAttestingShardHeaders() uint32 { + return hfb.requestMissingFinalityAttestingShardHeaders() +} diff --git a/process/block/headerForBlock/headersForBlock.go b/process/block/headerForBlock/headersForBlock.go index 3d2726b9ad..f3f3a3863a 100644 --- a/process/block/headerForBlock/headersForBlock.go +++ b/process/block/headerForBlock/headersForBlock.go @@ -580,6 +580,8 @@ func (hfb *headersForBlock) requestMissingFinalityAttestingHeaders( false, false, ) + + hfb.requestProofIfNeeded(headersHashes[index], headers[index]) } } diff --git a/process/block/headerForBlock/headersForBlock_test.go b/process/block/headerForBlock/headersForBlock_test.go index d1da9b42c8..9abb4912ab 100644 --- a/process/block/headerForBlock/headersForBlock_test.go +++ b/process/block/headerForBlock/headersForBlock_test.go @@ -914,6 +914,98 @@ func TestHeadersForBlock_ComputeHeadersForCurrentBlock(t *testing.T) { }) } +func TestHeadersForBlock_RequestMissingFinalityAttestingShardHeaders(t *testing.T) { + t.Parallel() + + t.Run("attestation header present but proof missing should request proof", func(t *testing.T) { + t.Parallel() + + referencedHeaderHash := []byte("sh0TestHash1") + referencedHeader := &block.HeaderV2{ + Header: &block.Header{ + ShardID: 0, + Round: 100, + Nonce: 100, + }, + } + attestationHeaderHash := []byte("sh0TestHash2") + attestationHeader := &block.HeaderV2{ + Header: &block.Header{ + ShardID: 0, + Round: 101, + Nonce: 101, + PrevHash: referencedHeaderHash, + }, + } + + counter := 0 + var requestedShardID uint32 + var requestedHash []byte + var mutRequestEquivalentProof sync.Mutex + wg := &sync.WaitGroup{} + wg.Add(1) + + args := createMockArgs() + args.ShardCoordinator = &testscommon.ShardsCoordinatorMock{ + NoShards: 2, + } + args.EnableEpochsHandler = &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return flag == common.AndromedaFlag + }, + } + args.RequestHandler = &testscommon.RequestHandlerStub{ + RequestEquivalentProofByHashCalled: func(headerShard uint32, headerHash []byte) { + mutRequestEquivalentProof.Lock() + counter++ + requestedShardID = headerShard + requestedHash = append([]byte(nil), headerHash...) + mutRequestEquivalentProof.Unlock() + + wg.Done() + }, + } + + poolsHolder, ok := args.DataPool.(*dataRetriever.PoolsHolderMock) + require.True(t, ok) + poolsHolder.SetHeadersPool(createPoolsHolderForHeaderRequests()) + poolsHolder.SetProofsPool(&dataRetriever.ProofsPoolMock{ + HasProofCalled: func(shardID uint32, headerHash []byte) bool { + return false // no proof available for the attestation header + }, + }) + + args.BlockTracker = &mock.BlockTrackerStub{ + GetLastCrossNotarizedHeaderCalled: func(shardID uint32) (data.HeaderHandler, []byte, error) { + return referencedHeader, referencedHeaderHash, nil + }, + } + + hfb, err := headerForBlock.NewHeadersForBlock(args) + require.NoError(t, err) + + hfb.SetShardBlockFinality(1) + hfb.SetHighestHdrNonceForCurrentBlock(referencedHeader.GetShardID(), referencedHeader.GetNonce()) + hfb.SetLastNotarizedHeaderForShard( + referencedHeader.GetShardID(), + headerForBlock.NewLastNotarizedHeaderInfo(referencedHeader, referencedHeaderHash, false, false), + ) + + poolsHolder.Headers().AddHeader(attestationHeaderHash, attestationHeader) + + missingFinalityHeaders := hfb.RequestMissingFinalityAttestingShardHeaders() + require.Equal(t, uint32(0), missingFinalityHeaders) + + wg.Wait() + + mutRequestEquivalentProof.Lock() + require.Equal(t, 1, counter) + require.Equal(t, attestationHeader.GetShardID(), requestedShardID) + require.Equal(t, attestationHeaderHash, requestedHash) + mutRequestEquivalentProof.Unlock() + }) +} + type headerData struct { header data.HeaderHandler headerHash []byte From f60f0138a5e6536be4086fc4ed90f8c402b0cc5e Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Fri, 3 Jul 2026 16:19:56 +0300 Subject: [PATCH 4/4] fix linter --- process/block/interceptedBlocks/common.go | 1 - process/coordinator/process.go | 34 ----------------------- 2 files changed, 35 deletions(-) diff --git a/process/block/interceptedBlocks/common.go b/process/block/interceptedBlocks/common.go index cdd6133caa..188f0766c5 100644 --- a/process/block/interceptedBlocks/common.go +++ b/process/block/interceptedBlocks/common.go @@ -10,7 +10,6 @@ import ( "github.com/multiversx/mx-chain-go/sharding" ) -const maxLenMiniBlockReservedField = 10 const maxLenMiniBlockHeaderReservedField = 32 func checkForDuplicateHashes(hashes [][]byte) error { diff --git a/process/coordinator/process.go b/process/coordinator/process.go index b5a27f108c..c438fa5e78 100644 --- a/process/coordinator/process.go +++ b/process/coordinator/process.go @@ -484,40 +484,6 @@ func (tc *transactionCoordinator) processMiniBlocksFromMe( return nil } -// TODO consider calling this from VerifyBlockProposal instead of ProcessBlockProposal -func (tc *transactionCoordinator) checkMiniBlock( - miniBlock *block.MiniBlock, -) error { - // there are checks for non existing shard id at interceptors level - - if miniBlock.SenderShardID != tc.shardCoordinator.SelfId() && miniBlock.GetReceiverShardID() != tc.shardCoordinator.SelfId() && miniBlock.GetReceiverShardID() != core.AllShardId { - return fmt.Errorf("%w - not valid shard ids: block type: %s, sender shard id: %d, receiver shard id: %d", - process.ErrInvalidShardId, - miniBlock.Type, - miniBlock.SenderShardID, - miniBlock.ReceiverShardID) - } - - if miniBlock.GetType() == block.PeerBlock && - (miniBlock.GetSenderShardID() != core.MetachainShardId || miniBlock.GetReceiverShardID() != core.AllShardId) { - return fmt.Errorf("%w - peer blocks: block type: %s, sender shard id: %d, receiver shard id: %d", - process.ErrInvalidShardId, - miniBlock.Type, - miniBlock.SenderShardID, - miniBlock.ReceiverShardID) - } - - if miniBlock.GetType() != block.PeerBlock && miniBlock.GetReceiverShardID() == core.AllShardId { - return fmt.Errorf("%w - invalid all shard ids: block type: %s, sender shard id: %d, receiver shard id: %d", - process.ErrInvalidShardId, - miniBlock.Type, - miniBlock.SenderShardID, - miniBlock.ReceiverShardID) - } - - return nil -} - func (tc *transactionCoordinator) processMiniBlocksToMe( header data.HeaderHandler, body *block.Body,