Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions core/types/gen_account_rlp.go

This file was deleted.

109 changes: 62 additions & 47 deletions core/types/state_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,67 +20,79 @@ import (
"bytes"

"github.com/ethereum/go-ethereum/common"
qkccommon "github.com/ethereum/go-ethereum/qkc/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)

//go:generate go run ../../rlp/rlpgen -type StateAccount -out gen_account_rlp.go

// StateAccount is the Ethereum consensus representation of accounts.
// These objects are stored in the main account trie.
// StateAccount is the QuarkChain consensus representation of accounts.
// It uses the codec in state_account_qkc.go because the standard four-field
// Ethereum codec cannot encode its token and shard fields.
type StateAccount struct {
Nonce uint64
Balance *uint256.Int
Root common.Hash // merkle root of the storage trie
CodeHash []byte
Nonce uint64
MntBalances *qkccommon.TokenBalances // QKC and MNT balances.
Root common.Hash // merkle root of the storage trie
CodeHash []byte
FullShardKey uint32
}

// NewEmptyStateAccount constructs an empty state account.
func NewEmptyStateAccount() *StateAccount {
return &StateAccount{
Balance: new(uint256.Int),
Root: EmptyRootHash,
CodeHash: EmptyCodeHash.Bytes(),
MntBalances: qkccommon.NewEmptyTokenBalances(),
Root: EmptyRootHash,
CodeHash: EmptyCodeHash.Bytes(),
}
}

// Copy returns a deep-copied state account object.
func (acct *StateAccount) Copy() *StateAccount {
var balance *uint256.Int
if acct.Balance != nil {
balance = new(uint256.Int).Set(acct.Balance)
var mntBalances *qkccommon.TokenBalances
if acct.MntBalances != nil {
mntBalances = acct.MntBalances.Copy()
}
return &StateAccount{
Nonce: acct.Nonce,
Balance: balance,
Root: acct.Root,
CodeHash: common.CopyBytes(acct.CodeHash),
Nonce: acct.Nonce,
MntBalances: mntBalances,
Root: acct.Root,
CodeHash: common.CopyBytes(acct.CodeHash),
FullShardKey: acct.FullShardKey,
}
}

Comment thread
ping-ke marked this conversation as resolved.
// SlimAccount is a modified version of an Account, where the root is replaced
// with a byte slice. This format can be used to represent full-consensus format
// or slim format which replaces the empty root and code hash as nil byte slice.
// SlimAccount is retained for the inherited snapshot and pathdb code. QuarkChain
// nodes support only hashdb-backed state and do not enable those modes. The QKC
// fields remain lossless here so shared conversion code does not discard them.
// Empty roots and code hashes are represented by nil slices.
type SlimAccount struct {
Nonce uint64
Balance *uint256.Int
Root []byte // Nil if root equals to types.EmptyRootHash
CodeHash []byte // Nil if hash equals to types.EmptyCodeHash
Nonce uint64
Comment thread
ping-ke marked this conversation as resolved.
Outdated
MntBal []byte
FullShardKey qkccommon.Uint32
Root []byte // Nil if root equals to types.EmptyRootHash
CodeHash []byte // Nil if hash equals to types.EmptyCodeHash
}

// SlimAccountRLP encodes the state account in 'slim RLP' format.
func SlimAccountRLP(account StateAccount) []byte {
balances := account.MntBalances
if balances == nil {
balances = qkccommon.NewEmptyTokenBalances()
}
mntBal, err := balances.SerializeToBytes()
if err != nil {
panic(err)
}
slim := SlimAccount{
Nonce: account.Nonce,
Balance: account.Balance,
Nonce: account.Nonce,
MntBal: mntBal,
FullShardKey: qkccommon.Uint32(account.FullShardKey),
}
if account.Root != EmptyRootHash {
slim.Root = account.Root[:]
}
if !bytes.Equal(account.CodeHash, EmptyCodeHash[:]) {
slim.CodeHash = account.CodeHash
}
data, err := rlp.EncodeToBytes(slim)
data, err := rlp.EncodeToBytes(&slim)
if err != nil {
panic(err)
}
Expand All @@ -94,28 +106,31 @@ func FullAccount(data []byte) (*StateAccount, error) {
if err := rlp.DecodeBytes(data, &slim); err != nil {
return nil, err
}
var account StateAccount
account.Nonce, account.Balance = slim.Nonce, slim.Balance

// Interpret the storage root and code hash in slim format.
if len(slim.Root) == 0 {
account.Root = EmptyRootHash
} else {
account.Root = common.BytesToHash(slim.Root)
}
if len(slim.CodeHash) == 0 {
account.CodeHash = EmptyCodeHash[:]
} else {
account.CodeHash = slim.CodeHash
}
return &account, nil
return slim.ToStateAccount()
}

// FullAccountRLP converts data on the 'slim RLP' format into the full RLP-format.
func FullAccountRLP(data []byte) ([]byte, error) {
account, err := FullAccount(data)
if err != nil {
var slim SlimAccount
if err := rlp.DecodeBytes(data, &slim); err != nil {
return nil, err
}
return rlp.EncodeToBytes(account)
if _, err := qkccommon.NewTokenBalances(slim.MntBal); err != nil {
return nil, err
}
root := EmptyRootHash
if len(slim.Root) != 0 {
root = common.BytesToHash(slim.Root)
}
codeHash := EmptyCodeHash.Bytes()
if len(slim.CodeHash) != 0 {
codeHash = slim.CodeHash
}
return rlp.EncodeToBytes(&qkcAccountRLP{
Nonce: slim.Nonce,
TokenBal: slim.MntBal,
Root: root,
CodeHash: codeHash,
FullShardKey: slim.FullShardKey,
})
}
122 changes: 122 additions & 0 deletions core/types/state_account_qkc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright 2026-2027, QuarkChain.

package types

import (
"errors"
"io"

"github.com/ethereum/go-ethereum/common"
qkccommon "github.com/ethereum/go-ethereum/qkc/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)

// qkcAccountRLP matches pyquarkchain's six-field _Account encoding.
type qkcAccountRLP struct {
Nonce uint64
TokenBal []byte
Root common.Hash
CodeHash []byte
FullShardKey qkccommon.Uint32
Optional []byte
}

// ToStateAccount expands a slim snapshot account into the consensus account
// representation without dropping token balances or the full shard key.
func (acct *SlimAccount) ToStateAccount() (*StateAccount, error) {
balances, err := qkccommon.NewTokenBalances(acct.MntBal)
if err != nil {
return nil, err
}
full := &StateAccount{
Nonce: acct.Nonce,
MntBalances: balances,
FullShardKey: uint32(acct.FullShardKey),
}
if len(acct.Root) == 0 {
full.Root = EmptyRootHash
} else {
full.Root = common.BytesToHash(acct.Root)
}
if len(acct.CodeHash) == 0 {
full.CodeHash = EmptyCodeHash.Bytes()
} else {
full.CodeHash = common.CopyBytes(acct.CodeHash)
}
return full, nil
}

// GetMntBalance returns the balance of tokenID in the account's unified balance map.
func (acct *StateAccount) GetMntBalance(tokenID uint64) *uint256.Int {
if acct.MntBalances == nil {
return new(uint256.Int)
}
return acct.MntBalances.GetTokenBalance(tokenID)
}

// Balance returns the account's QKC balance.
func (acct *StateAccount) Balance() *uint256.Int {
return acct.GetMntBalance(qkccommon.DefaultTokenID)
}

// SetBalance sets the account's QKC balance.
func (acct *StateAccount) SetBalance(balance *uint256.Int) {
if acct.MntBalances == nil {
acct.MntBalances = qkccommon.NewEmptyTokenBalances()
}
acct.MntBalances.SetValue(balance, qkccommon.DefaultTokenID)
}

// NewQKCTokenBalances creates an account balance map with a QKC balance.
func NewQKCTokenBalances(balance *uint256.Int) *qkccommon.TokenBalances {
balances := qkccommon.NewEmptyTokenBalances()
if balance != nil {
balances.SetValue(balance, qkccommon.DefaultTokenID)
}
return balances
}

// EncodeRLP implements pyquarkchain's account encoding.
func (acct *StateAccount) EncodeRLP(w io.Writer) error {
balances := acct.MntBalances
if balances == nil {
balances = qkccommon.NewEmptyTokenBalances()
}
tokenBal, err := balances.SerializeToBytes()
if err != nil {
return err
}
return rlp.Encode(w, &qkcAccountRLP{
Nonce: acct.Nonce,
TokenBal: tokenBal,
Root: acct.Root,
CodeHash: acct.CodeHash,
FullShardKey: qkccommon.Uint32(acct.FullShardKey),
})
}

// DecodeRLP implements pyquarkchain's account decoding.
func (acct *StateAccount) DecodeRLP(s *rlp.Stream) error {
raw, err := s.Raw()
if err != nil {
return err
}
var wire qkcAccountRLP
if err := rlp.DecodeBytes(raw, &wire); err != nil {
return err
}
if len(wire.Optional) != 0 {
return errors.New("unsupported non-empty QuarkChain account optional field")
}
acct.Nonce = wire.Nonce
acct.Root = wire.Root
acct.CodeHash = wire.CodeHash
acct.FullShardKey = uint32(wire.FullShardKey)
balances, err := qkccommon.NewTokenBalances(wire.TokenBal)
if err != nil {
return err
}
acct.MntBalances = balances
return nil
}
Loading