diff --git a/core/types/gen_account_rlp.go b/core/types/gen_account_rlp.go new file mode 100644 index 0000000000..8b424493af --- /dev/null +++ b/core/types/gen_account_rlp.go @@ -0,0 +1,21 @@ +// Code generated by rlpgen. DO NOT EDIT. + +package types + +import "github.com/ethereum/go-ethereum/rlp" +import "io" + +func (obj *StateAccount) EncodeRLP(_w io.Writer) error { + w := rlp.NewEncoderBuffer(_w) + _tmp0 := w.List() + w.WriteUint64(obj.Nonce) + if obj.Balance == nil { + w.Write(rlp.EmptyString) + } else { + w.WriteUint256(obj.Balance) + } + w.WriteBytes(obj.Root[:]) + w.WriteBytes(obj.CodeHash) + w.ListEnd(_tmp0) + return w.Flush() +} diff --git a/core/types/state_account.go b/core/types/state_account.go index be647cce73..52ef843b35 100644 --- a/core/types/state_account.go +++ b/core/types/state_account.go @@ -29,13 +29,10 @@ import ( // StateAccount is the Ethereum consensus representation of accounts. // These objects are stored in the main account trie. type StateAccount struct { - Nonce uint64 - Balance *uint256.Int - Root common.Hash // merkle root of the storage trie - CodeHash []byte - MntBalances *TokenBalances `rlp:"optional"` // non-QKC MNT balances; nil = no MNT tokens - FullShardKey uint32 // QuarkChain shard key; set on first tx, preserved thereafter - + Nonce uint64 + Balance *uint256.Int + Root common.Hash // merkle root of the storage trie + CodeHash []byte } // NewEmptyStateAccount constructs an empty state account. @@ -53,17 +50,11 @@ func (acct *StateAccount) Copy() *StateAccount { if acct.Balance != nil { balance = new(uint256.Int).Set(acct.Balance) } - var mnt *TokenBalances - if acct.MntBalances != nil { - mnt = acct.MntBalances.Copy() - } return &StateAccount{ - Nonce: acct.Nonce, - Balance: balance, - Root: acct.Root, - CodeHash: common.CopyBytes(acct.CodeHash), - MntBalances: mnt, - FullShardKey: acct.FullShardKey, + Nonce: acct.Nonce, + Balance: balance, + Root: acct.Root, + CodeHash: common.CopyBytes(acct.CodeHash), } } diff --git a/core/types/state_account_qkc.go b/core/types/state_account_qkc.go deleted file mode 100644 index 1b8747e822..0000000000 --- a/core/types/state_account_qkc.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2024 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package types - -import ( - "io" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/rlp" - "github.com/holiman/uint256" -) - -// qkcAccountRLP is the wire struct for QuarkChain's 6-element account format: -// [Nonce, TokenBal(bytes), Root, CodeHash, FullShardKey(4B fixed), Optional]. -// TokenBal is stored as raw serialized bytes (matching pyquarkchain's `binary` type), -// so nil encodes as 0x80 (empty string), not 0xC0 (empty list). -type qkcAccountRLP struct { - Nonce uint64 - TokenBal []byte // SerializeToBytes output; nil = no balances - Root common.Hash - CodeHash []byte - FullShardKey Uint32 - Optional []byte -} - -// mergeQKCTokenBalances combines the QKC native balance (tokenID=35760) and MNT -// balances into a single TokenBalances for wire encoding. Returns nil if both empty, -// which causes EncodeRLP to write 0x80 (RLP nil/empty) matching goquarkchain behavior. -func mergeQKCTokenBalances(balance *uint256.Int, mnt *TokenBalances) *TokenBalances { - if (balance == nil || balance.IsZero()) && (mnt == nil || mnt.IsBlank()) { - return nil - } - merged := NewEmptyTokenBalances() - if mnt != nil { - for id, bal := range mnt.GetBalanceMap() { - merged.SetValue(bal, id) - } - } - if balance != nil && !balance.IsZero() { - merged.SetValue(balance, DefaultTokenID) - } - return merged -} - -// EncodeRLP implements rlp.Encoder for StateAccount using QuarkChain's -// 6-element format. Root is always written as 32 bytes (no nil optimization). -func (acct *StateAccount) EncodeRLP(w io.Writer) error { - var tokenBal []byte - switch { - case acct.MntBalances == nil && (acct.Balance == nil || acct.Balance.IsZero()): - // No QKC balance, no MNT tokens — encode TokenBal as nil → 0x80. - // Covers: new accounts and accounts that never entered the TokenBalances map. - tokenBal = nil - - case acct.MntBalances != nil && acct.MntBalances.IsBlank() && acct.Balance != nil && acct.Balance.IsZero(): - // QKC balance zero, MNT map explicitly empty → re-serialize as - // list-format with zero pairs → 0x8200c0. This preserves the - // "account touched TokenBalances map then emptied it" history. - tokenBal, _ = acct.MntBalances.SerializeToBytes() - - default: - // Normal case: has QKC balance and/or non-empty MNT tokens. - tb := mergeQKCTokenBalances(acct.Balance, acct.MntBalances) - var err error - tokenBal, err = tb.SerializeToBytes() - if err != nil { - return err - } - } - qkc := &qkcAccountRLP{ - Nonce: acct.Nonce, - Root: acct.Root, - CodeHash: acct.CodeHash, - TokenBal: tokenBal, - FullShardKey: Uint32(acct.FullShardKey), - Optional: nil, - } - return rlp.Encode(w, qkc) -} - -// DecodeRLP implements rlp.Decoder for StateAccount using QuarkChain's -// 6-element format. -func (acct *StateAccount) DecodeRLP(s *rlp.Stream) error { - raw, err := s.Raw() - if err != nil { - return err - } - var qkc qkcAccountRLP - if err := rlp.DecodeBytes(raw, &qkc); err != nil { - return err - } - acct.Nonce = qkc.Nonce - acct.CodeHash = qkc.CodeHash - acct.Root = qkc.Root - acct.FullShardKey = uint32(qkc.FullShardKey) - acct.Balance = new(uint256.Int) - if len(qkc.TokenBal) > 0 { - tb, err := NewTokenBalancesFromBytes(qkc.TokenBal) - if err != nil { - return err - } - balMap := tb.GetBalanceMap() - if qkcBal, ok := balMap[DefaultTokenID]; ok { - acct.Balance.Set(qkcBal) - delete(balMap, DefaultTokenID) - } - // Always set MntBalances when TokenBal has content — even if empty - // after stripping QKC, this lets EncodeRLP produce 0x8200c0 instead - // of 0x80, preserving byte-identical round-trip. - acct.MntBalances = &TokenBalances{balances: balMap} - } - return nil -} diff --git a/core/types/state_account_qkc_test.go b/core/types/state_account_qkc_test.go deleted file mode 100644 index b836aeb566..0000000000 --- a/core/types/state_account_qkc_test.go +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright 2024 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package types - -import ( - "bytes" - "encoding/hex" - "strings" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/rlp" - "github.com/holiman/uint256" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// pyquarkchain test vectors generated by: -// -// from quarkchain.evm.state import _Account, TokenBalancePair -// import rlp -// from rlp.sedes import BigEndianInt, binary, CountableList, big_endian_int -// -// See pyquarkchain/quarkchain/evm/state.py _Account for the canonical format. -// -// All vectors use: -// - storage = EmptyRootHash (keccak256 of empty) -// - code_hash = EmptyCodeHash (keccak256 of empty string) -// - full_shard_key = 1 (BigEndianInt(4) → always 4 bytes on the wire) -// - optional = b"" -var ( - pyqkcVecNonce1QKC1000 = mustHex("f853018900c7c6828bb08203e8a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470840000000180") - pyqkcVecNonce5QKC2000MNT500 = mustHex("f858058e00ccc4648201f4c6828bb08207d0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470840000000180") - pyqkcVecZeroAccount = mustHex("f84a8080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470840000000180") -) - -func mustHex(s string) []byte { - b, err := hex.DecodeString(s) - if err != nil { - panic(err) - } - return b -} - -// TestStateAccountEncodeDecodeRoundtrip verifies that EncodeRLP→DecodeRLP is -// a lossless roundtrip for all fields including FullShardKey. -func TestStateAccountEncodeDecodeRoundtrip(t *testing.T) { - cases := []struct { - name string - acct StateAccount - }{ - { - name: "empty", - acct: *NewEmptyStateAccount(), - }, - { - name: "nonce1 QKC=1000 shard=1", - acct: StateAccount{ - Nonce: 1, - Balance: uint256.NewInt(1000), - Root: EmptyRootHash, - CodeHash: EmptyCodeHash[:], - FullShardKey: 1, - }, - }, - { - name: "nonce5 QKC=2000 MNT[100]=500 shard=0x2f3e", - acct: StateAccount{ - Nonce: 5, - Balance: uint256.NewInt(2000), - Root: EmptyRootHash, - CodeHash: EmptyCodeHash[:], - MntBalances: NewTokenBalancesWithMap(map[uint64]*uint256.Int{ - 100: uint256.NewInt(500), - }), - FullShardKey: 0x2f3e, - }, - }, - { - name: "non-empty root and codehash shard=0x72ea", - acct: StateAccount{ - Nonce: 3, - Balance: uint256.NewInt(1e18), - Root: common.HexToHash("0xdeadbeef"), - CodeHash: common.Hex2Bytes("abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"), - FullShardKey: 0x72ea, - }, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - encoded, err := rlp.EncodeToBytes(&tc.acct) - require.NoError(t, err) - - var decoded StateAccount - require.NoError(t, rlp.DecodeBytes(encoded, &decoded)) - - assert.Equal(t, tc.acct.Nonce, decoded.Nonce) - assert.Equal(t, tc.acct.Root, decoded.Root) - assert.Equal(t, tc.acct.CodeHash, decoded.CodeHash) - assert.Equal(t, tc.acct.FullShardKey, decoded.FullShardKey) - require.NotNil(t, decoded.Balance) - assert.Equal(t, tc.acct.Balance, decoded.Balance) - if tc.acct.MntBalances == nil || tc.acct.MntBalances.IsBlank() { - assert.True(t, decoded.MntBalances == nil || decoded.MntBalances.IsBlank()) - } else { - require.NotNil(t, decoded.MntBalances) - assert.Equal(t, tc.acct.MntBalances.GetBalanceMap(), decoded.MntBalances.GetBalanceMap()) - } - }) - } -} - -// TestStateAccountPyquarkchainDecodeCompatibility verifies that goshard can -// decode blobs produced by pyquarkchain. These are the authoritative wire -// vectors for the QKC 6-element RLP format. All vectors use full_shard_key=1. -func TestStateAccountPyquarkchainDecodeCompatibility(t *testing.T) { - cases := []struct { - name string - blob []byte - wantNonce uint64 - wantQKC *uint256.Int - wantMNT map[uint64]*uint256.Int // nil = no MNT tokens expected - wantFullShardKey uint32 - }{ - { - name: "nonce=1 QKC=1000", - blob: pyqkcVecNonce1QKC1000, - wantNonce: 1, - wantQKC: uint256.NewInt(1000), - wantFullShardKey: 1, - }, - { - name: "nonce=5 QKC=2000 MNT[100]=500", - blob: pyqkcVecNonce5QKC2000MNT500, - wantNonce: 5, - wantQKC: uint256.NewInt(2000), - wantMNT: map[uint64]*uint256.Int{100: uint256.NewInt(500)}, - wantFullShardKey: 1, - }, - { - name: "zero account", - blob: pyqkcVecZeroAccount, - wantNonce: 0, - wantQKC: uint256.NewInt(0), - wantFullShardKey: 1, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - var acct StateAccount - require.NoError(t, rlp.DecodeBytes(tc.blob, &acct)) - - assert.Equal(t, tc.wantNonce, acct.Nonce) - assert.Equal(t, EmptyRootHash, acct.Root) - assert.Equal(t, EmptyCodeHash[:], acct.CodeHash) - assert.Equal(t, tc.wantFullShardKey, acct.FullShardKey) - require.NotNil(t, acct.Balance) - assert.Equal(t, tc.wantQKC, acct.Balance) - - if tc.wantMNT == nil { - assert.True(t, acct.MntBalances == nil || acct.MntBalances.IsBlank()) - } else { - require.NotNil(t, acct.MntBalances) - assert.Equal(t, tc.wantMNT, acct.MntBalances.GetBalanceMap()) - } - }) - } -} - -// TestStateAccountPyquarkchainEncodeCompatibility verifies that goshard -// produces byte-for-byte identical output to pyquarkchain for the same account. -// This is the strongest compatibility check: same input → same wire bytes. -func TestStateAccountPyquarkchainEncodeCompatibility(t *testing.T) { - cases := []struct { - name string - acct StateAccount - want []byte - }{ - { - name: "nonce=1 QKC=1000", - acct: StateAccount{ - Nonce: 1, - Balance: uint256.NewInt(1000), - Root: EmptyRootHash, - CodeHash: EmptyCodeHash[:], - FullShardKey: 1, - }, - want: pyqkcVecNonce1QKC1000, - }, - { - name: "nonce=5 QKC=2000 MNT[100]=500", - acct: StateAccount{ - Nonce: 5, - Balance: uint256.NewInt(2000), - Root: EmptyRootHash, - CodeHash: EmptyCodeHash[:], - MntBalances: NewTokenBalancesWithMap(map[uint64]*uint256.Int{ - 100: uint256.NewInt(500), - }), - FullShardKey: 1, - }, - want: pyqkcVecNonce5QKC2000MNT500, - }, - { - name: "zero account", - acct: StateAccount{ - Nonce: 0, - Balance: uint256.NewInt(0), - Root: EmptyRootHash, - CodeHash: EmptyCodeHash[:], - FullShardKey: 1, - }, - want: pyqkcVecZeroAccount, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got, err := rlp.EncodeToBytes(&tc.acct) - require.NoError(t, err) - assert.Equal(t, hex.EncodeToString(tc.want), hex.EncodeToString(got), - "goshard encoding must match pyquarkchain byte-for-byte") - }) - } -} - -// TestSlimRLPRoundTripEquivalence verifies that for non-MNT accounts (no MntBalances), -// the round-trip slim-RLP → decode → QKC-encode produces the same bytes as direct -// QKC-encode. This is the invariant that allows execute.go to use slim-RLP as the -// accountOrigin format for history reversal without affecting trie root correctness. -// -// Note: SlimAccount does not carry FullShardKey. Both paths therefore encode with -// FullShardKey=0, so the test holds — but the slim-RLP path should only be used -// for accounts where FullShardKey is known to be zero (e.g. freshly created accounts -// before any shard key is set). -func TestSlimRLPRoundTripEquivalence(t *testing.T) { - cases := []struct { - name string - acc StateAccount - }{ - {"zero balance, empty root", StateAccount{Nonce: 0, Balance: new(uint256.Int), Root: EmptyRootHash, CodeHash: EmptyCodeHash.Bytes()}}, - {"nonzero balance, empty root", StateAccount{Nonce: 5, Balance: uint256.NewInt(1_000_000), Root: EmptyRootHash, CodeHash: EmptyCodeHash.Bytes()}}, - {"with storage root", StateAccount{Nonce: 1, Balance: uint256.NewInt(42), Root: common.HexToHash("0xabcdef"), CodeHash: EmptyCodeHash.Bytes()}}, - {"with code hash", StateAccount{Nonce: 99, Balance: uint256.NewInt(999), Root: EmptyRootHash, CodeHash: common.FromHex("0xdeadbeef" + strings.Repeat("00", 28))}}, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - // Path A: direct QKC encode - directQKC, err := rlp.EncodeToBytes(&tc.acc) - if err != nil { - t.Fatalf("direct QKC encode: %v", err) - } - // Path B: slim-RLP → FullAccount decode → QKC encode (execute.go path) - slim := SlimAccountRLP(tc.acc) - decoded, err := FullAccount(slim) - if err != nil { - t.Fatalf("FullAccount decode: %v", err) - } - viaSlim, err := rlp.EncodeToBytes(decoded) - if err != nil { - t.Fatalf("QKC encode after slim round-trip: %v", err) - } - if !bytes.Equal(directQKC, viaSlim) { - t.Errorf("mismatch:\n direct QKC: %x\n via slim: %x", directQKC, viaSlim) - } - }) - } -} diff --git a/core/types/token.go b/core/types/token.go deleted file mode 100644 index b6d1bdf260..0000000000 --- a/core/types/token.go +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2024 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package types - -import ( - "fmt" - "sort" - - "github.com/ethereum/go-ethereum/rlp" - "github.com/holiman/uint256" -) - -// TokenTrieThreshold is the maximum number of non-zero token balances supported -// in list format. Accounts exceeding this limit are not supported. -// -// NOTE: goquarkchain supports a trie format (0x01 prefix + 32-byte SecureTrie root) -// for accounts with > 16 MNT tokens. That format is NOT implemented here because: -// 1. The trie format requires a SecureTrie backed by a database, which would -// introduce an import cycle (core/types → trie → core/rawdb → core/types). -// 2. No real mainnet accounts with > 16 MNT tokens have been observed in practice. -// -// If trie-format support becomes necessary, a standalone MPT implementation must -// be added (without importing the trie package) and verified byte-for-byte against -// goquarkchain's SecureTrie output. -const TokenTrieThreshold = 16 - -// listFormatPrefix is the prefix byte for the list serialization format (≤ 16 tokens). -const listFormatPrefix = byte(0x00) - -// trieFormatPrefix is the goquarkchain trie format prefix (> 16 tokens). -// Deserialization of this format returns an error; serialization is unsupported. -const trieFormatPrefix = byte(0x01) - -// DefaultTokenID is the QKC token ID (= TokenIDEncode("QKC") = 35760). -const DefaultTokenID = uint64(35760) - -// TokenBalancePair is a (TokenID, Balance) pair used in list-format encoding. -type TokenBalancePair struct { - TokenID uint64 - Balance *uint256.Int -} - -// TokenBalances holds multi-token balances for an account. -// Serialization uses list format (0x00 prefix) for ≤ TokenTrieThreshold non-zero -// balances. Trie format (> 16 tokens) is not supported; SerializeToBytes returns -// an error if more than TokenTrieThreshold non-zero balances are present. -type TokenBalances struct { - balances map[uint64]*uint256.Int -} - -// NewEmptyTokenBalances creates an empty TokenBalances. -func NewEmptyTokenBalances() *TokenBalances { - return &TokenBalances{ - balances: make(map[uint64]*uint256.Int), - } -} - -// NewTokenBalancesWithMap creates a TokenBalances from a map. -// The provided values are deep-copied. -func NewTokenBalancesWithMap(data map[uint64]*uint256.Int) *TokenBalances { - tb := NewEmptyTokenBalances() - for id, bal := range data { - if bal != nil && !bal.IsZero() { - tb.balances[id] = new(uint256.Int).Set(bal) - } - } - return tb -} - -// NewTokenBalancesFromBytes deserializes a TokenBalances from its serialized form. -// Prefix 0x00 → list format (RLP-decoded, ≤ 16 tokens). -// Prefix 0x01 → trie format (> 16 tokens); NOT SUPPORTED — returns an error. -func NewTokenBalancesFromBytes(data []byte) (*TokenBalances, error) { - if len(data) == 0 { - return NewEmptyTokenBalances(), nil - } - switch data[0] { - case listFormatPrefix: - var pairs []TokenBalancePair - if err := rlp.DecodeBytes(data[1:], &pairs); err != nil { - return nil, fmt.Errorf("token_balances: list decode error: %w", err) - } - tb := NewEmptyTokenBalances() - for _, p := range pairs { - if p.Balance != nil && !p.Balance.IsZero() { - tb.balances[p.TokenID] = new(uint256.Int).Set(p.Balance) - } - } - return tb, nil - - case trieFormatPrefix: - // Trie format (> 16 MNT tokens) is not implemented. - // See TokenTrieThreshold comment for details. - return nil, fmt.Errorf("token_balances: trie format (0x01, >%d tokens) is not supported", TokenTrieThreshold) - - default: - return nil, fmt.Errorf("token_balances: unknown format prefix 0x%02x", data[0]) - } -} - -// SetValue sets the balance for the given tokenID. -// A nil or zero amount removes the entry. -func (t *TokenBalances) SetValue(amount *uint256.Int, tokenID uint64) { - if amount == nil || amount.IsZero() { - delete(t.balances, tokenID) - return - } - t.balances[tokenID] = new(uint256.Int).Set(amount) -} - -// GetTokenBalance returns the balance for the given tokenID. -// Returns a new zero uint256 if not present. -func (t *TokenBalances) GetTokenBalance(tokenID uint64) *uint256.Int { - if bal, ok := t.balances[tokenID]; ok { - return new(uint256.Int).Set(bal) - } - return new(uint256.Int) -} - -// GetBalanceMap returns a copy of the internal balance map. -func (t *TokenBalances) GetBalanceMap() map[uint64]*uint256.Int { - out := make(map[uint64]*uint256.Int, len(t.balances)) - for id, bal := range t.balances { - out[id] = new(uint256.Int).Set(bal) - } - return out -} - -// IsBlank reports whether there are no non-zero balances. -func (t *TokenBalances) IsBlank() bool { - return len(t.balances) == 0 -} - -// Len returns the number of non-zero token balances. -func (t *TokenBalances) Len() int { - return len(t.balances) -} - -// Copy returns a deep copy of the TokenBalances. -func (t *TokenBalances) Copy() *TokenBalances { - cp := NewEmptyTokenBalances() - for id, bal := range t.balances { - cp.balances[id] = new(uint256.Int).Set(bal) - } - return cp -} - -// SerializeToBytes encodes TokenBalances to bytes. -// -// List format (≤ TokenTrieThreshold non-zero balances): -// - Output: 0x00 + RLP([]TokenBalancePair sorted by TokenID ascending) -// -// Trie format (> TokenTrieThreshold) is NOT supported and returns an error. -// See TokenTrieThreshold comment for details. -func (t *TokenBalances) SerializeToBytes() ([]byte, error) { - if t.Len() > TokenTrieThreshold { - // Trie format (0x01) is not implemented. - // See TokenTrieThreshold comment for details. - return nil, fmt.Errorf("token_balances: %d tokens exceeds the supported maximum of %d; trie format (0x01) is not implemented", t.Len(), TokenTrieThreshold) - } - return t.serializeListFormat() -} - -// serializeListFormat encodes in list format: 0x00 + RLP(sorted pairs). -func (t *TokenBalances) serializeListFormat() ([]byte, error) { - pairs := make([]TokenBalancePair, 0, len(t.balances)) - for id, bal := range t.balances { - pairs = append(pairs, TokenBalancePair{ - TokenID: id, - Balance: bal, - }) - } - sort.Slice(pairs, func(i, j int) bool { - return pairs[i].TokenID < pairs[j].TokenID - }) - - encoded, err := rlp.EncodeToBytes(pairs) - if err != nil { - return nil, fmt.Errorf("token_balances: list encode error: %w", err) - } - - out := make([]byte, 1+len(encoded)) - out[0] = listFormatPrefix - copy(out[1:], encoded) - return out, nil -} diff --git a/core/types/token_test.go b/core/types/token_test.go deleted file mode 100644 index 3b6fdfdd19..0000000000 --- a/core/types/token_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2024 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package types - -import ( - "testing" - - "github.com/holiman/uint256" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestTokenBalancesListFormat(t *testing.T) { - tb := NewEmptyTokenBalances() - tb.SetValue(uint256.NewInt(1000), DefaultTokenID) - tb.SetValue(uint256.NewInt(500), 100) - - data, err := tb.SerializeToBytes() - require.NoError(t, err) - assert.Equal(t, byte(0x00), data[0], "list format prefix") - - tb2, err := NewTokenBalancesFromBytes(data) - require.NoError(t, err) - assert.Equal(t, uint256.NewInt(1000), tb2.GetTokenBalance(DefaultTokenID)) - assert.Equal(t, uint256.NewInt(500), tb2.GetTokenBalance(100)) -} - -func TestTokenBalancesIsBlank(t *testing.T) { - tb := NewEmptyTokenBalances() - assert.True(t, tb.IsBlank()) - tb.SetValue(uint256.NewInt(0), DefaultTokenID) - assert.True(t, tb.IsBlank(), "zero balance is blank") - tb.SetValue(uint256.NewInt(1), DefaultTokenID) - assert.False(t, tb.IsBlank()) -} - -// TestTokenBalancesTrieFormatUnsupported verifies that serializing more than -// TokenTrieThreshold (16) non-zero token balances returns an error, since the -// trie format (0x01) is not implemented. -func TestTokenBalancesTrieFormatUnsupported(t *testing.T) { - tb := NewEmptyTokenBalances() - for i := uint64(1); i <= 17; i++ { - tb.SetValue(uint256.NewInt(i*100), i) - } - _, err := tb.SerializeToBytes() - require.Error(t, err, "expected error for >16 token balances (trie format unsupported)") -} - -// TestTokenBalancesFromBytesTrieFormatUnsupported verifies that deserializing -// a 0x01-prefixed (trie format) byte slice returns an error. -func TestTokenBalancesFromBytesTrieFormatUnsupported(t *testing.T) { - data := make([]byte, 33) - data[0] = 0x01 // trie format prefix - _, err := NewTokenBalancesFromBytes(data) - require.Error(t, err, "expected error for trie format (0x01) deserialization") -} - -func TestTokenBalancesCopy(t *testing.T) { - tb := NewTokenBalancesWithMap(map[uint64]*uint256.Int{ - DefaultTokenID: uint256.NewInt(1e18), - 100: uint256.NewInt(500), - }) - cp := tb.Copy() - cp.SetValue(uint256.NewInt(0), DefaultTokenID) - assert.Equal(t, uint256.NewInt(1e18), tb.GetTokenBalance(DefaultTokenID), "original unaffected") -} diff --git a/core/types/uint32_rlp.go b/core/types/uint32_rlp.go deleted file mode 100644 index 27a3ad75ec..0000000000 --- a/core/types/uint32_rlp.go +++ /dev/null @@ -1,40 +0,0 @@ -package types - -import ( - "encoding/binary" - "fmt" - "io" - - "github.com/ethereum/go-ethereum/rlp" -) - -type Uint32 uint32 - -const ( - rlpUint32Prefix = byte(0x84) // RLP "string of length 4" - rlpUint32Len = 5 -) - -func (u *Uint32) GetValue() uint32 { return uint32(*u) } - -func (u *Uint32) EncodeRLP(w io.Writer) error { - b := [rlpUint32Len]byte{rlpUint32Prefix} - binary.BigEndian.PutUint32(b[1:], uint32(*u)) - _, err := w.Write(b[:]) - return err -} - -func (u *Uint32) DecodeRLP(s *rlp.Stream) error { - data, err := s.Raw() - if err != nil { - return err - } - if len(data) != rlpUint32Len { - return fmt.Errorf("Uint32 RLP: expected %d bytes, got %d", rlpUint32Len, len(data)) - } - if data[0] != rlpUint32Prefix { - return fmt.Errorf("Uint32 RLP: expected prefix 0x%02x, got 0x%02x", rlpUint32Prefix, data[0]) - } - *u = Uint32(binary.BigEndian.Uint32(data[1:])) - return nil -} diff --git a/core/types/uint32_rlp_test.go b/core/types/uint32_rlp_test.go deleted file mode 100644 index a4023f7173..0000000000 --- a/core/types/uint32_rlp_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package types - -import ( - "bytes" - "testing" - - "github.com/ethereum/go-ethereum/rlp" -) - -func TestUint32RLP(t *testing.T) { - v := Uint32(0x12345678) - encoded, err := rlp.EncodeToBytes(&v) - if err != nil { - t.Fatal(err) - } - // Expected: 0x84 + big-endian uint32 - want := []byte{0x84, 0x12, 0x34, 0x56, 0x78} - if !bytes.Equal(encoded, want) { - t.Fatalf("encode: want %x, got %x", want, encoded) - } - var v2 Uint32 - if err := rlp.DecodeBytes(encoded, &v2); err != nil { - t.Fatal(err) - } - if v2 != v { - t.Fatalf("decode: want %d, got %d", v, v2) - } -} diff --git a/params/config.go b/params/config.go index b82e7bd73f..17508cbf27 100644 --- a/params/config.go +++ b/params/config.go @@ -27,14 +27,11 @@ import ( ) // Genesis hashes to enforce below configs on. -// NOTE: These reflect the QKC (QuarkChain) 6-element account encoding which differs -// from standard Ethereum encoding. The state root and therefore block hash changes -// when the account RLP format changes. var ( - MainnetGenesisHash = common.HexToHash("0x7a86192e8901d313f5ca3af78e13a660aae94173959c69a3f33e0ae5f2749d7c") - HoleskyGenesisHash = common.HexToHash("0x0706dbd8f023baa1808450dd3dfce54335339247b2116c5613e178037b2ccc8f") - SepoliaGenesisHash = common.HexToHash("0xc61eb78385a53483f9de8a5710f91d0921bb4ff968ed482c8955b3a9c6d3ccaf") - HoodiGenesisHash = common.HexToHash("0x21a5ed4cc25beb5aa3e1fe4552e2ff7bcf785aeb6cf0472e1b529bd3346c3ba3") + MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") + HoleskyGenesisHash = common.HexToHash("0xb5f7f912443c940f21fd611f12828d75b534364ed9e95ca4e307729a4661bde4") + SepoliaGenesisHash = common.HexToHash("0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9") + HoodiGenesisHash = common.HexToHash("0xbbe312868b376a3001692a646dd2d7d1e4406380dfd86b98aa8a34d1557c971b") ) func newUint64(val uint64) *uint64 { return &val } @@ -1413,4 +1410,3 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules IsEIP4762: isUBT, } } -