Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ func CreateLastExecutionResultFromPrevHeader(prevHeader data.HeaderHandler, prev
HeaderHash: prevHeaderHash,
HeaderNonce: prevHeader.GetNonce(),
HeaderRound: prevHeader.GetRound(),
HeaderEpoch: prevHeader.GetEpoch(),
RootHash: prevHeader.GetRootHash(),
GasUsed: 0, // we don't have this information in previous header
},
Expand All @@ -472,6 +473,7 @@ func CreateLastExecutionResultFromPrevHeader(prevHeader data.HeaderHandler, prev
HeaderHash: prevHeaderHash,
HeaderNonce: prevMetaHeader.GetNonce(),
HeaderRound: prevMetaHeader.GetRound(),
HeaderEpoch: prevMetaHeader.GetEpoch(),
RootHash: prevMetaHeader.GetRootHash(),
GasUsed: 0, // we don't have this information in previous header
},
Expand Down
7 changes: 4 additions & 3 deletions integrationTests/chainSimulator/staking/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import (
"testing"

"github.com/multiversx/mx-chain-core-go/core"
"github.com/stretchr/testify/require"

chainSimulatorIntegrationTests "github.com/multiversx/mx-chain-go/integrationTests/chainSimulator"
"github.com/multiversx/mx-chain-go/node/chainSimulator/dtos"
chainSimulatorProcess "github.com/multiversx/mx-chain-go/node/chainSimulator/process"
"github.com/multiversx/mx-chain-go/process"
"github.com/multiversx/mx-chain-go/vm"
"github.com/stretchr/testify/require"
)

