Skip to content

fix(token): check global symbol uniqueness in finishCreate to prevent duplicate mint - #1312

Open
33cn wants to merge 2 commits into
masterfrom
fix-token-duplicate-finishcreate
Open

fix(token): check global symbol uniqueness in finishCreate to prevent duplicate mint#1312
33cn wants to merge 2 commits into
masterfrom
fix-token-duplicate-finishcreate

Conversation

@33cn

@33cn 33cn commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Vulnerability

Duplicate finishCreate of the same token symbol causes over-issuance (N x Total minted).

  • Location: plugin/dapp/token/executor/tokendb.go finishCreate (~line 242)

Root cause

finishCreate only looked up the per-owner record keyed by (symbol, owner) and checked its status. Unlike preCreate, which rejects a symbol whose global record mavl-token-<symbol> already exists (checkTokenExist, tokendb.go:182), finishCreate had no global symbol uniqueness check.

As a result, the same symbol could be precreated by N different owners (per-owner records, first-finisher-wins flow), and every owner could then be finished: each finishCreate ran GenesisInit(owner, Total), so a token declaring Total supply was actually issued N x Total.

Fix

In finishCreate, after the per-owner precreated-status check, add a global uniqueness check: if the global record mavl-token-<symbol> already exists (written by the first successful finish), reject with ErrTokenExist.

preCreate is intentionally left per-owner:

  • preCreate-after-finish is already blocked globally by checkTokenExist (tokendb.go:182).
  • Multiple owners precreating the same symbol before any finish is a valid existing usage (per-owner records); the first approved finish now wins and any later duplicate finish is rejected. Changing preCreate to global dedup would break this flow and is not needed to close the over-issuance.

Fork gating

The behavior change is gated by a new dapp fork ForkTokenFinishCheck (token/types, registered at height 0, following the ForkEVMFixOverflow pattern). Before the fork height the old behavior is preserved to avoid breaking consensus of running chains. Config lines are added under [fork.sub.token] in chain33.fork.toml, chain33.para.toml, plugin/dapp/evm/cmd/ci2/chain33.proxyminer.toml and plugin/dapp/ticket/executor/testdata/chain33.cfg.toml.

Tests

New file plugin/dapp/token/executor/token_finishcheck_test.go (helpers uniquely prefixed with dupFinish to avoid redefinition when merging with other PRs):

  • TestTokenDupFinishCreateRejected: with the fork active, two owners precreate the same symbol; the first finish succeeds; the duplicate finish is rejected with ErrTokenExist; repeated same-owner finish still fails with ErrTokenNotPrecreated; only the first owner holds Total, no over-issuance.
  • TestTokenDupFinishCreatePreFork: below the fork height the old behavior is unchanged (duplicate finish succeeds).
  • Normal precreate/finish flow is unaffected (also covered by existing TestToken/TestFinish).

go test -ldflags=-checklinkname=0 -count=1 ./plugin/dapp/token/... all pass; ./plugin/dapp/ticket/... (testdata toml touched) all pass.

king and others added 2 commits August 31, 2026 11:41
… duplicate mint

finishCreate only looked up the per-owner (symbol, owner) record, so the
same symbol precreated by N different owners could be finished N times and
each finish triggered GenesisInit, over-issuing N*Total of the same token.

Add a global symbol uniqueness check in finishCreate (the mavl-token-<symbol>
record already written by the first finish), gated by the new dapp fork
ForkTokenFinishCheck (registered at height 0). Pre-fork behavior is unchanged.

preCreate is intentionally left per-owner: the global key only exists after a
finish (already guarded by checkTokenExist in preCreate), and multiple owners
precreating the same symbol before any finish remains a valid first-finisher-
wins flow.
…ss check

finishCreate 的全局 symbol 唯一性修复需要经过真实链路径验证,因为直接调用
exec.Exec 与 testnode 的 mempool -> 共识 -> executor 完整链路行为不同。

新增 testnode 端到端用例:
- fork 生效后,同一 symbol 第二次 finishCreate 被拒绝,仅第一个 owner 持有 Total;
- fork 高度之前,旧行为保留(重复 finish 成功,实际发行 2*Total,超发对照)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant