Skip to content

qkc/types: port QuarkChain slavechain block, header & transaction types (byte-compatible)#20

Open
blockchaindevsh wants to merge 4 commits into
goshard/basefrom
qkc-3-types
Open

qkc/types: port QuarkChain slavechain block, header & transaction types (byte-compatible)#20
blockchaindevsh wants to merge 4 commits into
goshard/basefrom
qkc-3-types

Conversation

@blockchaindevsh

Copy link
Copy Markdown

Third of the 8 stacked PRs porting QuarkChain's shardchain
(slavechain) into
goshard. Builds on #12 (qkc/serialize) and #16
(qkc/{common,account,params,config});
the remaining layers (qkc/core/rawdb, qkc/consensus,
qkc/core, qkc/miner,
cmd/qkcshard) stack on top of this one.

This layer adds the qkc/types package — the QuarkChain
MinorBlock type set and
all its dependencies — byte-for-byte compatible with
QuarkChain (block/header/meta
hashes must match exactly).

What's included

  • minorblock.goMinorBlock, MinorBlockHeader,
    MinorBlockMeta (+ Hash/SealHash,
    Copy*, accessors, gencodec JSON).
  • transaction.go / transaction_signing.go /
    solidity_abi_utils.go
    EvmTransaction
    (+ shard keys & token ids), the Transaction wrapper (type byte
  • length-prefixed payload),
    Transactions, and the EIP155Signer (v0/v1/v2 sender
    recovery, incl. EIP-712 typed-data for v1).
  • receipt.go / log.go / bloom9.go — QuarkChain's own
    Receipt/Receipts, Log/LogForStorage,
    and Bloom/CreateBloom (deliberately not geth's — required
    for byte-compat).
  • token.goTokenBalances (multi-native-token balances;
    sorted-pair + >16-token trie forms).
  • derive_sha.go / utils.goDeriveSha (receipt-root via
    trie.StackTrie) and
    CalculateMerkleRoot (QuarkChain's custom keccak Merkle for
    MinorBlockMeta.TxHash — a tree, not a trie).
  • special_rlp.go — the Uint32 special RLP (e.g.
    FullShardKey), plus XShardTxCursorInfo,
    CrossShardTransactionDeposit.
  • rootblock.goRootBlock/RootBlockHeader ported as
    passive dependencies only, so shard
    code that references root types compiles. No root-chain logic.

Byte-compatibility

Header/block hashing is keccak256(serialize(...)) (QuarkChain's
reflection serializer from #12, not
RLP); SealHash excludes nonce+mixhash;
MinorBlockMeta.ReceiptHash is a trie root while TxHash is the
custom binary Merkle root. Verified against goquarkchain golden
vectors (header hash, token-trie root,
CalculateMerkleRoot) and round-trip (SerializeDeserialize)
for header/meta/block/tx/receipt.

pyquarkchain alignment

Per the project rule (pyquarkchain is authoritative on behavior
where goquarkchain diverges), this PR
includes two behavioral fixes vs the verbatim goquarkchain port:

  • TokenBalances.Serialize now omits zero-balance entries
    (matches pyquarkchain's TokenBalanceMap
    skip-func; also fixes a Serialize/Deserialize asymmetry).
  • CrossShardTransactionDepositV0 field order corrected so
    is_from_root_chain serializes last
    (matches pyquarkchain core.py; goquarkchain placed it after
    transfer_token_id).
    Both carry code comments citing the pyquarkchain source.

Out of scope (deferred)

Transaction execution / state transition is a separate issue —
these types are ported for byte-compat
and chain plumbing, not driven here. Root-chain implementation
is also out of scope (root types are
passive deps only).

Testing

go build ./... and go test ./qkc/types/... green; byte-compat
golden vectors, round-trips, and v0/v1/v2
tx-signing tests included.

Byte-compatible port of goquarkchain/core/types: MinorBlock/Header/Meta,
Transaction+EvmTransaction, Receipt, Log, Bloom, TokenBalances,
CalculateMerkleRoot and DeriveSha, plus passive RootBlock/RootBlockHeader.
Header hash is keccak256(serialize(header)); adaptations are compile-level
only. Byte-parity locked by goquarkchain's golden vectors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread qkc/types/derive_sha.go Outdated
hashList = append(hashList, common.Hash{})
} else {
for i := 0; i < val.Len(); i++ {
bytes, _ := serialize.SerializeToBytes(val.Index(i).Interface())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If serialize.SerializeToBytes fails, swallowing the error here means sha3_256(nil) will silently produce a wrong hash, corrupting the Merkle root in consensus-critical paths (NewMinorBlock, Finalize).

We should panic immediately on error here (similar to Receipts.Bytes)?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e39259b

Comment thread qkc/types/derive_sha.go
var EmptyHash = common.Hash{}

func CalculateMerkleRoot(list interface{}) (h common.Hash) {
val := reflect.ValueOf(list)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reflect.ValueOf(nil).Type() will trigger an opaque runtime panic if a nil list is passed. Adding an explicit check improves API defensiveness and provides a much clearer error message.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e253b3f

Comment thread qkc/types/minorblock.go Outdated

// Bytes implements DerivableList and returns the i'th element of s in serialize.
func (s MinorBlockHeaders) Bytes(i int) []byte {
enc, _ := serialize.SerializeToBytes(s[i])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a failure occurs, it returns nil bytes, resulting in a root hash error. Please follow the same panic(err) pattern as Receipts.Bytes?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 969f0d5, also fixed two Size() swallows.

Comment thread qkc/types/transaction.go Outdated
func (s Transactions) Len() int { return len(s) }

func (s Transactions) Bytes(i int) []byte {
enc, _ := serialize.SerializeToBytes(s[i]) //todo error handle?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a failure occurs, it returns nil bytes, resulting in a root hash error. Please follow the same panic(err) pattern as Receipts.Bytes? TODO?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 969f0d5, also fixed two Size() swallows.

}

func (s EIP155Signer) Sender(tx *EvmTransaction) (account.Recipient, error) {
if tx.Version() != 2 && tx.NetworkId() != s.networkId {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing network ID check for versions 0 and 1.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I meant to say that the network ID check should apply to all versions, not just tx.Version() != 2.

@blockchaindevsh blockchaindevsh Jun 26, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v2's network_id is the Ethereum chain id (BASE_ETH_CHAIN_ID + chain_id + 1, e.g. 100001), not NETWORK_ID — so applying this check to v2 would reject every EIP155 tx. The real check is version-aware and belongs at admission (validateTx: v2 → EthChainID, else → NETWORK_ID, matching pyquarkchain shard_state.py), landing with the deferred execution port. v2 is also bound to its chain id via the EIP155 V here. So the != 2 guard is intentional.

Comment thread qkc/types/rootblock.go

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is root block outside the scope of the shard chain?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's needed by functions like CreateMinorBlock.

blockchaindevsh and others added 3 commits June 26, 2026 08:34
The error from serialize.SerializeToBytes was swallowed, so a failure would
silently hash nil (sha3_256(nil)) and corrupt the Merkle root in
consensus-critical paths (NewMinorBlock, Finalize). Panic on error instead,
mirroring Receipts.Bytes.

Addresses review feedback from @iteyelmp.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…kleRoot

reflect.Value.Type() panics opaquely ("reflect: call of reflect.Value.Type
on zero Value") when CalculateMerkleRoot is passed a nil list. Value.Kind()
has no zero-Value guard and returns Invalid instead, so the existing clear
"expect slice input" error fires. Kind() == Type().Kind() for every valid
input, so this is behavior-preserving except on the nil case it fixes.

Addresses iteyelmp's review comment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MinorBlockHeaders.Bytes and Transactions.Bytes (DerivableList.Bytes
implementations) plus MinorBlock.Size/RootBlock.Size swallowed the
SerializeToBytes error and returned nil/zero. A failure would then
silently corrupt the DeriveSha root hash (Bytes) or the cached storage
size (Size) instead of failing loudly. Follow the existing Receipts.Bytes
pattern and panic(err).

Addresses iteyelmp's review comment.

Co-Authored-By: Claude Opus 4.8 (1M context) <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.

3 participants