Skip to content

BitcoinZ node validation issue around block 1789967 #118

Description

@zalpader

Node version:
BitcoinZ v2.2.0-e90047d4ae65

Problem:
During sync/reindex the node can fail at block 1789967:
Here's a snapshot of the blockchain where this issue occurs: https://archive.org/download/snapshot.7z/snapshot.7z

2026-07-04 02:44:17 UpdateTip: new best=000002ac0515f0e34b3692f6aec476a468c1730563c161fc32e722afd95fcaab height=1789966 bits=503917374 log2_work=47.335383 tx=4684503 date=2026-06-20 15:28:09 progress=0.981079 cache=2.0MiB(4758tx)
2026-07-04 02:44:17 ERROR: CheckTransaction(): joinsplit does not verify
2026-07-04 02:44:17 ERROR: CheckBlock: CheckTransaction failed
2026-07-04 02:44:17 Misbehaving: 51.222.50.26:1989 (0 -> 100) BAN THRESHOLD EXCEEDED
2026-07-04 02:44:17 InvalidChainFound: invalid block=0000002e04e291438a422e2d5e9d6d67ac87059afefeb03136ff9f21711bb359 height=1789967 log2_work=47.335383 date=2026-06-20 15:29:41
2026-07-04 02:44:17 InvalidChainFound: current best=000002ac0515f0e34b3692f6aec476a468c1730563c161fc32e722afd95fcaab height=1789966 log2_work=47.335383 date=2026-06-20 15:28:09
2026-07-04 02:44:17 ERROR: ConnectTip(): ConnectBlock 0000002e04e291438a422e2d5e9d6d67ac87059afefeb03136ff9f21711bb359 failed
2026-07-04 02:44:17 InvalidChainFound: invalid block=0000002e04e291438a422e2d5e9d6d67ac87059afefeb03136ff9f21711bb359 height=1789967 log2_work=47.335383 date=2026-06-20 15:29:41
2026-07-04 02:44:17 InvalidChainFound: current best=000002ac0515f0e34b3692f6aec476a468c1730563c161fc32e722afd95fcaab height=1789966 log2_work=47.335383 date=2026-06-20 15:28:09

The same block hash was fetched directly from multiple peers via P2P getdata, and the raw block payload was identical from all tested peers:

152.53.180.255:1989
51.222.50.26:1989
109.199.122.150:1989

Payload:
size: 2312 bytes
tx count: 2
raw payload sha256:
ef224f10e4976746f7ce2a7fcf1ebc90b8cefcac6dec9d870d16bd74132cc741

AI Debug:

Suspicious code path:
ProcessNewBlock() runs preliminary CheckBlock() with ProofVerifier::Disabled():

src/main.cpp:
auto verifier = ProofVerifier::Disabled();
bool checked = CheckBlock(*pblock, state, chainparams, verifier);

CheckBlock() can then set block.fChecked = true:

if (fCheckPOW && fCheckMerkleRoot)
    block.fChecked = true;

Later, the same in-memory pblock can be passed into ActivateBestChain()/ConnectTip().
ConnectBlock() calls CheckBlock() with ProofVerifier::Strict(), but CheckBlock() returns early if block.fChecked is already true:

if (block.fChecked)
    return true;

This means live sync may skip Sprout JoinSplit proof verification for the in-memory block, while reindex/VerifyDB reads the block from disk with fChecked=false and then fails strict verification.

Possible minimal fix:
Only cache block.fChecked when proof verification was actually performed.

Example:

In proof_verifier.h:

bool PerformsVerification() const { return perform_verification; }

In main.cpp:

if (fCheckPOW && fCheckMerkleRoot && verifier.PerformsVerification())
    block.fChecked = true;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions