Skip to content

fix(address): reject the 25-byte checksum error below the base58 fork - #1406

Merged
33cn merged 3 commits into
33cn:masterfrom
bysomeone:fix-address-checksum-gate
Oct 8, 2026
Merged

33cn merged 3 commits into
33cn:masterfrom
bysomeone:fix-address-checksum-gate

Conversation

@bysomeone

@bysomeone bysomeone commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

What was wrong

DriverBase.CheckAddress tolerates some address errors below the fork heights so historical
blocks still replay. #1401 added ErrCheckChecksum to the ForkBase58AddressCheck gate:

if !cfg.IsFork(height, "ForkBase58AddressCheck") &&
    (err == address.ErrAddressChecksum || err == address.ErrCheckChecksum) {
    return nil
}

That tolerance costs a replay divergence. bityuan mainnet block 101641 contains a coins transfer
to the 25-byte address 1Di16bUjPJnvZ8Hrf4vuQDffzkv9jC5Jp, whose checksum does not match; the
chain packed it as ExecPack with Address Checksum error. A node syncing bityuan from genesis
on v1.72.3 stalls there: the replay accepts a transfer the chain executed as a failure, so the
state root diverges.

Why that tolerance does not apply here

#1401 added it because a 25-byte address could come back as ErrCheckVersion from a driver that
expects another version byte — tolerated below ForkMultiSignAddress — and would otherwise flip
from "sometimes accepted" to "always rejected" once the driver walk became deterministic. That
requires an enabled driver reporting ErrCheckVersion for the address. On bityuan, below
ForkMultiSignAddress, btcMultiSign is not enabled:

btcMultiSign enable height ForkMultiSignAddress
bityuan 2270000 2270000
chain33 defaults 0 1298600

Measured on block 101641's address, 20,000 driver walks below the fork: two possible answers,
ErrCheckChecksum (37.6%) and ErrAddressType (62.4%) — ErrCheckVersion never appeared. So the
tolerance protects no block on this chain; its only effect is to accept the one transaction the
chain rejected.

What this changes for other chains

For a chain that does enable btcMultiSign below ForkMultiSignAddress — the chain33 default
shape, where the window is [0, 1298600) — the tolerated verdict was never stable to begin with:
Go re-randomises the map walk on every call, so a block minted between v1.67.0 and v1.72.2 that
depended on it recorded whichever branch that one draw produced. Measured on that shape, 18.8% of
walks accept and 81.2% reject. This PR moves such chains from "sometimes accepted" to
"deterministically rejected", which is the majority branch; the alternative matches the minority.
No chain is known to have a block that depends on the accepting branch.

Once the walk is deterministic a 25-byte address can only report ErrCheckChecksum:
CheckBase58Address cannot report ErrAddressChecksum for one, because both checksum
computations are byte-equivalent sha256d and the 25-byte branch returns first.

Changes

  • system/dapp/driver.go — drop ErrCheckChecksum from the ForkBase58AddressCheck gate, and
    replace the comment explaining the old tolerance with the reasoning above.
  • system/dapp/driver_test.go — the test that asserted the 25-byte case was tolerated below the
    fork now asserts it is rejected, using the address from the on-chain block instead of a
    synthetically corrupted one.

Verification

A node running this change synced bityuan mainnet from genesis past the fork heights without
diverging. The ExecPack transaction above is the one that previously forced the replay to
diverge at that block; with the change it replays as the failure the chain recorded.

Relationship to #1401

This reverts the system/dapp/driver.go half of #1401 (566304371, released in v1.72.3). The
rest of that PR — the deterministic driver walk and the address cache key — is what makes a
from-genesis replay work at all, and is untouched here. A chain on v1.72.3 or later needs this
change to replay bityuan from genesis; bityuan itself pins chain33 v1.72.2 today, so the
regression only bites once that dependency moves.

The gate below ForkBase58AddressCheck tolerated ErrCheckChecksum as well as
ErrAddressChecksum, which accepts transactions the chain packed as failures.
Block 101641 carries one: a coins transfer to the 25-byte address
1Di16bUjPJnvZ8Hrf4vuQDffzkv9jC5Jp whose checksum does not match, executed as
ExecPack with "Address Checksum error". A replay that accepts it runs the
transfer -- the coins transfer path no longer validates the recipient's
checksum -- so the computed state root stops matching and a node syncing from
scratch stalls at 101641.

Tolerating ErrCheckChecksum was meant to keep 25-byte addresses from flipping
from "sometimes accepted" to "always rejected" once the drivers were visited in
a fixed order. That flip cannot happen at the heights below ForkMultiSignAddress
where this gate matters: only the btc and utxo drivers are enabled there, and
neither reports ErrCheckVersion, so the legacy form was always rejected. From
ForkMultiSignAddress up, ErrCheckVersion is what the gate above already
tolerates, which is what the on-chain blocks relied on.

Co-authored-by: Claude <[email protected]>
bysomeone and others added 2 commits October 7, 2026 17:19
The gate's two fork heights are separate knobs and are equal only on chains that say
so -- bityuan sets both to 2270000, the chain33 defaults are 1298600 and 1800000 -- so
a test that sets them to the same value cannot see the window between them. Inside
[ForkMultiSignAddress, ForkBase58AddressCheck) the version error stops being tolerated
while the checksum error still is, so the legacy form at 546820 is rejected there:
that is the behaviour change 33cn#1401 disclosed and left in place. The 25-byte checksum
failure does not depend on ForkMultiSignAddress and is rejected at every height.

The 25-byte assertions fail if ErrCheckChecksum is tolerated again below the fork.

Co-authored-by: Claude <[email protected]>
@33cn
33cn merged commit f3821b4 into 33cn:master Oct 8, 2026
13 checks passed
@33cn

33cn commented Oct 8, 2026

Copy link
Copy Markdown
Owner

🎉 This PR is included in version 1.72.5 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@33cn 33cn added the released label Oct 8, 2026
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.

2 participants