Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
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
9 changes: 6 additions & 3 deletions qkc/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,8 @@ func CreateMinorBlock(qkc *config.QuarkChainConfig, fullShardID uint32, root *ty
CrossShardGasUsed: &serialize.Uint256{Value: new(big.Int)},
// The cross-shard cursor starts at (root_height, 0, 0), matching
// pyquarkchain (quarkchain/genesis.py:92).
XShardTxCursor: types.XShardTxCursorInfo{RootBlockHeight: uint64(root.Number)},
XShardGasLimit: &serialize.Uint256{Value: big.NewInt(defaultXShardGasLimit)},
XShardTxCursorInfo: &types.XShardTxCursorInfo{RootBlockHeight: uint64(root.Number)},
XShardGasLimit: &serialize.Uint256{Value: big.NewInt(defaultXShardGasLimit)},
}
header := &types.MinorBlockHeader{
Version: g.Version,
Expand All @@ -188,7 +188,10 @@ func CreateMinorBlock(qkc *config.QuarkChainConfig, fullShardID uint32, root *ty
Difficulty: new(big.Int).SetUint64(g.Difficulty),
Extra: bytes.Clone(g.ExtraData),
}
return types.NewMinorBlock(header, meta, nil, nil), nil
// NewMinorBlockWithHeader, not NewMinorBlock: the latter derives meta.TxHash
// from the tx list, which would overwrite the HASH_MERKLE_ROOT the genesis
// config pins (and that header.MetaHash above already commits to).
return types.NewMinorBlockWithHeader(header, meta), nil
}

