docs: Add design documentation for Multi Native Token (MNT) integration#11
docs: Add design documentation for Multi Native Token (MNT) integration#11ping-ke wants to merge 10 commits into
Conversation
- goshard-mnt-design.md: full integration design for goshard including StateAccount extension, TokenBalances, EVM Context changes, 5 MNT precompiles, system contracts, journal support, and testing strategy - goqkc-mnt-design.md: existing goquarkchain MNT design reference (Account struct, TokenBalances types, precompile interfaces, transaction flow examples, EVM dispatch mechanism) - mainnet-mnt-txs.md: real mainnet transactions with non-QKC transfer_token_id (QKCUP, tokenID=46347397) confirming TransferTokenID requirement Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- §5.2/§7.11: GasTokenID/TransferTokenID moved to TxContext (not BlockContext); update all code snippets, function signatures, and summary table - §5.4/§7.11: checkTokenIDQueried condition updated to != 0 && != types.DefaultTokenID - §7.4: remove EncodeToByte32 (only needed for trie format, not implemented); renumber §7.5–§7.15 → §7.4–§7.13 - §7.13/§13: Genesis MNT alloc marked out of scope - Replace defaultMntTokenID with types.DefaultTokenID throughout Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Rename token_balances.go → token.go - Add state_account_qkc.go (New) and gen_account_rlp.go (Deleted) - Remove reader.go and transaction.go (not actually modified) - Fix duplicate contracts_qkc.go entry - Add core/evm.go; update line counts to match actual diff Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Fix typo: rename Optial to Optional in goshard and goqkc design docs - Clarify FullShardKey rationale: determined by creating tx, not derivable - Remove historical comparison phrases (no longer, originally, etc.) - Remove goshard simplification paragraph about trie format dependencies - Fix test case matrix: remove FullShardKey column, correct cases 1 and 5 - Case 1: QKC=0 + nil MntBalances → 0x80 (not QKC-only with balance) - Case 5: >16 tokens returns encode error (trie format not supported) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
mnt-design re-review — verdict: materially closer, still blocked One real prior blocker fixed; the two root-divergence issues remain, plus a regression and a subtle new ✅ Fixed (verified vs pyquarkchain)
⛔ Blocker — empty() still NOT addressed IsBlankMnt() is defined (doc line 592) but never wired into empty() (no empty()/EIP-158 reference anywhere in §7.11.5 (the "Full Implementation") does the move twice: lines 1094-1096 SubMntBalance(caller) + 🆕 New encoding bug — empty TokenBalances (subtle but real) The new EncodeRLP switch maps MntBalances != nil && IsBlank() → 0x8200c0. But pyquarkchain Still-open majors (unchanged; the doc defers them)
|
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Rename token_balances.go → token.go throughout - Remove trie-format fields (db, tokenTrie, Commit, EncodeRLP, DecodeRLP) from TokenBalances design; document balances-only-stores-nonzero invariant - Replace "import cycle" rationale with "unseen on mainnet" for trie format limitation (§4.3 and §7.1) - Fix §9.3 and §16 transferMnt flow: precompile does not Sub/Add directly; single transfer is performed inside evm.Call via Transfer routing Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Message struct (with GasTokenID/TransferTokenID) lives in core/state_transition.go, not core/types/transaction.go. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Resolved:
No Need to Resolved
|
| ```go | ||
| // core/vm/evm.go — inside Call(), after contract execution | ||
| if err == nil && len(contract.Code) != 0 && !contract.TokenIDQueried && | ||
| evm.TxContext.TransferTokenID != 0 && evm.TxContext.TransferTokenID != types.DefaultTokenID && |
There was a problem hiding this comment.
pyquarkchain supports token_id == 0, so technically evm.TxContext.TransferTokenID != 0 here can cause a fork, and so do other != 0 guards.
…imAccount, remove bogus tokenID check Comment 1 (line 901): activePrecompiledContracts was merging MNT precompiles unconditionally — history replay of blocks before the MNT activation timestamp would fork. Fix: gate the merge behind rules.IsQKCMNT, backed by a new QKCMNTTime *uint64 field in ChainConfig (same pattern as ShanghaiTime/CancunTime). Add implementation steps to Phase 3 list. Comment 2 (line 1265): Snapshot tree was incorrectly listed under §10 "What Does NOT Change". Fix: remove from that list; note that SlimAccount must be extended with MntBalances and FullShardKey (both rlp:"optional") and that flatReader.Account must copy those fields. Add to Phase 2 implementation notes. Comment 3 (line 834): transferMnt flow listed a 'tokenID ≠ QKC tokenID' validation step that does not exist in goquarkchain transferMnt.Run. Fix: remove step 4, re-number, update the 'Identical to goquarkchain' prose to clarify the routing behaviour instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| } | ||
|
|
||
| evm.StateDB.AddMntBalance(recipient, amount, tokenID) | ||
| return nil, nil |
There was a problem hiding this comment.
The existing goquarkchain mintMNT implementation returns the 32-byte success value, but here returns nil?
| t.Run("qkc_tokenid_rejected", func(t *testing.T) { | ||
| input := encodeTransferMNTInput(bob, 35760, uint256.NewInt(1), nil) | ||
| _, err := precompile.Run(input, evm, newContract(alice)) | ||
| require.ErrorIs(t, err, vm.ErrExecutionReverted) |
There was a problem hiding this comment.
Looks like transferMnt has no explicit tokenID == QKC rejection?
| // QKC balance zero, MNT map explicitly empty → serialize as empty list → wire: 0x8200c0 | ||
| // Preserves "account touched TokenBalances map then emptied it" history | ||
| // (9194 such accounts observed on mainnet). | ||
| tokenBal, _ = acct.MntBalances.SerializeToBytes() |
There was a problem hiding this comment.
SerializeToBytes() is documented to fail for >16 token balances, but here drops that error.
| {"mnt_only", uint256.NewInt(0), types.NewTokenBalancesWithMap(map[uint64]*uint256.Int{TokenIDEncode("QKCUP"): uint256.NewInt(500000)}), 0, "..."}, | ||
| {"qkc_and_mnt", uint256.NewInt(1000000), types.NewTokenBalancesWithMap(map[uint64]*uint256.Int{TokenIDEncode("QKCUP"): uint256.NewInt(500000)}), 0, "..."}, | ||
| {"nonzero_shardkey", uint256.NewInt(1000000), nil, 0x10000, "..."}, | ||
| {"trie_format", uint256.NewInt(0), make17Tokens(), 0, "..."}, |
There was a problem hiding this comment.
The test matrix says 17 distinct tokens should be an encode error, but the sample loop still calls require.NoError.
| ```go | ||
| func (st *stateTransition) buyGas() error { | ||
| gasTokenID := st.msg.GasTokenID | ||
| // deduct gas cost from gasTokenID balance |
There was a problem hiding this comment.
Should it fail if gasTokenID != QKC?
Summary
Add design documentation for Multi Native Token (MNT) integration in
docs/mnt/:goshard-mnt-design.md— Full design for goshard (the go-ethereum fork used by QuarkChain). Covers everything needed to implement MNT support with byte-for-byte state trie compatibility with goquarkchain/pyquarkchain.goqkc-mnt-design.md— Reference documentation for goquarkchain's existing MNT design: Account struct, TokenBalances types, precompile/system contracts, transaction flow, and EVM dispatch mechanism.mainnet-mnt-txs.md— Two real mainnet transactions withtransfer_token_id = 46347397(QKCUP, non-QKC), confirming theTransferTokenIDfield in EVM Context is a hard requirement for historical chain compatibility.Key design decisions documented
StateAccount extension (
goshard-mnt-design.md§2)StateAccountgains a 5th fieldMntBalances *TokenBalancesfor non-QKC tokensBalance *uint256.Intstays as-is for QKC (tokenID=35760) in EVM and state machineEncodeAccountRLPmerges both into goquarkchain's 6-element RLP formatEVM Context (§5.2)
GasTokenIDandTransferTokenIDadded tovm.Context, mirroring goquarkchainCanTransfer/Transfersignatures extended withuint64 tokenIDparametertransfer_token_idwithout going through thetransferMntprecompile — required for the two observed mainnet transactionsMNT Precompile/system contracts (§7.9–7.11)
0x...514b430001–0x...514b430005:currentMntID,transferMnt,deploySystemContract,mintMNT,balanceMNT0x514b43...0002/0003)TokenIDQueriedflag onContractstruct +checkTokenIDQueriedinevm.Call— prevents contracts from silently accepting the wrong tokenTokenBalances trie format (§4.3)
0x00+ RLP-sorted pairs — implemented0x01+ SecureTrie root — no mainnet accounts with >16 MNT tokens observed, no current requirementTesting strategy (§12)