sherlock-audit / 2024-06-allora-judging

0 stars 0 forks source link

LZ_security - The SelectTopNWorkerNonces function lacks a sorting algorithm internally. #96

Open sherlock-admin3 opened 3 months ago

sherlock-admin3 commented 3 months ago

LZ_security

Medium

The SelectTopNWorkerNonces function lacks a sorting algorithm internally.

Summary

The SelectTopNWorkerNonces function lacks a sorting algorithm internally and only selects N workers from the array.

Vulnerability Detail

func SelectTopNWorkerNonces(workerNonces emissions.Nonces, N int) []*emissions.Nonce {
    if len(workerNonces.Nonces) <= N {
        return workerNonces.Nonces
    }
@>  return workerNonces.Nonces[:N]
}

We can see that the function lacks a sorting algorithm and only selects N workers from the array. The SelectTopNWorkerNonces function is used in the requestTopicWorkers function.

func (th *TopicsHandler) requestTopicWorkers(ctx sdk.Context, topic emissionstypes.Topic) {
    Logger(ctx).Debug(fmt.Sprintf("Triggering inference generation for topic: %v metadata: %s default arg: %s. \n",
        topic.Id, topic.Metadata, topic.DefaultArg))

@>  workerNonces, err := th.emissionsKeeper.GetUnfulfilledWorkerNonces(ctx, topic.Id)
    if err != nil {
        Logger(ctx).Error("Error getting worker nonces: " + err.Error())
        return
    }
    // Filter workerNonces to only include those that are within the epoch length
    // This is to avoid requesting inferences for epochs that have already ended
@>  workerNonces = synth.FilterNoncesWithinEpochLength(workerNonces, ctx.BlockHeight(), topic.EpochLength)

    maxRetriesToFulfilNoncesWorker := emissionstypes.DefaultParams().MaxRetriesToFulfilNoncesWorker
    emissionsParams, err := th.emissionsKeeper.GetParams(ctx)
    if err != nil {
        Logger(ctx).Warn(fmt.Sprintf("Error getting max retries to fulfil nonces for worker requests (using default), err: %s", err.Error()))
    } else {
        maxRetriesToFulfilNoncesWorker = emissionsParams.MaxRetriesToFulfilNoncesWorker
    }
@>  sortedWorkerNonces := synth.SelectTopNWorkerNonces(workerNonces, int(maxRetriesToFulfilNoncesWorker))
    Logger(ctx).Debug(fmt.Sprintf("Iterating Top N Worker Nonces: %d", len(sortedWorkerNonces)))
    // iterate over all the worker nonces to find if this is unfulfilled
    for _, nonce := range sortedWorkerNonces {
        nonceCopy := nonce
        Logger(ctx).Debug(fmt.Sprintf("Current Worker block height has been found unfulfilled, requesting inferences %v", nonceCopy))
        go generateInferencesRequest(ctx, topic.InferenceLogic, topic.InferenceMethod, topic.DefaultArg, topic.Id, topic.AllowNegative, *nonceCopy)
    }
}

It can be seen that workerNonces is unsorted data, and after being processed by synth.SelectTopNWorkerNonces, it only selects N workers from the array rather than selecting the latest N workers based on the Nonce.

Impact

It does not select the top N latest worker nonces as intended. Moreover, selecting workers not within the top might result in choosing workers that are already offline or not able to return the desired data correctly.

Code Snippet

https://github.com/sherlock-audit/2024-06-allora/blob/main/allora-chain/x/emissions/keeper/inference_synthesis/nonce_mgmt.go#L51

https://github.com/sherlock-audit/2024-06-allora/blob/main/allora-chain/app/topics_handler.go#L71

Tool used

Manual Review

Recommendation

Add the SortByBlockHeight function within the SelectTopNWorkerNonces function.

sherlock-admin2 commented 2 months ago

The protocol team fixed this issue in the following PRs/commits: https://github.com/allora-network/allora-chain/pull/458