Skip to content

core/vm: reject duplicate bridge validators at Pasteur#3623

Open
MatusKysel wants to merge 5 commits into
developfrom
fix/mendel-duplicate-validators
Open

core/vm: reject duplicate bridge validators at Pasteur#3623
MatusKysel wants to merge 5 commits into
developfrom
fix/mendel-duplicate-validators

Conversation

@MatusKysel
Copy link
Copy Markdown
Contributor

@MatusKysel MatusKysel commented Mar 26, 2026

Summary

  • add Pasteur-specific precompile variants for BLS signature verification (0x66) and CometBFT light block validation (0x67)
  • reject duplicate bridge validator identities at Pasteur for both BLS pubkey aggregation inputs and light-client validator sets
  • add regression coverage for Pasteur precompile selection and duplicate-validator rejection paths

Details

This change introduces Pasteur-only behavior for the bridge validator precompiles.

For BLS signature verification, the Pasteur variant rejects inputs that repeat the same validator pubkey in the aggregated signer set. Pre-Pasteur behavior is preserved.

For CometBFT light block validation, the Pasteur variant validates that validator sets do not contain duplicate identities. The validation covers validator address, consensus pubkey, BLS key, and relayer
address, and is applied when decoding both the trusted consensus state and the incoming light block validator set.

Why

Duplicate bridge validators can allow malformed validator sets or signer lists to bypass uniqueness assumptions in bridge verification logic. Pasteur now enforces uniqueness at the precompile boundary instead
of relying on callers to sanitize inputs.

annielz
annielz previously approved these changes Mar 27, 2026

func (t validatorDuplicateTracker) check(idx int, value []byte) error {
key := string(value)
if firstIdx, ok := t.seen[key]; ok {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

are bls key and RelayerAddress allowed to stay unset? represent by zero value

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes. Those two fields are allowed to be unset upstream. In our fixed-width decode path, "unset" is represented as zero-filled bytes, so I treat empty/nil and all-zero as the same unset state and exclude only
that case from duplicate detection.

// input:
// | chainID | height | nextValidatorSetHash | [{validator pubkey, voting power, relayer address, relayer bls pubkey}] |
// | 32 bytes | 8 bytes | 32 bytes | [{32 bytes, 8 bytes, 20 bytes, 48 bytes}] |
func DecodeConsensusState(input []byte) (ConsensusState, error) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

how about

  1. func DecodeConsensusState(input []byte) (ConsensusState, error)
    --> func DecodeConsensusState(input []byte, requireUniqueValidators bool) (ConsensusState, error)
  2. delete DecodeConsensusStateWithValidation

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

// input:
// consensus state length | consensus state | light block |
// 32 bytes | | |
func DecodeLightBlockValidationInput(input []byte) (*ConsensusState, *types.LightBlock, error) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

how about

  1. func DecodeLightBlockValidationInput(input []byte) (*ConsensusState, *types.LightBlock, error)
    --> func DecodeLightBlockValidationInput(input []byte, requireUniqueValidators bool) (*ConsensusState, *types.LightBlock, error)
  2. delete DecodeLightBlockValidationInputWithValidation

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

Comment thread core/vm/contracts_lightclient.go Outdated
return "COMET_BFT_LIGHT_BLOCK_VALIDATE_HERTZ"
}

type cometBFTLightBlockValidateHertzMendel struct {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

how about
cometBFTLightBlockValidateHertzMendel --> cometBFTLightBlockValidateMendel?
because when Mendel enabled, Hertz must be already enalbed

@allformless
Copy link
Copy Markdown
Collaborator

delay to Pasteur HF after discussion

@allformless allformless changed the title core/vm: reject duplicate bridge validators at Mendel core/vm: reject duplicate bridge validators at Pasteur Apr 1, 2026
@zlacfzy
Copy link
Copy Markdown
Contributor

zlacfzy commented Apr 15, 2026

0x66 (blsSignatureVerify) is left unchanged — it is a generic BLS primitive and does not require unique pubkeys.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants