Skip to content

[BUG] VoteBlame crashes when VoteOnBallot fails #4526

@ngapaxs

Description

@ngapaxs

ZetaChain Node - Bug Report

Repo: https://github.com/zeta-chain/node
Version: v37.0.0
Commit: 78e1a91
Date: 2026-01-11


Bug 1: VoteBlame crashes when VoteOnBallot fails

Severity: HIGH
File: x/observer/keeper/msg_server_vote_blame.go
Lines: 32-44

The Problem

Found a nil pointer bug in the blame voting handler. When VoteOnBallot returns an error, the code still tries to read ballot.BallotIdentifier for the error message. But ballot is nil at that point, so it panics.

Basically the error handling code itself causes a crash. Not great.

Why This Matters

  • Node goes down if this gets triggered during blame voting
  • Multiple validators hitting this = bad time for the network
  • Any observer can trigger it by voting when conditions make VoteOnBallot fail

The Code

// x/observer/keeper/msg_server_vote_blame.go:32-44
ballot, isFinalized, isNew, err := k.VoteOnBallot(
    ctx,
    observationChain,
    msg.Digest(),
    types.ObservationType_TSSKeySign,
    msg.Creator,
    types.VoteType_SuccessObservation,
)
if err != nil {
    return nil, sdkerrors.Wrapf(
        err,
        "%s, BallotIdentifier %v", voteBlameID, ballot.BallotIdentifier)  // boom - ballot is nil
}

What Happens

panic: runtime error: invalid memory address or nil pointer dereference

Fix

Just check if ballot exists before using it:

if err != nil {
    if ballot != nil {
        return nil, sdkerrors.Wrapf(err, "%s, BallotIdentifier %v", voteBlameID, ballot.BallotIdentifier)
    }
    return nil, sdkerrors.Wrapf(err, "%s, failed to vote on ballot", voteBlameID)
}

Link

https://github.com/zeta-chain/node/blob/78e1a91/x/observer/keeper/msg_server_vote_blame.go#L32-L44


Bug 2: Wrong order of nil check in ZRC20 functions

Severity: MEDIUM
File: x/fungible/keeper/zrc20_cosmos_coins_mapping.go
Lines: 96, 127

The Problem

Two functions check amount.Sign() before checking if amount is nil. Go evaluates left to right, so if you pass nil it crashes before the nil check even runs.

if amount.Sign() <= 0 || amount == nil {  // wrong - crashes on nil

Should be:

if amount == nil || amount.Sign() <= 0 {  // right - nil check first

Why This Matters

These are exported functions (CheckZRC20Allowance, CheckZRC20Balance). Right now nothing external calls them with nil, but if someone adds a precompile or new feature that does, the node crashes.

It's a bug waiting to happen.

The Code

// line 96
func (k Keeper) CheckZRC20Allowance(..., amount *big.Int) error {
    if amount.Sign() <= 0 || amount == nil {  // panic on nil
        return fungibletypes.ErrInvalidAmount
    }
}

// line 127  
func (k Keeper) CheckZRC20Balance(..., amount *big.Int) error {
    if amount.Sign() <= 0 || amount == nil {  // same issue
        return fungibletypes.ErrInvalidAmount
    }
}

Fix

Flip the order:

if amount == nil || amount.Sign() <= 0 {
    return fungibletypes.ErrInvalidAmount
}

Links


Bug 3: Feature flag function crashes on bad context

Severity: LOW
File: zetaclient/context/feature_flags.go
Lines: 10-14

The Problem

When FromContext fails, app is nil. But then the code tries to log with app.logger. Crash.

func EnableMultipleCallsFeatureFlag(ctx context.Context) bool {
    app, err := FromContext(ctx)
    if err != nil {
        app.logger.Warn().Err(err)...  // app is nil here
        return false
    }
}

Why This Matters

Not consensus-critical since it's zetaclient code, but it can crash the client if context is messed up.

Fix

Don't try to use app when it's nil:

if err != nil {
    return false  // just return, can't log without app
}

Link

https://github.com/zeta-chain/node/blob/78e1a91/zetaclient/context/feature_flags.go#L10-L14

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions