linera-io/linera-protocol · error · ChainError

The signature was not created by a valid entity

Error message

The signature was not created by a valid entity

What it means

Thrown by SignatureAggregator::append when the signature's public key has zero voting weight in the aggregator's committee, i.e. the signer is not a member of the current committee (ChainError::InvalidSigner). The aggregator only counts signatures from validators with voting rights in the committee it was constructed with. A signature from an unknown or retired validator can never contribute to the quorum, so append rejects it immediately.

Source

Thrown at linera-chain/src/data_types/mod.rs:1118

    {
        let hash_and_round = VoteValue(
            self.partial.hash(),
            self.partial.round,
            T::KIND,
            self.partial.unlocking_round(),
            self.partial.first_round(),
            self.partial.justification_commitment(),
        );
        signature.check(&hash_and_round, public_key)?;
        // Check that each validator only appears once.
        ensure!(
            !self.used_validators.contains(&public_key),
            ChainError::CertificateValidatorReuse
        );
        self.used_validators.insert(public_key);
        // Update weight.
        let voting_rights = self.committee.weight(&public_key);
        ensure!(voting_rights > 0, ChainError::InvalidSigner);
        self.weight += voting_rights;
        // Update certificate.
        self.partial.add_signature((public_key, signature));

        if self.weight >= self.committee.quorum_threshold() {
            self.weight = 0; // Prevent from creating the certificate twice.
            Ok(Some(self.partial.clone()))
        } else {
            Ok(None)
        }
    }
}

// Checks if the array slice is strictly ordered. That means that if the array
// has duplicates, this will return False, even if the array is sorted
pub(crate) fn is_strictly_ordered(values: &[(ValidatorPublicKey, ValidatorSignature)]) -> bool {
    values.windows(2).all(|pair| pair[0].0 < pair[1].0)
}

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Verify committee.weight(&public_key) > 0 before calling append, and skip/drop the vote when it is 0.
  2. Check that the aggregator and the votes come from the same epoch — rebuild the aggregator with the committee of the epoch the votes were cast in.
  3. If you aggregate votes received over the network, filter them against the current committee at receipt time so stale validators never reach the aggregator.
  4. In tests, make sure the signing keys are taken from the same Committee builder used to construct the aggregator.

Example fix

// before
for (key, sig) in votes {
    if let Some(cert) = aggregator.append(key, sig)? {
        return Ok(cert);
    }
}

// after
for (key, sig) in votes {
    if committee.weight(&key) == 0 {
        continue; // not a member of this committee; skip
    }
    if let Some(cert) = aggregator.append(key, sig)? {
        return Ok(cert);
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// before aggregating a vote
if committee.weight(&public_key) == 0 {
    tracing::warn!(?public_key, "dropping vote from non-committee validator");
    continue;
}

Try / catch

match aggregator.append(key, sig) {
    Ok(Some(cert)) => return Ok(cert),
    Ok(None) => {}
    Err(ChainError::InvalidSigner) => continue, // skip non-committee voter
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling SignatureAggregator::append(public_key, signature) where committee.weight(&public_key) == 0. Happens when votes are collected from validators of a different epoch: e.g. a worker still holds votes signed under an old committee while the aggregator was built with the new one, or the operator feed maps a stale validator key set to a new committee.

Common situations: Epoch/committee rotations where in-flight votes from the outgoing committee reach the aggregator of the incoming one; misconfigured validator sets between nodes; test code generating signatures with keys never registered in the committee.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/2a9f624dde69019d. Report an issue: GitHub.