// ShardChainConfig returns the EVM rule set one shard runs: Petersburg-only,
Expand Down
26 changes: 13 additions & 13 deletions qkc/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func TestMinorGenesisGolden(t *testing.T) {
if err != nil {
t.Fatalf("CreateMinorBlock: %v", err)
}
h, m := block.Header, block.Meta
h, m := block.Header(), block.Meta()

if h.PrevRootBlockHash != common.HexToHash(want.RootGenesisHash) {
t.Errorf("PrevRootBlockHash = %s, want %s", h.PrevRootBlockHash, want.RootGenesisHash)
Expand All @@ -220,19 +220,19 @@ func TestMinorGenesisGolden(t *testing.T) {
MinorBlockIndex: want.XShardCursor[1],
XShardDepositIndex: want.XShardCursor[2],
}
if m.XShardTxCursor != wantCursor {
t.Errorf("XShardTxCursor = %+v, want %+v", m.XShardTxCursor, wantCursor)
if *m.XShardTxCursorInfo != wantCursor {
t.Errorf("XShardTxCursor = %+v, want %+v", *m.XShardTxCursorInfo, wantCursor)
}

// The whole point: materializing ALLOC and assembling the block must
// reproduce pyquarkchain's genesis block, byte for byte.
if got := block.Meta.Root; got != common.HexToHash(want.StateRoot) {
if got := block.Meta().Root; got != common.HexToHash(want.StateRoot) {
t.Errorf("genesis state root\n got %s\nwant %s", got.Hex(), want.StateRoot)
}
if got := block.Meta.Hash(); got != common.HexToHash(want.MetaHash) {
if got := block.Meta().Hash(); got != common.HexToHash(want.MetaHash) {
t.Errorf("genesis meta hash\n got %s\nwant %s", got.Hex(), want.MetaHash)
}
if got := block.Header.MetaHash; got != common.HexToHash(want.MetaHash) {
if got := block.Header().MetaHash; got != common.HexToHash(want.MetaHash) {
t.Errorf("header's meta hash\n got %s\nwant %s", got.Hex(), want.MetaHash)
}
if got := block.Hash(); got != common.HexToHash(want.HeaderHash) {
Expand All @@ -254,7 +254,7 @@ func TestCreateMinorBlock(t *testing.T) {
if err != nil {
t.Fatalf("CreateMinorBlock: %v", err)
}
h, m, sg := block.Header, block.Meta, shardCfg.Genesis
h, m, sg := block.Header(), block.Meta(), shardCfg.Genesis

if h.Branch.GetFullShardID() != firstShardID {
t.Errorf("branch = 0x%08x, want 0x%08x", h.Branch.GetFullShardID(), firstShardID)
Expand All @@ -280,8 +280,8 @@ func TestCreateMinorBlock(t *testing.T) {
if h.PrevRootBlockHash != root.Hash() {
t.Errorf("PrevRootBlockHash = %s, want the root genesis %s", h.PrevRootBlockHash, root.Hash())
}
if want := (types.XShardTxCursorInfo{RootBlockHeight: uint64(root.Number)}); m.XShardTxCursor != want {
t.Errorf("XShardTxCursor = %+v, want %+v", m.XShardTxCursor, want)
if want := (types.XShardTxCursorInfo{RootBlockHeight: uint64(root.Number)}); *m.XShardTxCursorInfo != want {
t.Errorf("XShardTxCursor = %+v, want %+v", *m.XShardTxCursorInfo, want)
}

// Meta: the config's merkle root, a materialized state root, and an
Expand Down Expand Up @@ -320,8 +320,8 @@ func TestCreateMinorBlock(t *testing.T) {
}

// The genesis body is empty.
if len(block.Transactions) != 0 || len(block.TrackingData) != 0 {
t.Errorf("body = %d txs / %d tracking bytes, want empty", len(block.Transactions), len(block.TrackingData))
if len(block.Transactions()) != 0 || len(block.TrackingData()) != 0 {
t.Errorf("body = %d txs / %d tracking bytes, want empty", len(block.Transactions()), len(block.TrackingData()))
}
})
}
Expand All @@ -346,8 +346,8 @@ func TestCommitGenesisState(t *testing.T) {
if err != nil {
t.Fatalf("CommitGenesisState: %v", err)
}
if stateRoot != block.Meta.Root {
t.Errorf("committed state root = %s, want the derived meta root %s", stateRoot, block.Meta.Root)
if stateRoot != block.Meta().Root {
t.Errorf("committed state root = %s, want the derived meta root %s", stateRoot, block.Meta().Root)
}
if !rawdb.HasLegacyTrieNode(db, stateRoot) {
t.Errorf("state root %s was not written to the database", stateRoot)
Expand Down
6 changes: 3 additions & 3 deletions qkc/shard/rawdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func WriteGenesisBlock(db ethdb.KeyValueWriter, block *types.MinorBlock) error {
// another shard's chaindb, a changed config, or a header whose meta and body no
// longer match it.
func ReconcileGenesisBlock(db ethdb.KeyValueStore, expected *types.MinorBlock, dbPath string) (existed bool, err error) {
fullShardID := expected.Header.Branch.GetFullShardID()
fullShardID := expected.Header().Branch.GetFullShardID()
storedData, err := readGenesisBlockBytes(db)
if err != nil {
return false, fmt.Errorf("shard 0x%08x: read genesis block (db %s): %w", fullShardID, dbPath, err)
Expand All @@ -118,7 +118,7 @@ func ReconcileGenesisBlock(db ethdb.KeyValueStore, expected *types.MinorBlock, d
}
// A chaindb holding another shard's genesis is a misplaced directory, not a
// config change — name the right cause.
if storedID := stored.Header.Branch.GetFullShardID(); storedID != fullShardID {
if storedID := stored.Header().Branch.GetFullShardID(); storedID != fullShardID {
return true, fmt.Errorf("shard 0x%08x: stored genesis belongs to shard 0x%08x (db %s) — misplaced chaindb",
fullShardID, storedID, dbPath)
}
Expand Down Expand Up @@ -163,7 +163,7 @@ func ReconcileGenesisBlock(db ethdb.KeyValueStore, expected *types.MinorBlock, d
// CheckCompatible's time argument is inert, and the ShardChain seam exposes no
// head timestamp to pass. A timestamp-scheduled fork would need both.
func ReconcileChainConfig(db ethdb.Database, genesis *types.MinorBlock, cfg *params.ChainConfig, head uint64, existed bool, dbPath string) error {
fullShardID := genesis.Header.Branch.GetFullShardID()
fullShardID := genesis.Header().Branch.GetFullShardID()
if cfg == nil {
return fmt.Errorf("shard 0x%08x: shard has no chain config (db %s)", fullShardID, dbPath)
}
Expand Down
20 changes: 10 additions & 10 deletions qkc/shard/rawdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,17 @@ func TestGenesisBlockRoundTrip(t *testing.T) {
if got.Hash() != want.Hash() {
t.Errorf("round-trip hash mismatch: got %s, want %s", got.Hash(), want.Hash())
}
if got.Meta.Hash() != want.Meta.Hash() {
t.Errorf("round-trip meta mismatch: got %s, want %s", got.Meta.Hash(), want.Meta.Hash())
if got.Meta().Hash() != want.Meta().Hash() {
t.Errorf("round-trip meta mismatch: got %s, want %s", got.Meta().Hash(), want.Meta().Hash())
}
if got.Meta.Root != want.Meta.Root {
t.Errorf("round-trip state root = %s, want %s", got.Meta.Root, want.Meta.Root)
if got.Root() != want.Root() {
t.Errorf("round-trip state root = %s, want %s", got.Root(), want.Root())
}
if got.Header.Branch.GetFullShardID() != firstShardID {
t.Errorf("round-trip branch = 0x%08x, want 0x%08x", got.Header.Branch.GetFullShardID(), firstShardID)
if got.Header().Branch.GetFullShardID() != firstShardID {
t.Errorf("round-trip branch = 0x%08x, want 0x%08x", got.Header().Branch.GetFullShardID(), firstShardID)
}
if len(got.Transactions) != 0 || len(got.TrackingData) != 0 {
t.Errorf("round-trip body = %d txs / %d tracking bytes, want empty", len(got.Transactions), len(got.TrackingData))
if len(got.Transactions()) != 0 || len(got.TrackingData()) != 0 {
t.Errorf("round-trip body = %d txs / %d tracking bytes, want empty", len(got.Transactions()), len(got.TrackingData()))
}
}

Expand Down Expand Up @@ -135,9 +135,9 @@ func TestReconcileGenesisBlockTamperedMeta(t *testing.T) {
db := rawdb.NewMemoryDatabase()
expected := testGenesisBlock(t, fixtureMainnet)

meta := *expected.Meta
meta := *expected.Meta()
meta.Root = common.HexToHash("0xdead")
tampered := types.NewMinorBlock(expected.Header, &meta, nil, nil)
tampered := types.NewMinorBlockWithHeader(expected.Header(), &meta)
if tampered.Hash() != expected.Hash() {
t.Fatal("tampering with the meta moved the block hash: this test no longer covers what it claims")
}
Expand Down
8 changes: 4 additions & 4 deletions qkc/shard/shard.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ func New(ctx *config.SlaveContext, branch account.Branch, rootGenesis *types.Roo
// The block was derived from a hash of the same allocation. If the
// flushed root disagrees, the chain would open on a genesis whose state
// is not the one below it.
if stateRoot != genesis.Meta.Root {
return fmt.Errorf("committed genesis state %s does not match the derived genesis root %s", stateRoot, genesis.Meta.Root)
if stateRoot != genesis.Root() {
return fmt.Errorf("committed genesis state %s does not match the derived genesis root %s", stateRoot, genesis.Root())
}
return nil
},
Expand Down Expand Up @@ -177,7 +177,7 @@ type genesisSetup struct {
// the genesis block only once the chain is standing. It stops a constructed chain
// on failure; the caller retains ownership of the db.
func initializeChain(db ethdb.Database, dbPath string, g genesisSetup, service ChainService) (ShardChain, bool, error) {
fullShardID := g.block.Header.Branch.GetFullShardID()
fullShardID := g.block.Header().Branch.GetFullShardID()
existed, err := ReconcileGenesisBlock(db, g.block, dbPath)
if err != nil {
return nil, false, err
Expand All @@ -188,7 +188,7 @@ func initializeChain(db ethdb.Database, dbPath string, g genesisSetup, service C
if err := g.commit(db); err != nil {
return nil, false, fmt.Errorf("shard 0x%08x: commit genesis state (db %s): %w", fullShardID, dbPath, err)
}
} else if err := qkc.CheckGenesisState(db, g.block.Meta.Root); err != nil {
} else if err := qkc.CheckGenesisState(db, g.block.Root()); err != nil {
// On reopen the state is already there — unless the datadir lost it. The
// stored genesis is an identity and says nothing about the trie under it, so
// the two are checked separately. Re-materializing here would repair a
Expand Down
17 changes: 9 additions & 8 deletions qkc/shard/shard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ func TestShardNewAndReopen(t *testing.T) {

// Deriving the block persists nothing, so the state is here only
// because the fresh path flushed it into the shard's own database.
if !rawdb.HasLegacyTrieNode(s.DB(), genesis.Meta.Root) {
t.Errorf("genesis state root %s is missing from the shard db", genesis.Meta.Root)
if !rawdb.HasLegacyTrieNode(s.DB(), genesis.Root()) {
t.Errorf("genesis state root %s is missing from the shard db", genesis.Root())
}

// The genesis block is stored, and carries the shard's root linkage
Expand All @@ -107,10 +107,11 @@ func TestShardNewAndReopen(t *testing.T) {
if stored.Hash() != genesis.Hash() {
t.Errorf("stored genesis %s, want %s", stored.Hash(), genesis.Hash())
}
if stored.Header.Branch.GetFullShardID() != firstShardID ||
stored.Header.PrevRootBlockHash != root.Hash() ||
stored.Meta.XShardTxCursor != (types.XShardTxCursorInfo{RootBlockHeight: uint64(root.Number)}) {
t.Errorf("stored genesis %+v inconsistent with config derivation", stored.Header)
cursor := stored.Meta().XShardTxCursorInfo
if stored.Header().Branch.GetFullShardID() != firstShardID ||
stored.PrevRootBlockHash() != root.Hash() || cursor == nil ||
*cursor != (types.XShardTxCursorInfo{RootBlockHeight: uint64(root.Number)}) {
t.Errorf("stored genesis %+v inconsistent with config derivation", stored.Header())
}

if err := s.Stop(); err != nil {
Expand Down Expand Up @@ -230,7 +231,7 @@ func TestShardReopenMissingGenesisState(t *testing.T) {

// Drop the genesis state while leaving the stored genesis block intact.
withDB(t, datadir, func(db ethdb.Database) {
rawdb.DeleteLegacyTrieNode(db, genesis.Meta.Root)
rawdb.DeleteLegacyTrieNode(db, genesis.Root())
})

_, err = New(ctx, branch, root, datadir, Options{})
Expand All @@ -242,7 +243,7 @@ func TestShardReopenMissingGenesisState(t *testing.T) {
// The failed boot did not quietly re-materialize what was lost: a corrupt
// datadir stays corrupt until an operator looks at it.
withDB(t, datadir, func(db ethdb.Database) {
if rawdb.HasLegacyTrieNode(db, genesis.Meta.Root) {
if rawdb.HasLegacyTrieNode(db, genesis.Root()) {
t.Error("the failed reopen rewrote the genesis state instead of reporting corruption")
}
})
Expand Down
46 changes: 46 additions & 0 deletions qkc/types/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2026-2027, QuarkChain.

// QKC block interfaces follow pyquarkchain-compatible type boundaries.

package types

import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/qkc/account"
qkcCommon "github.com/ethereum/go-ethereum/qkc/common"
)

type IHeader interface {
Hash() common.Hash
SealHash() common.Hash
NumberU64() uint64
GetVersion() uint32
GetParentHash() common.Hash
GetCoinbase() account.Address
GetTime() uint64
GetCoinbaseAmount() *qkcCommon.TokenBalances
GetDifficulty() *big.Int
GetNonce() uint64
GetExtra() []byte
GetMixDigest() common.Hash
}

type IBlock interface {
Comment thread
ping-ke marked this conversation as resolved.
Hash() common.Hash
NumberU64() uint64
IHeader() IHeader
WithMiningResult(nonce uint64, mixDigest common.Hash, signature *[65]byte) IBlock
Content() []IHashable
GetTrackingData() []byte
GetSize() common.StorageSize
ParentHash() common.Hash
Coinbase() account.Address
Time() uint64
Difficulty() *big.Int
}

type IHashable interface {
Hash() common.Hash
}
Loading