const (
Expand All @@ -22,7 +23,7 @@ const (
// GasLimitForUnBond the const for the gas limit value for the unBond operation
GasLimitForUnBond = 12_000_000
// MaxNumOfBlockToGenerateWhenExecutingTx the const for the maximum number of block to generate when execute a transaction
MaxNumOfBlockToGenerateWhenExecutingTx = 7
MaxNumOfBlockToGenerateWhenExecutingTx = 10

// QueuedStatus the const for the queued status of a validators
QueuedStatus = "queued"
Expand All @@ -37,7 +38,7 @@ const (
)

var (
//InitialDelegationValue the variable for the initial delegation value
// InitialDelegationValue the variable for the initial delegation value
InitialDelegationValue = big.NewInt(0).Mul(chainSimulatorIntegrationTests.OneEGLD, big.NewInt(1250))
)

Expand Down
2 changes: 2 additions & 0 deletions process/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,7 @@ func CreateLastExecutionResultInfoFromExecutionResult(notarizedInRound uint64, l
HeaderHash: lastExecResult.GetHeaderHash(),
HeaderNonce: lastExecResult.GetHeaderNonce(),
HeaderRound: lastExecResult.GetHeaderRound(),
HeaderEpoch: lastExecResult.GetHeaderEpoch(),
RootHash: lastExecResult.GetRootHash(),
GasUsed: lastExecResult.GetGasUsed(),
},
Expand All @@ -1173,6 +1174,7 @@ func CreateLastExecutionResultInfoFromExecutionResult(notarizedInRound uint64, l
HeaderHash: lastMetaExecResult.GetHeaderHash(),
HeaderNonce: lastMetaExecResult.GetHeaderNonce(),
HeaderRound: lastMetaExecResult.GetHeaderRound(),
HeaderEpoch: lastMetaExecResult.GetHeaderEpoch(),
RootHash: lastMetaExecResult.GetRootHash(),
GasUsed: lastMetaExecResult.GetGasUsed(),
},
Expand Down
25 changes: 24 additions & 1 deletion process/missingData/missingDataResolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ import (
"github.com/multiversx/mx-chain-core-go/core/check"
"github.com/multiversx/mx-chain-core-go/data"
"github.com/multiversx/mx-chain-core-go/data/block"
logger "github.com/multiversx/mx-chain-logger-go"

"github.com/multiversx/mx-chain-go/dataRetriever"
"github.com/multiversx/mx-chain-go/process"
)

const checkMissingDataStep = 10 * time.Millisecond

var log = logger.GetOrCreate("missingDataResolver")

// ResolverArgs holds the arguments needed to create a Resolver
type ResolverArgs struct {
HeadersPool dataRetriever.HeadersPool
Expand Down Expand Up @@ -219,15 +222,35 @@ func (r *Resolver) WaitForMissingData(timeout time.Duration) error {
return haveTime
}

err := r.blockDataRequester.IsDataPreparedForProcessing(stepHaveTime(checkMissingDataStep))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i do not think you need this line. Inside the for, it would be enough.

for {
err := r.blockDataRequester.IsDataPreparedForProcessing(stepHaveTime(checkMissingDataStep))
if r.allDataReceived() && err == nil {
log.Debug("missingDataResolver.WaitForMissingData: all missing data received")
return nil
}

if time.Now().After(waitDeadline) {
r.mutHeaders.RLock()
numMissingHeaders := len(r.missingHeaders)
r.mutHeaders.RUnlock()

r.mutProofs.RLock()
numMissingProofs := len(r.missingProofs)
r.mutProofs.RUnlock()

log.Debug("missingDataResolver.WaitForMissingData: timeout reached while waiting for missing data",
"missingHeaders", numMissingHeaders,
"missingProofs", numMissingProofs,
"IsDataPreparedError", err)

return process.ErrTimeIsOut
}

if err != nil {
err = r.blockDataRequester.IsDataPreparedForProcessing(stepHaveTime(checkMissingDataStep))
}

time.Sleep(checkMissingDataStep)
}
}

Expand Down
20 changes: 20 additions & 0 deletions process/missingData/missingDataResolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,11 @@ func TestResolver_RequestMissingMetaHeadersBlocking(t *testing.T) {
require.Equal(t, process.ErrTimeIsOut, err)
require.False(t, mdr.allHeadersReceived())
require.False(t, mdr.allProofsReceived())

mutRequestedData.Lock()
defer mutRequestedData.Unlock()
require.Equal(t, len(expectedMetaHeadersRequested), len(requestedMetaHeaders))

for i := 0; i < len(expectedMetaHeadersRequested); i++ {
require.Contains(t, requestedMetaHeaders, expectedMetaHeadersRequested[i])
}
Expand Down Expand Up @@ -729,8 +733,14 @@ func TestResolver_RequestMissingShardHeadersBlocking(t *testing.T) {
require.Equal(t, process.ErrTimeIsOut, err)
require.False(t, mdr.allHeadersReceived())
require.False(t, mdr.allProofsReceived())

mutRequestedData.Lock()
require.ElementsMatch(t, expectedShardHeadersRequested, requestedShardHeaders)
mutRequestedData.Unlock()

mutRequestedProofs.Lock()
require.ElementsMatch(t, expectedShardHeadersRequested, requestedShardProofs)
mutRequestedProofs.Unlock()
})

t.Run("requesting missing shard headers with nonce gaps", func(t *testing.T) {
Expand Down Expand Up @@ -864,16 +874,26 @@ func TestResolver_RequestMissingShardHeadersBlocking(t *testing.T) {
require.Equal(t, process.ErrTimeIsOut, err)
require.False(t, mdr.allHeadersReceived())
require.False(t, mdr.allProofsReceived())

mutRequestedData.Lock()
require.ElementsMatch(t, expectedShardHeadersRequested, requestedShardHeaders)
mutRequestedData.Unlock()

mutRequestedProofs.Lock()
require.ElementsMatch(t, expectedShardHeadersRequested, requestedShardProofs)
mutRequestedProofs.Unlock()

mutRequestedProofs.Lock()
require.Len(t, requestProofNonces, 2)
require.ElementsMatch(t, requestProofNonces[1], []uint64{4})
require.ElementsMatch(t, requestProofNonces[2], []uint64{3, 4})
mutRequestedProofs.Unlock()

mutRequestedData.Lock()
require.Len(t, requestShardHeaderNonces, 2)
require.ElementsMatch(t, requestShardHeaderNonces[1], []uint64{4})
require.ElementsMatch(t, requestShardHeaderNonces[2], []uint64{3, 4})
mutRequestedData.Unlock()
})

t.Run("request missing shard headers and proofs, all received", func(t *testing.T) {
Expand Down
93 changes: 58 additions & 35 deletions process/sync/baseSync.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ import (
"github.com/multiversx/mx-chain-core-go/data/typeConverters"
"github.com/multiversx/mx-chain-core-go/hashing"
"github.com/multiversx/mx-chain-core-go/marshal"
"github.com/multiversx/mx-chain-go/process/asyncExecution/queue"
logger "github.com/multiversx/mx-chain-logger-go"

"github.com/multiversx/mx-chain-go/process/asyncExecution/queue"

"github.com/multiversx/mx-chain-go/common"
"github.com/multiversx/mx-chain-go/consensus"
"github.com/multiversx/mx-chain-go/dataRetriever"
Expand Down Expand Up @@ -1051,42 +1052,12 @@ func (boot *baseBootstrap) prepareForSyncIfNeeded(syncingNonce uint64) error {

currentHeader := boot.getCurrentBlock()
currentHeaderHash := boot.getCurrentBlockHash()
lastExecResult, err := process.GetPrevBlockLastExecutionResult(boot.chainHandler)
if err != nil {
return err
}

lastExecResultsHandler, err := common.ExtractBaseExecutionResultHandler(lastExecResult)
lastExecutionResultHeaderNonce, err := boot.getExecutionResultHeaderNonceForSyncStart(syncingNonce, currentHeader, currentHeaderHash)
if err != nil {
return err
}

log.Debug("prepareForSyncIfNeeded",
"syncingNonce", syncingNonce,
"currHeader nonce", currentHeader.GetNonce(),
"currHeader hash", currentHeaderHash,
"lastExecRes nonce", lastExecResultsHandler.GetHeaderNonce(),
"lastExecRes hash", lastExecResultsHandler.GetHeaderHash(),
"lastExecRes rootHash", lastExecResultsHandler.GetRootHash(),
)

lastExecutedHash := lastExecResultsHandler.GetHeaderHash()
lastExecutedHeader, err := boot.getHeader(lastExecutedHash)
if err != nil {
return err
}

rootHash := lastExecResultsHandler.GetRootHash()

txPool := boot.poolsHolder.Transactions()
err = txPool.OnExecutedBlock(lastExecutedHeader, rootHash)
if err != nil {
txPool.ResetTracker()
return err
}

lastExecutedNonce := lastExecutedHeader.GetNonce()
if syncingNonce == lastExecutedNonce+2 {
if syncingNonce == lastExecutionResultHeaderNonce+2 {
// the ideal/most common case:
// the previous block was already processed, its nonce should have been syncingNonce-1,
// and it notarized its previous block, which should have had nonce equal to syncingNonce-2
Expand Down Expand Up @@ -1120,7 +1091,7 @@ func (boot *baseBootstrap) prepareForSyncIfNeeded(syncingNonce uint64) error {

// if there are multiple headers in between the syncing header and the last one executed,
// add them into the queue and pool
for i := lastExecutedNonce + 1; i < syncingNonce; i++ {
for i := lastExecutionResultHeaderNonce + 1; i < syncingNonce; i++ {
hdr, hdrHash, errGetHdr := boot.getHeaderFromPoolWithNonce(i)
if errGetHdr != nil {
log.Debug("prepareForSyncIfNeeded: failed to get header with nonce", "nonce", i, "error", errGetHdr)
Expand All @@ -1132,7 +1103,7 @@ func (boot *baseBootstrap) prepareForSyncIfNeeded(syncingNonce uint64) error {
return errGetBody
}

err := boot.saveProposedTxsToPool(hdr, body)
err = boot.saveProposedTxsToPool(hdr, body)
if err != nil {
return err
}
Expand Down Expand Up @@ -1160,6 +1131,58 @@ func (boot *baseBootstrap) prepareForSyncIfNeeded(syncingNonce uint64) error {
return nil
}

func (boot *baseBootstrap) getExecutionResultHeaderNonceForSyncStart(
syncingNonce uint64,
currentHeader data.HeaderHandler,
currentHeaderHash []byte,
) (uint64, error) {
Comment on lines +1134 to +1138
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new function getExecutionResultHeaderNonceForSyncStart lacks documentation. Consider adding a comment that explains its purpose, parameters, and return value. For example, it should explain that it retrieves the header nonce of the most recent execution result available, either from the last notarized result or from a more recent execution result if available.

Copilot uses AI. Check for mistakes.
lastNotarizedExecResult, err := process.GetPrevBlockLastExecutionResult(boot.chainHandler)
if err != nil {
return 0, err
}

lastNotarizedExecResultsHandler, err := common.ExtractBaseExecutionResultHandler(lastNotarizedExecResult)
if err != nil {
return 0, err
}

log.Debug("getExecutionResultHeaderNonceForSyncStart",
"syncingNonce", syncingNonce,
"currHeader nonce", currentHeader.GetNonce(),
"currHeader hash", currentHeaderHash,
"lastNotarizedExecRes nonce", lastNotarizedExecResultsHandler.GetHeaderNonce(),
"lastNotarizedExecRes hash", lastNotarizedExecResultsHandler.GetHeaderHash(),
"lastNotarizedExecRes rootHash", lastNotarizedExecResultsHandler.GetRootHash(),
)

lastNotarizedExecutedHash := lastNotarizedExecResultsHandler.GetHeaderHash()
lastNotarizedExecutedHeader, err := boot.getHeader(lastNotarizedExecutedHash)
if err != nil {
return 0, err
}

rootHash := lastNotarizedExecResultsHandler.GetRootHash()

txPool := boot.poolsHolder.Transactions()
err = txPool.OnExecutedBlock(lastNotarizedExecutedHeader, rootHash)
if err != nil {
txPool.ResetTracker()
return 0, err
}

lastExecutionResultNonce := lastNotarizedExecutedHeader.GetNonce()

// in case there is a more recent execution result available, use it
lastExecutionResult := boot.chainHandler.GetLastExecutionResult()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there any chance that this happens right before we call txPool.OnExecutedBlock on L1167? would that be an ambiguous case where tx pool has last executed different than what this method returns?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that can happen.

The onExecute needs to be done with the latest notarized execution result not with the last available execution result. The last notarized execution result cannot change for the node if the node is not committing a new block that has passed consensus, either on the consensus flow, or on the sync flow.

This here is preceding the sync flow.

if !check.IfNil(lastExecutionResult) && lastExecutionResult.GetHeaderNonce() > lastExecutionResultNonce {
lastExecutionResultNonce = lastExecutionResult.GetHeaderNonce()
}

log.Debug("getExecutionResultHeaderNonceForSyncStart", "lastExecutionResultNonce", lastExecutionResultNonce)

return lastExecutionResultNonce, nil
Comment on lines +1173 to +1183
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable name lastExecutionResultNonce is ambiguous because it starts as the nonce from the notarized execution result but may be updated to a more recent execution result. Consider renaming to something clearer like executionResultHeaderNonce or documenting what this variable represents after line 1178.

Suggested change
lastExecutionResultNonce := lastNotarizedExecutedHeader.GetNonce()
// in case there is a more recent execution result available, use it
lastExecutionResult := boot.chainHandler.GetLastExecutionResult()
if !check.IfNil(lastExecutionResult) && lastExecutionResult.GetHeaderNonce() > lastExecutionResultNonce {
lastExecutionResultNonce = lastExecutionResult.GetHeaderNonce()
}
log.Debug("getExecutionResultHeaderNonceForSyncStart", "lastExecutionResultNonce", lastExecutionResultNonce)
return lastExecutionResultNonce, nil
// executionResultHeaderNonce starts from the last notarized executed header
// and may be updated to a more recent execution result header nonce if available.
executionResultHeaderNonce := lastNotarizedExecutedHeader.GetNonce()
// in case there is a more recent execution result available, use it
lastExecutionResult := boot.chainHandler.GetLastExecutionResult()
if !check.IfNil(lastExecutionResult) && lastExecutionResult.GetHeaderNonce() > executionResultHeaderNonce {
executionResultHeaderNonce = lastExecutionResult.GetHeaderNonce()
}
log.Debug("getExecutionResultHeaderNonceForSyncStart", "executionResultHeaderNonce", executionResultHeaderNonce)
return executionResultHeaderNonce, nil

Copilot uses AI. Check for mistakes.
}

func (boot *baseBootstrap) saveProposedTxsToPool(
header data.HeaderHandler,
body data.BodyHandler,
Expand Down
Loading