From 450d335214ad00c6325b201e48b14bfa07750ec5 Mon Sep 17 00:00:00 2001 From: Sorin Stanculeanu Date: Wed, 23 Oct 2024 18:40:06 +0300 Subject: [PATCH] proper proofs verification on notarization --- process/block/baseProcess.go | 7 +-- process/block/hdrForBlock.go | 4 ++ process/block/metablock.go | 116 ++++++++++++++++++++++------------- process/block/shardblock.go | 16 ++--- 4 files changed, 90 insertions(+), 53 deletions(-) diff --git a/process/block/baseProcess.go b/process/block/baseProcess.go index cc42e415cac..854a8b63812 100644 --- a/process/block/baseProcess.go +++ b/process/block/baseProcess.go @@ -60,7 +60,6 @@ type nonceAndHashInfo struct { type hdrInfo struct { usedInBlock bool - hasProof bool hdr data.HeaderHandler } @@ -620,9 +619,9 @@ func (bp *baseProcessor) sortHeadersForCurrentBlockByNonce(usedInBlock bool) map hdrsForCurrentBlock := make(map[uint32][]data.HeaderHandler) bp.hdrsForCurrBlock.mutHdrsForBlock.RLock() - for _, headerInfo := range bp.hdrsForCurrBlock.hdrHashAndInfo { + for hdrHash, headerInfo := range bp.hdrsForCurrBlock.hdrHashAndInfo { isFlagEnabledForHeader := bp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, headerInfo.hdr.GetEpoch()) - hasMissingProof := isFlagEnabledForHeader && !headerInfo.hasProof + hasMissingProof := isFlagEnabledForHeader && !bp.proofsPool.HasProof(headerInfo.hdr.GetShardID(), []byte(hdrHash)) if headerInfo.usedInBlock != usedInBlock || hasMissingProof { continue } @@ -645,7 +644,7 @@ func (bp *baseProcessor) sortHeaderHashesForCurrentBlockByNonce(usedInBlock bool bp.hdrsForCurrBlock.mutHdrsForBlock.RLock() for metaBlockHash, headerInfo := range bp.hdrsForCurrBlock.hdrHashAndInfo { isFlagEnabledForHeader := bp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, headerInfo.hdr.GetEpoch()) - hasMissingProof := isFlagEnabledForHeader && !headerInfo.hasProof + hasMissingProof := isFlagEnabledForHeader && !bp.proofsPool.HasProof(headerInfo.hdr.GetShardID(), []byte(metaBlockHash)) if headerInfo.usedInBlock != usedInBlock || hasMissingProof { continue } diff --git a/process/block/hdrForBlock.go b/process/block/hdrForBlock.go index fd7384aedc7..ce9e7f76e2b 100644 --- a/process/block/hdrForBlock.go +++ b/process/block/hdrForBlock.go @@ -4,6 +4,7 @@ import ( "sync" "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-core-go/data/block" ) type hdrForBlock struct { @@ -12,12 +13,14 @@ type hdrForBlock struct { highestHdrNonce map[uint32]uint64 mutHdrsForBlock sync.RWMutex hdrHashAndInfo map[string]*hdrInfo + missingProofs map[string]*block.HeaderProof } func newHdrForBlock() *hdrForBlock { return &hdrForBlock{ hdrHashAndInfo: make(map[string]*hdrInfo), highestHdrNonce: make(map[uint32]uint64), + missingProofs: make(map[string]*block.HeaderProof), } } @@ -25,6 +28,7 @@ func (hfb *hdrForBlock) initMaps() { hfb.mutHdrsForBlock.Lock() hfb.hdrHashAndInfo = make(map[string]*hdrInfo) hfb.highestHdrNonce = make(map[uint32]uint64) + hfb.missingProofs = make(map[string]*block.HeaderProof) hfb.mutHdrsForBlock.Unlock() } diff --git a/process/block/metablock.go b/process/block/metablock.go index e7e3d70cc20..f31e5157db2 100644 --- a/process/block/metablock.go +++ b/process/block/metablock.go @@ -322,7 +322,7 @@ func (mp *metaProcessor) ProcessBlock( ) } - err = mp.waitForBlockHeadersAndProofs(haveTime()) + err = mp.waitForBlockHeaders(haveTime()) mp.hdrsForCurrBlock.mutHdrsForBlock.RLock() missingShardHdrs := mp.hdrsForCurrBlock.missingHdrs @@ -341,6 +341,16 @@ func (mp *metaProcessor) ProcessBlock( } } + // check proofs for shard data + for _, shardData := range header.ShardInfo { + // TODO: consider the validation of the proof: + // compare the one from proofsPool with what shardData.CurrentSignature and shardData.CurrentPubKeysBitmap hold + // if they are different, verify the proof received on header + if !mp.proofsPool.HasProof(shardData.ShardID, shardData.HeaderHash) { + return fmt.Errorf("%w for header hash %s", process.ErrMissingHeaderProof, hex.EncodeToString(shardData.HeaderHash)) + } + } + defer func() { go mp.checkAndRequestIfShardHeadersMissing() }() @@ -1098,7 +1108,6 @@ func (mp *metaProcessor) createAndProcessCrossMiniBlocksDstMe( mp.hdrsForCurrBlock.hdrHashAndInfo[string(orderedHdrsHashes[i])] = &hdrInfo{ hdr: currShardHdr, usedInBlock: true, - hasProof: true, } hdrsAdded++ hdrsAddedForShard[currShardHdr.GetShardID()]++ @@ -1138,7 +1147,10 @@ func (mp *metaProcessor) createAndProcessCrossMiniBlocksDstMe( miniBlocks = append(miniBlocks, currMBProcessed...) txsAdded += currTxsAdded - mp.hdrsForCurrBlock.hdrHashAndInfo[string(orderedHdrsHashes[i])] = &hdrInfo{hdr: currShardHdr, usedInBlock: true, hasProof: true} + mp.hdrsForCurrBlock.hdrHashAndInfo[string(orderedHdrsHashes[i])] = &hdrInfo{ + hdr: currShardHdr, + usedInBlock: true, + } hdrsAdded++ hdrsAddedForShard[currShardHdr.GetShardID()]++ @@ -1753,7 +1765,10 @@ func (mp *metaProcessor) getLastCrossNotarizedShardHdrs() (map[uint32]data.Heade log.Debug("lastCrossNotarizedHeader for shard", "shardID", shardID, "hash", hash) lastCrossNotarizedHeader[shardID] = lastCrossNotarizedHeaderForShard usedInBlock := mp.isGenesisShardBlockAndFirstMeta(lastCrossNotarizedHeaderForShard.GetNonce()) - mp.hdrsForCurrBlock.hdrHashAndInfo[string(hash)] = &hdrInfo{hdr: lastCrossNotarizedHeaderForShard, usedInBlock: usedInBlock} + mp.hdrsForCurrBlock.hdrHashAndInfo[string(hash)] = &hdrInfo{ + hdr: lastCrossNotarizedHeaderForShard, + usedInBlock: usedInBlock, + } } return lastCrossNotarizedHeader, nil @@ -1939,22 +1954,36 @@ func (mp *metaProcessor) receivedShardHeader(headerHandler data.HeaderHandler, s hdrInfoForHash.hdr = shardHeader mp.hdrsForCurrBlock.missingHdrs-- + // if there is an entry for the missing proof, it means that proofsPool did not have it while scanning shardData + // thus header epoch was not available at that time + incompleteProof, hasMissingProof := mp.hdrsForCurrBlock.missingProofs[string(shardHeaderHash)] + if hasMissingProof { + constructedProof := &block.HeaderProof{ + PubKeysBitmap: incompleteProof.PubKeysBitmap, + AggregatedSignature: incompleteProof.AggregatedSignature, + HeaderHash: incompleteProof.HeaderHash, + HeaderEpoch: shardHeader.GetEpoch(), + HeaderNonce: incompleteProof.HeaderNonce, + HeaderShardId: incompleteProof.HeaderShardId, + } + errAddProof := mp.proofsPool.AddProof(constructedProof) + if errAddProof != nil { + log.Trace("could not add the constructed proof after header received", "hash", hex.EncodeToString(incompleteProof.HeaderHash)) + } + + delete(mp.hdrsForCurrBlock.missingProofs, string(shardHeaderHash)) + } + if shardHeader.GetNonce() > mp.hdrsForCurrBlock.highestHdrNonce[shardHeader.GetShardID()] { mp.hdrsForCurrBlock.highestHdrNonce[shardHeader.GetShardID()] = shardHeader.GetNonce() } } - if hdrInfoForHash != nil && !hdrInfoForHash.hasProof { - hdrInfoForHash.hasProof = mp.proofsPool.HasProof(shardHeader.GetShardID(), shardHeaderHash) - } - shouldConsiderProofsForNotarization := mp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, shardHeader.GetEpoch()) - if mp.hdrsForCurrBlock.missingHdrs == 0 { - if !shouldConsiderProofsForNotarization { - mp.hdrsForCurrBlock.missingFinalityAttestingHdrs = mp.requestMissingFinalityAttestingShardHeaders() - if mp.hdrsForCurrBlock.missingFinalityAttestingHdrs == 0 { - log.Debug("received all missing finality attesting shard headers") - } + if mp.hdrsForCurrBlock.missingHdrs == 0 && !shouldConsiderProofsForNotarization { + mp.hdrsForCurrBlock.missingFinalityAttestingHdrs = mp.requestMissingFinalityAttestingShardHeaders() + if mp.hdrsForCurrBlock.missingFinalityAttestingHdrs == 0 { + log.Debug("received all missing finality attesting shard headers") } } @@ -1962,30 +1991,7 @@ func (mp *metaProcessor) receivedShardHeader(headerHandler data.HeaderHandler, s missingFinalityAttestingShardHdrs := mp.hdrsForCurrBlock.missingFinalityAttestingHdrs mp.hdrsForCurrBlock.mutHdrsForBlock.Unlock() - hasAllProofs := true - for shardHdrHash, shardHdr := range mp.hdrsForCurrBlock.hdrHashAndInfo { - if check.IfNil(shardHdr.hdr) { - continue - } - - if !mp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, shardHdr.hdr.GetEpoch()) { - continue - } - - if shardHdr.hasProof { - continue - } - - hasProof := mp.proofsPool.HasProof(shardHdr.hdr.GetShardID(), []byte(shardHdrHash)) - if !hasProof { - hasAllProofs = false - break - } - } - hasMissingProofs := !hasAllProofs && shouldConsiderProofsForNotarization - - allMissingShardHeadersReceived := missingShardHdrs == 0 && - missingFinalityAttestingShardHdrs == 0 && !hasMissingProofs + allMissingShardHeadersReceived := missingShardHdrs == 0 && missingFinalityAttestingShardHdrs == 0 if allMissingShardHeadersReceived { mp.chRcvAllHdrs <- true } @@ -2058,6 +2064,15 @@ func (mp *metaProcessor) computeExistingAndRequestMissingShardHeaders(metaBlock usedInBlock: true, } + mp.hdrsForCurrBlock.missingProofs[string(shardData.HeaderHash)] = &block.HeaderProof{ + PubKeysBitmap: shardData.PubKeysBitmap, + AggregatedSignature: shardData.Signature, + HeaderHash: shardData.HeaderHash, + HeaderEpoch: 0, // will come later with the header + HeaderNonce: shardData.Nonce, + HeaderShardId: shardData.ShardID, + } + go mp.requestHandler.RequestShardHeader(shardData.ShardID, shardData.HeaderHash) continue } @@ -2076,7 +2091,20 @@ func (mp *metaProcessor) computeExistingAndRequestMissingShardHeaders(metaBlock notarizedShardHdrsBasedOnProofs++ hasProofForShardHdr := mp.proofsPool.HasProof(shardData.ShardID, shardData.HeaderHash) - mp.hdrsForCurrBlock.hdrHashAndInfo[string(shardData.HeaderHash)].hasProof = hasProofForShardHdr + if !hasProofForShardHdr { + // TODO: consider verifying the proof before adding it into the proofsPool + errAddProof := mp.proofsPool.AddProof(&block.HeaderProof{ + PubKeysBitmap: shardData.CurrentPubKeysBitmap, + AggregatedSignature: shardData.CurrentSignature, + HeaderHash: shardData.HeaderHash, + HeaderEpoch: hdr.GetEpoch(), + HeaderNonce: shardData.Nonce, + HeaderShardId: shardData.ShardID, + }) + if errAddProof != nil { + log.Trace("could not add proof from shard data for header", "hash", hex.EncodeToString(shardData.HeaderHash)) + } + } } } @@ -2104,7 +2132,7 @@ func (mp *metaProcessor) createShardInfo() ([]data.ShardDataHandler, error) { isBlockAfterEquivalentMessagesFlag := check.IfNil(headerInfo.hdr) && mp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, headerInfo.hdr.GetEpoch()) - hasMissingShardHdrProof := isBlockAfterEquivalentMessagesFlag && !headerInfo.hasProof + hasMissingShardHdrProof := isBlockAfterEquivalentMessagesFlag && !mp.proofsPool.HasProof(headerInfo.hdr.GetShardID(), []byte(hdrHash)) if hasMissingShardHdrProof { return nil, fmt.Errorf("%w for shard header with hash %s", process.ErrMissingHeaderProof, hdrHash) } @@ -2133,6 +2161,7 @@ func (mp *metaProcessor) createShardInfo() ([]data.ShardDataHandler, error) { return nil, err } shardData.CurrentPubKeysBitmap = currentProof.GetPubKeysBitmap() + shardData.CurrentSignature = currentProof.GetAggregatedSignature() } shardData.NumPendingMiniBlocks = uint32(len(mp.pendingMiniBlocksHandler.GetPendingMiniBlocks(shardData.ShardID))) header, _, err := mp.blockTracker.GetLastSelfNotarizedHeader(shardHdr.GetShardID()) @@ -2371,7 +2400,10 @@ func (mp *metaProcessor) prepareBlockHeaderInternalMapForValidatorProcessor() { } mp.hdrsForCurrBlock.mutHdrsForBlock.Lock() - mp.hdrsForCurrBlock.hdrHashAndInfo[string(currentBlockHeaderHash)] = &hdrInfo{false, false, currentBlockHeader} + mp.hdrsForCurrBlock.hdrHashAndInfo[string(currentBlockHeaderHash)] = &hdrInfo{ + usedInBlock: false, + hdr: currentBlockHeader, + } mp.hdrsForCurrBlock.mutHdrsForBlock.Unlock() } @@ -2398,7 +2430,7 @@ func (mp *metaProcessor) verifyValidatorStatisticsRootHash(header *block.MetaBlo return nil } -func (mp *metaProcessor) waitForBlockHeadersAndProofs(waitTime time.Duration) error { +func (mp *metaProcessor) waitForBlockHeaders(waitTime time.Duration) error { select { case <-mp.chRcvAllHdrs: return nil diff --git a/process/block/shardblock.go b/process/block/shardblock.go index 7e0cd794fd7..d85382a9a67 100644 --- a/process/block/shardblock.go +++ b/process/block/shardblock.go @@ -264,7 +264,7 @@ func (sp *shardProcessor) ProcessBlock( ) } - err = sp.waitForMetaHdrHashesAndProofs(haveTime()) + err = sp.waitForMetaHdrHashes(haveTime()) sp.hdrsForCurrBlock.mutHdrsForBlock.RLock() missingMetaHdrs := sp.hdrsForCurrBlock.missingHdrs @@ -293,6 +293,13 @@ func (sp *shardProcessor) ProcessBlock( return process.ErrAccountStateDirty } + // check proofs for shard data + for _, metaBlockHash := range header.GetMetaBlockHashes() { + if !sp.proofsPool.HasProof(core.MetachainShardId, metaBlockHash) { + return fmt.Errorf("%w for header hash %s", process.ErrMissingHeaderProof, hex.EncodeToString(metaBlockHash)) + } + } + defer func() { go sp.checkAndRequestIfMetaHeadersMissing() }() @@ -1803,9 +1810,6 @@ func (sp *shardProcessor) computeExistingAndRequestMissingMetaHeaders(header dat shouldConsiderProofsForNotarization := sp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, hdr.Epoch) if shouldConsiderProofsForNotarization { notarizedMetaHdrsBasedOnProofs++ - - hasProofForShardHdr := sp.proofsPool.HasProof(core.MetachainShardId, metaBlockHashes[i]) - sp.hdrsForCurrBlock.hdrHashAndInfo[string(metaBlockHashes[i])].hasProof = hasProofForShardHdr } } @@ -1952,7 +1956,6 @@ func (sp *shardProcessor) createAndProcessMiniBlocksDstMe(haveTime func() bool) sp.hdrsForCurrBlock.hdrHashAndInfo[string(createAndProcessInfo.currMetaHdrHash)] = &hdrInfo{ hdr: createAndProcessInfo.currMetaHdr, usedInBlock: true, - hasProof: true, } createAndProcessInfo.numHdrsAdded++ lastMetaHdr = createAndProcessInfo.currMetaHdr @@ -2020,7 +2023,6 @@ func (sp *shardProcessor) createMbsAndProcessCrossShardTransactionsDstMe( sp.hdrsForCurrBlock.hdrHashAndInfo[string(createAndProcessInfo.currMetaHdrHash)] = &hdrInfo{ hdr: createAndProcessInfo.currMetaHdr, usedInBlock: true, - hasProof: true, } createAndProcessInfo.numHdrsAdded++ createAndProcessInfo.hdrAdded = true @@ -2259,7 +2261,7 @@ func (sp *shardProcessor) applyBodyToHeader( return newBody, nil } -func (sp *shardProcessor) waitForMetaHdrHashesAndProofs(waitTime time.Duration) error { +func (sp *shardProcessor) waitForMetaHdrHashes(waitTime time.Duration) error { select { case <-sp.chRcvAllMetaHdrs: return nil