From 0961cb753cb029a1517260aa15b4848641879114 Mon Sep 17 00:00:00 2001 From: syntrust Date: Tue, 9 Jun 2026 14:39:40 +0800 Subject: [PATCH] account and genesis --- core/state/quarkchain_account.go | 268 +++++++++ core/state/quarkchain_account_test.go | 191 +++++++ core/state/quarkchain_genesis.go | 303 ++++++++++ core/state/quarkchain_genesis_test.go | 166 ++++++ .../mainnet/cluster_config_template.json | 517 ++++++++++++++++++ 5 files changed, 1445 insertions(+) create mode 100644 core/state/quarkchain_account.go create mode 100644 core/state/quarkchain_account_test.go create mode 100644 core/state/quarkchain_genesis.go create mode 100644 core/state/quarkchain_genesis_test.go create mode 100644 quarkchain/mainnet/cluster_config_template.json diff --git a/core/state/quarkchain_account.go b/core/state/quarkchain_account.go new file mode 100644 index 000000000000..767c5df85282 --- /dev/null +++ b/core/state/quarkchain_account.go @@ -0,0 +1,268 @@ +// Copyright 2026-2027, QuarkChain. + +package state + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "math/big" + "slices" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" + "github.com/ethereum/go-ethereum/trie/trienode" + "github.com/ethereum/go-ethereum/triedb" +) + +const QuarkChainTokenTrieThreshold = 16 + +// QuarkChainAccount is the QKC EVM account shape stored in the account trie. +// Storage and Code are genesis-construction inputs and are not encoded directly. +type QuarkChainAccount struct { + Nonce uint64 + TokenBalances map[uint64]*big.Int + StorageRoot common.Hash + Storage map[common.Hash]common.Hash + CodeHash []byte + Code []byte + FullShardKey uint32 + Optional []byte +} + +type quarkChainAccountRLP struct { + Nonce uint64 + TokenBalances []byte + StorageRoot common.Hash + CodeHash []byte + FullShardKey quarkChainUint32 + Optional []byte +} + +type quarkChainUint32 uint32 + +func (u quarkChainUint32) EncodeRLP(w io.Writer) error { + var enc [5]byte + enc[0] = 0x84 + binary.BigEndian.PutUint32(enc[1:], uint32(u)) + _, err := w.Write(enc[:]) + return err +} + +func (u *quarkChainUint32) DecodeRLP(s *rlp.Stream) error { + raw, err := s.Raw() + if err != nil { + return err + } + if len(raw) != 5 || raw[0] != 0x84 { + return fmt.Errorf("invalid qkc uint32 rlp encoding: %x", raw) + } + *u = quarkChainUint32(binary.BigEndian.Uint32(raw[1:])) + return nil +} + +type quarkChainTokenBalancePair struct { + TokenID uint64 + Balance *big.Int +} + +// NewQuarkChainMemoryTrieDB returns an ephemeral trie database suitable for +// computing QKC state roots without polluting an existing chain database. +func NewQuarkChainMemoryTrieDB() *triedb.Database { + return triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.HashDefaults) +} + +// QuarkChainTokenIDKey returns QKC's 32-byte big-endian token trie key. +func QuarkChainTokenIDKey(tokenID uint64) []byte { + key := make([]byte, 32) + binary.BigEndian.PutUint64(key[24:], tokenID) + return key +} + +// EncodeQuarkChainTokenBalances serializes token balances using QKC's account +// field encoding. Large balance sets are committed into a token secure trie. +func EncodeQuarkChainTokenBalances(balances map[uint64]*big.Int, db *triedb.Database) ([]byte, error) { + pairs, err := quarkChainTokenPairs(balances) + if err != nil { + return nil, err + } + if len(pairs) == 0 { + return nil, nil + } + if len(pairs) <= QuarkChainTokenTrieThreshold { + payload, err := rlp.EncodeToBytes(pairs) + if err != nil { + return nil, err + } + return append([]byte{0x00}, payload...), nil + } + if db == nil { + db = NewQuarkChainMemoryTrieDB() + } + tokenTrie, err := trie.NewSecure(types.EmptyRootHash, common.Hash{}, types.EmptyRootHash, db) + if err != nil { + return nil, err + } + for _, pair := range pairs { + value, err := rlp.EncodeToBytes(pair.Balance) + if err != nil { + return nil, err + } + tokenTrie.MustUpdate(QuarkChainTokenIDKey(pair.TokenID), value) + } + root, nodes := tokenTrie.Commit(false) + if err := quarkChainCommitTrie(db, root, types.EmptyRootHash, nodes); err != nil { + return nil, err + } + encoded := make([]byte, 33) + encoded[0] = 0x01 + copy(encoded[1:], root.Bytes()) + return encoded, nil +} + +// BuildQuarkChainStorageRoot builds the QKC storage trie root for genesis data. +func BuildQuarkChainStorageRoot(storage map[common.Hash]common.Hash, db *triedb.Database) (common.Hash, error) { + if len(storage) == 0 { + return types.EmptyRootHash, nil + } + if db == nil { + db = NewQuarkChainMemoryTrieDB() + } + storageTrie, err := trie.NewSecure(types.EmptyRootHash, common.Hash{}, types.EmptyRootHash, db) + if err != nil { + return common.Hash{}, err + } + keys := make([]common.Hash, 0, len(storage)) + for key := range storage { + keys = append(keys, key) + } + slices.SortFunc(keys, func(a, b common.Hash) int { + return bytes.Compare(a[:], b[:]) + }) + for _, key := range keys { + value := storage[key] + if value == (common.Hash{}) { + continue + } + encoded, err := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) + if err != nil { + return common.Hash{}, err + } + storageTrie.MustUpdate(key[:], encoded) + } + root, nodes := storageTrie.Commit(false) + if err := quarkChainCommitTrie(db, root, types.EmptyRootHash, nodes); err != nil { + return common.Hash{}, err + } + return root, nil +} + +// BuildQuarkChainStateRoot builds the QKC account trie root for the provided +// accounts. The trie key is the 20-byte EVM recipient address. +func BuildQuarkChainStateRoot(accounts map[common.Address]QuarkChainAccount, db *triedb.Database) (common.Hash, error) { + if len(accounts) == 0 { + return types.EmptyRootHash, nil + } + if db == nil { + db = NewQuarkChainMemoryTrieDB() + } + accountTrie, err := trie.NewSecure(types.EmptyRootHash, common.Hash{}, types.EmptyRootHash, db) + if err != nil { + return common.Hash{}, err + } + addresses := make([]common.Address, 0, len(accounts)) + for addr := range accounts { + addresses = append(addresses, addr) + } + slices.SortFunc(addresses, func(a, b common.Address) int { + return bytes.Compare(a[:], b[:]) + }) + for _, addr := range addresses { + blob, err := EncodeQuarkChainAccount(accounts[addr], db) + if err != nil { + return common.Hash{}, fmt.Errorf("encode qkc account %s: %w", addr, err) + } + accountTrie.MustUpdate(addr.Bytes(), blob) + } + root, nodes := accountTrie.Commit(false) + if err := quarkChainCommitTrie(db, root, types.EmptyRootHash, nodes); err != nil { + return common.Hash{}, err + } + return root, nil +} + +// EncodeQuarkChainAccount encodes a QKC account as it appears in the QKC state trie. +func EncodeQuarkChainAccount(account QuarkChainAccount, db *triedb.Database) ([]byte, error) { + tokenBalances, err := EncodeQuarkChainTokenBalances(account.TokenBalances, db) + if err != nil { + return nil, err + } + storageRoot := account.StorageRoot + if len(account.Storage) != 0 { + storageRoot, err = BuildQuarkChainStorageRoot(account.Storage, db) + if err != nil { + return nil, err + } + } + if storageRoot == (common.Hash{}) { + storageRoot = types.EmptyRootHash + } + codeHash := account.CodeHash + if len(codeHash) == 0 { + if len(account.Code) != 0 { + hash := crypto.Keccak256Hash(account.Code) + codeHash = hash.Bytes() + } else { + codeHash = types.EmptyCodeHash.Bytes() + } + } + if len(codeHash) != common.HashLength { + return nil, fmt.Errorf("invalid qkc code hash length %d", len(codeHash)) + } + return rlp.EncodeToBytes(quarkChainAccountRLP{ + Nonce: account.Nonce, + TokenBalances: tokenBalances, + StorageRoot: storageRoot, + CodeHash: codeHash, + FullShardKey: quarkChainUint32(account.FullShardKey), + Optional: account.Optional, + }) +} + +func quarkChainTokenPairs(balances map[uint64]*big.Int) ([]quarkChainTokenBalancePair, error) { + if len(balances) == 0 { + return nil, nil + } + ids := make([]uint64, 0, len(balances)) + for tokenID, balance := range balances { + if balance == nil || balance.Sign() == 0 { + continue + } + if balance.Sign() < 0 { + return nil, errors.New("qkc token balance cannot be negative") + } + ids = append(ids, tokenID) + } + slices.Sort(ids) + pairs := make([]quarkChainTokenBalancePair, 0, len(ids)) + for _, tokenID := range ids { + pairs = append(pairs, quarkChainTokenBalancePair{ + TokenID: tokenID, + Balance: new(big.Int).Set(balances[tokenID]), + }) + } + return pairs, nil +} + +func quarkChainCommitTrie(db *triedb.Database, root common.Hash, parent common.Hash, nodes *trienode.NodeSet) error { + if db == nil || nodes == nil { + return nil + } + return db.Update(root, parent, 0, trienode.NewWithNodeSet(nodes), nil) +} diff --git a/core/state/quarkchain_account_test.go b/core/state/quarkchain_account_test.go new file mode 100644 index 000000000000..a1b15049155c --- /dev/null +++ b/core/state/quarkchain_account_test.go @@ -0,0 +1,191 @@ +// Copyright 2026-2027, QuarkChain. + +package state + +import ( + "bytes" + "encoding/hex" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +func TestQuarkChainTokenIDKey(t *testing.T) { + key := QuarkChainTokenIDKey(0x0102030405060708) + want := common.FromHex("0x0000000000000000000000000000000000000000000000000102030405060708") + if !bytes.Equal(key, want) { + t.Fatalf("wrong token key: got %x want %x", key, want) + } +} + +func TestQuarkChainUint32RLP(t *testing.T) { + blob, err := rlp.EncodeToBytes(quarkChainUint32(0x01020304)) + if err != nil { + t.Fatal(err) + } + if want := "8401020304"; hex.EncodeToString(blob) != want { + t.Fatalf("wrong qkc uint32 rlp: got %x want %s", blob, want) + } + var decoded quarkChainUint32 + if err := rlp.DecodeBytes(blob, &decoded); err != nil { + t.Fatal(err) + } + if decoded != quarkChainUint32(0x01020304) { + t.Fatalf("wrong decoded value: got %#x", uint32(decoded)) + } +} + +func TestQuarkChainTokenBalancesInline(t *testing.T) { + encoded, err := EncodeQuarkChainTokenBalances(map[uint64]*big.Int{ + 3: big.NewInt(4), + 1: big.NewInt(2), + 2: big.NewInt(0), + }, nil) + if err != nil { + t.Fatal(err) + } + if len(encoded) == 0 || encoded[0] != 0x00 { + t.Fatalf("expected inline token balance encoding, got %x", encoded) + } + var pairs []quarkChainTokenBalancePair + if err := rlp.DecodeBytes(encoded[1:], &pairs); err != nil { + t.Fatal(err) + } + if len(pairs) != 2 { + t.Fatalf("wrong pair count: got %d", len(pairs)) + } + if pairs[0].TokenID != 1 || pairs[0].Balance.Cmp(big.NewInt(2)) != 0 { + t.Fatalf("wrong first pair: %#v", pairs[0]) + } + if pairs[1].TokenID != 3 || pairs[1].Balance.Cmp(big.NewInt(4)) != 0 { + t.Fatalf("wrong second pair: %#v", pairs[1]) + } +} + +func TestQuarkChainTokenBalancesTrie(t *testing.T) { + db := NewQuarkChainMemoryTrieDB() + balances := make(map[uint64]*big.Int) + for i := uint64(1); i <= QuarkChainTokenTrieThreshold+1; i++ { + balances[i] = new(big.Int).SetUint64(i * 100) + } + encoded, err := EncodeQuarkChainTokenBalances(balances, db) + if err != nil { + t.Fatal(err) + } + if len(encoded) != 33 || encoded[0] != 0x01 { + t.Fatalf("expected token trie encoding, got %x", encoded) + } + root := common.BytesToHash(encoded[1:]) + tokenTrie, err := trie.NewSecure(root, common.Hash{}, root, db) + if err != nil { + t.Fatal(err) + } + for tokenID, wantBalance := range balances { + blob := tokenTrie.MustGet(QuarkChainTokenIDKey(tokenID)) + if len(blob) == 0 { + t.Fatalf("missing token %d", tokenID) + } + var got big.Int + if err := rlp.DecodeBytes(blob, &got); err != nil { + t.Fatal(err) + } + if got.Cmp(wantBalance) != 0 { + t.Fatalf("wrong token %d balance: got %s want %s", tokenID, &got, wantBalance) + } + } +} + +func TestBuildQuarkChainStorageRoot(t *testing.T) { + db := NewQuarkChainMemoryTrieDB() + storage := map[common.Hash]common.Hash{ + common.HexToHash("0x01"): common.HexToHash("0x02"), + common.HexToHash("0x03"): {}, + } + root, err := BuildQuarkChainStorageRoot(storage, db) + if err != nil { + t.Fatal(err) + } + if root == types.EmptyRootHash { + t.Fatal("expected non-empty storage root") + } + storageTrie, err := trie.NewSecure(root, common.Hash{}, root, db) + if err != nil { + t.Fatal(err) + } + blob := storageTrie.MustGet(common.HexToHash("0x01").Bytes()) + _, content, _, err := rlp.Split(blob) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(content, []byte{0x02}) { + t.Fatalf("wrong storage content: got %x", content) + } + if blob := storageTrie.MustGet(common.HexToHash("0x03").Bytes()); len(blob) != 0 { + t.Fatalf("zero storage value should be omitted, got %x", blob) + } +} + +func TestBuildQuarkChainStateRoot(t *testing.T) { + db := NewQuarkChainMemoryTrieDB() + addrA := common.HexToAddress("0x1111111111111111111111111111111111111111") + addrB := common.HexToAddress("0x2222222222222222222222222222222222222222") + accounts := map[common.Address]QuarkChainAccount{ + addrB: { + TokenBalances: map[uint64]*big.Int{2: big.NewInt(200)}, + FullShardKey: 0x00020003, + }, + addrA: { + Nonce: 1, + TokenBalances: map[uint64]*big.Int{1: big.NewInt(100)}, + Storage: map[common.Hash]common.Hash{ + common.HexToHash("0x01"): common.HexToHash("0x0a"), + }, + Code: []byte{0x60, 0x00}, + FullShardKey: 0x00010002, + }, + } + root, err := BuildQuarkChainStateRoot(accounts, db) + if err != nil { + t.Fatal(err) + } + rootAgain, err := BuildQuarkChainStateRoot(accounts, NewQuarkChainMemoryTrieDB()) + if err != nil { + t.Fatal(err) + } + if root != rootAgain { + t.Fatalf("state root is not deterministic: got %s want %s", root, rootAgain) + } + accountTrie, err := trie.NewSecure(root, common.Hash{}, root, db) + if err != nil { + t.Fatal(err) + } + blob := accountTrie.MustGet(addrA.Bytes()) + if len(blob) == 0 { + t.Fatal("missing account") + } + var account quarkChainAccountRLP + if err := rlp.DecodeBytes(blob, &account); err != nil { + t.Fatal(err) + } + if account.Nonce != 1 { + t.Fatalf("wrong nonce: got %d", account.Nonce) + } + if uint32(account.FullShardKey) != 0x00010002 { + t.Fatalf("wrong full shard key: got %#x", uint32(account.FullShardKey)) + } + if account.StorageRoot == types.EmptyRootHash { + t.Fatal("expected non-empty account storage root") + } + if !bytes.Equal(account.CodeHash, common.BytesToHash([]byte{ + 0x07, 0xad, 0x11, 0x8d, 0x6c, 0xc8, 0x64, 0x2c, + 0x86, 0xc0, 0x38, 0x27, 0xf2, 0x76, 0xd8, 0xb7, + 0x91, 0xa6, 0x5e, 0x5c, 0x99, 0xa3, 0x84, 0x5f, + 0xaf, 0x18, 0x6b, 0xe7, 0x20, 0xa1, 0x45, 0x5d, + }).Bytes()) { + t.Fatalf("wrong code hash: got %x", account.CodeHash) + } +} diff --git a/core/state/quarkchain_genesis.go b/core/state/quarkchain_genesis.go new file mode 100644 index 000000000000..ee1756abd779 --- /dev/null +++ b/core/state/quarkchain_genesis.go @@ -0,0 +1,303 @@ +// Copyright 2026-2027, QuarkChain. + +package state + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/triedb" +) + +// QuarkChainClusterGenesisConfig is the minimal subset of a goquarkchain +// cluster config needed to build QKC genesis EVM state roots. +type QuarkChainClusterGenesisConfig struct { + GenesisDir *string `json:"GENESIS_DIR"` + QuarkChain QuarkChainGenesisConfig `json:"QUARKCHAIN"` +} + +type QuarkChainGenesisConfig struct { + GenesisToken string `json:"GENESIS_TOKEN"` + BaseEthChainID uint32 `json:"BASE_ETH_CHAIN_ID"` + Chains []QuarkChainGenesisChain `json:"CHAINS"` +} + +type QuarkChainGenesisChain struct { + ChainID uint32 `json:"CHAIN_ID"` + ShardSize uint32 `json:"SHARD_SIZE"` + Genesis *QuarkChainShardGenesis `json:"GENESIS"` +} + +type QuarkChainShardGenesis struct { + Alloc map[QuarkChainGenesisAddress]QuarkChainGenesisAllocation +} + +type QuarkChainGenesisAddress struct { + Recipient common.Address + FullShardKey uint32 +} + +type QuarkChainGenesisAllocation struct { + Balances map[string]*big.Int + Code []byte + CodeSet bool + Storage map[common.Hash]common.Hash +} + +type quarkChainShardGenesisJSON struct { + Alloc map[string]QuarkChainGenesisAllocation `json:"ALLOC"` +} + +func ReadQuarkChainClusterGenesisConfig(r io.Reader) (*QuarkChainClusterGenesisConfig, error) { + var config QuarkChainClusterGenesisConfig + if err := json.NewDecoder(r).Decode(&config); err != nil { + return nil, err + } + return &config, nil +} + +func ParseQuarkChainClusterGenesisConfig(input []byte) (*QuarkChainClusterGenesisConfig, error) { + return ReadQuarkChainClusterGenesisConfig(bytes.NewReader(input)) +} + +func (s *QuarkChainShardGenesis) UnmarshalJSON(input []byte) error { + var dec quarkChainShardGenesisJSON + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + s.Alloc = make(map[QuarkChainGenesisAddress]QuarkChainGenesisAllocation, len(dec.Alloc)) + for raw, alloc := range dec.Alloc { + addr, err := ParseQuarkChainGenesisAddress(raw) + if err != nil { + return err + } + s.Alloc[addr] = alloc + } + return nil +} + +func (a *QuarkChainGenesisAllocation) UnmarshalJSON(input []byte) error { + var fields map[string]json.RawMessage + if err := json.Unmarshal(input, &fields); err != nil { + return err + } + if _, ok := fields["balances"]; !ok { + if _, ok := fields["code"]; !ok { + if _, ok := fields["storage"]; !ok { + var balances map[string]*big.Int + if err := json.Unmarshal(input, &balances); err != nil { + return err + } + a.Balances = balances + return nil + } + } + } + if raw := fields["balances"]; len(raw) != 0 { + if err := json.Unmarshal(raw, &a.Balances); err != nil { + return err + } + } + if raw := fields["code"]; len(raw) != 0 { + var code string + if err := json.Unmarshal(raw, &code); err != nil { + return err + } + blob, err := quarkChainDecodeHex(code) + if err != nil { + return err + } + a.Code = blob + a.CodeSet = true + } + if raw := fields["storage"]; len(raw) != 0 { + var storage map[quarkChainGenesisHash]quarkChainGenesisHash + if err := json.Unmarshal(raw, &storage); err != nil { + return err + } + a.Storage = make(map[common.Hash]common.Hash, len(storage)) + for key, value := range storage { + a.Storage[common.Hash(key)] = common.Hash(value) + } + } + return nil +} + +// ParseQuarkChainGenesisAddress parses a 24-byte QKC address: +// 20-byte recipient followed by a 4-byte big-endian full shard key. +func ParseQuarkChainGenesisAddress(input string) (QuarkChainGenesisAddress, error) { + blob, err := quarkChainDecodeHex(input) + if err != nil { + return QuarkChainGenesisAddress{}, err + } + if len(blob) != common.AddressLength+4 { + return QuarkChainGenesisAddress{}, fmt.Errorf("invalid qkc genesis address length %d", len(blob)) + } + return QuarkChainGenesisAddress{ + Recipient: common.BytesToAddress(blob[:common.AddressLength]), + FullShardKey: binary.BigEndian.Uint32(blob[common.AddressLength:]), + }, nil +} + +func (a QuarkChainGenesisAddress) FullShardID(shardSize uint32) (uint32, error) { + if shardSize == 0 || shardSize&(shardSize-1) != 0 { + return 0, fmt.Errorf("invalid qkc shard size %d", shardSize) + } + chainID := a.FullShardKey >> 16 + shardID := a.FullShardKey & (shardSize - 1) + return chainID<<16 | shardSize | shardID, nil +} + +func (c *QuarkChainClusterGenesisConfig) GenesisAccountsByFullShardID() (map[uint32]map[common.Address]QuarkChainAccount, error) { + if c == nil { + return nil, errors.New("nil qkc genesis config") + } + accountsByShard := make(map[uint32]map[common.Address]QuarkChainAccount) + for _, chain := range c.QuarkChain.Chains { + if chain.Genesis == nil { + continue + } + for qkcAddress, alloc := range chain.Genesis.Alloc { + if got := qkcAddress.FullShardKey >> 16; got != chain.ChainID { + return nil, fmt.Errorf("qkc genesis address chain id mismatch: address has %d, chain has %d", got, chain.ChainID) + } + fullShardID, err := qkcAddress.FullShardID(chain.ShardSize) + if err != nil { + return nil, err + } + account, err := alloc.toQuarkChainAccount(qkcAddress) + if err != nil { + return nil, err + } + accounts := accountsByShard[fullShardID] + if accounts == nil { + accounts = make(map[common.Address]QuarkChainAccount) + accountsByShard[fullShardID] = accounts + } + if _, exists := accounts[qkcAddress.Recipient]; exists { + return nil, fmt.Errorf("duplicate qkc genesis recipient %s in full shard %d", qkcAddress.Recipient, fullShardID) + } + accounts[qkcAddress.Recipient] = account + } + } + return accountsByShard, nil +} + +func (c *QuarkChainClusterGenesisConfig) BuildGenesisStateRoots(db *triedb.Database) (map[uint32]common.Hash, error) { + accountsByShard, err := c.GenesisAccountsByFullShardID() + if err != nil { + return nil, err + } + if db == nil { + db = NewQuarkChainMemoryTrieDB() + } + roots := make(map[uint32]common.Hash, len(accountsByShard)) + for fullShardID, accounts := range accountsByShard { + root, err := BuildQuarkChainStateRoot(accounts, db) + if err != nil { + return nil, fmt.Errorf("build qkc genesis state root for full shard %d: %w", fullShardID, err) + } + roots[fullShardID] = root + } + return roots, nil +} + +func (a QuarkChainGenesisAllocation) toQuarkChainAccount(addr QuarkChainGenesisAddress) (QuarkChainAccount, error) { + balances := make(map[uint64]*big.Int, len(a.Balances)) + for token, balance := range a.Balances { + tokenID, err := QuarkChainTokenIDEncode(token) + if err != nil { + return QuarkChainAccount{}, err + } + if balance == nil { + continue + } + balances[tokenID] = new(big.Int).Set(balance) + } + var nonce uint64 + if a.CodeSet { + nonce = 1 + } + return QuarkChainAccount{ + Nonce: nonce, + TokenBalances: balances, + Storage: a.Storage, + Code: a.Code, + FullShardKey: addr.FullShardKey, + }, nil +} + +func QuarkChainTokenIDEncode(token string) (uint64, error) { + if len(token) == 0 { + return 0, errors.New("empty qkc token symbol") + } + if len(token) >= 13 { + return 0, fmt.Errorf("qkc token symbol too long: %s", token) + } + id, err := quarkChainTokenCharEncode(token[len(token)-1]) + if err != nil { + return 0, err + } + base := uint64(36) + for i := len(token) - 2; i >= 0; i-- { + ch, err := quarkChainTokenCharEncode(token[i]) + if err != nil { + return 0, err + } + id += base * (ch + 1) + base *= 36 + if i == 0 { + break + } + } + return id, nil +} + +func quarkChainTokenCharEncode(ch byte) (uint64, error) { + switch { + case ch >= '0' && ch <= '9': + return uint64(ch - '0'), nil + case ch >= 'A' && ch <= 'Z': + return 10 + uint64(ch-'A'), nil + default: + return 0, fmt.Errorf("invalid qkc token symbol character %q", ch) + } +} + +type quarkChainGenesisHash common.Hash + +func (h *quarkChainGenesisHash) UnmarshalText(text []byte) error { + text = bytes.TrimPrefix(text, []byte("0x")) + if len(text) > common.HashLength*2 { + return fmt.Errorf("too many hex characters in qkc genesis hash %q", text) + } + if len(text)%2 == 1 { + text = append([]byte{'0'}, text...) + } + offset := len(h) - len(text)/2 + if _, err := hex.Decode((*h)[offset:], text); err != nil { + return fmt.Errorf("invalid qkc genesis hash %q", text) + } + return nil +} + +func quarkChainDecodeHex(input string) ([]byte, error) { + input = strings.TrimPrefix(input, "0x") + input = strings.TrimPrefix(input, "0X") + if len(input)%2 == 1 { + input = "0" + input + } + blob, err := hex.DecodeString(input) + if err != nil { + return nil, err + } + return blob, nil +} diff --git a/core/state/quarkchain_genesis_test.go b/core/state/quarkchain_genesis_test.go new file mode 100644 index 000000000000..5ffb3ca2ff5e --- /dev/null +++ b/core/state/quarkchain_genesis_test.go @@ -0,0 +1,166 @@ +// Copyright 2026-2027, QuarkChain. + +package state + +import ( + "bytes" + "math/big" + "os" + "path/filepath" + "runtime" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +func TestQuarkChainTokenIDEncode(t *testing.T) { + tests := []struct { + token string + want uint64 + }{ + {"0", 0}, + {"A", 10}, + {"QKC", 35760}, + } + for _, tt := range tests { + got, err := QuarkChainTokenIDEncode(tt.token) + if err != nil { + t.Fatal(err) + } + if got != tt.want { + t.Fatalf("wrong token id for %s: got %d want %d", tt.token, got, tt.want) + } + } + if _, err := QuarkChainTokenIDEncode("qkc"); err == nil { + t.Fatal("expected lowercase token symbol to fail") + } +} + +func TestParseQuarkChainGenesisAddress(t *testing.T) { + addr, err := ParseQuarkChainGenesisAddress("32c53C6c2B57B2026a51C87aDD0695F5AeEd3f2e000075b2") + if err != nil { + t.Fatal(err) + } + if addr.Recipient != common.HexToAddress("0x32c53C6c2B57B2026a51C87aDD0695F5AeEd3f2e") { + t.Fatalf("wrong recipient: got %s", addr.Recipient) + } + if addr.FullShardKey != 0x75b2 { + t.Fatalf("wrong full shard key: got %#x", addr.FullShardKey) + } + fullShardID, err := addr.FullShardID(1) + if err != nil { + t.Fatal(err) + } + if fullShardID != 1 { + t.Fatalf("wrong full shard id: got %#x", fullShardID) + } +} + +func TestParseQuarkChainGenesisAllocFormats(t *testing.T) { + const input = `{ + "GENESIS_DIR": null, + "QUARKCHAIN": { + "GENESIS_TOKEN": "QKC", + "BASE_ETH_CHAIN_ID": 100000, + "CHAINS": [{ + "CHAIN_ID": 0, + "SHARD_SIZE": 1, + "GENESIS": { + "ALLOC": { + "111111111111111111111111111111111111111100000000": {"QKC": 7}, + "222222222222222222222222222222222222222200000000": { + "balances": {"QKC": 9, "QETC": 11}, + "code": "0x6000", + "storage": {"0x01": "0x02"} + } + } + } + }] + } + }` + config, err := ParseQuarkChainClusterGenesisConfig([]byte(input)) + if err != nil { + t.Fatal(err) + } + accountsByShard, err := config.GenesisAccountsByFullShardID() + if err != nil { + t.Fatal(err) + } + accounts := accountsByShard[1] + if len(accounts) != 2 { + t.Fatalf("wrong account count: got %d", len(accounts)) + } + qkcID, err := QuarkChainTokenIDEncode("QKC") + if err != nil { + t.Fatal(err) + } + account := accounts[common.HexToAddress("0x2222222222222222222222222222222222222222")] + if account.Nonce != 1 { + t.Fatalf("code-bearing genesis account should have nonce 1, got %d", account.Nonce) + } + if account.TokenBalances[qkcID].Cmp(big.NewInt(9)) != 0 { + t.Fatalf("wrong qkc balance: got %s", account.TokenBalances[qkcID]) + } + if !bytes.Equal(account.Code, []byte{0x60, 0x00}) { + t.Fatalf("wrong code: got %x", account.Code) + } + if account.Storage[common.HexToHash("0x01")] != common.HexToHash("0x02") { + t.Fatalf("wrong storage value: got %s", account.Storage[common.HexToHash("0x01")]) + } + roots, err := config.BuildGenesisStateRoots(nil) + if err != nil { + t.Fatal(err) + } + if roots[1] == (common.Hash{}) || roots[1] == types.EmptyRootHash { + t.Fatalf("expected non-empty genesis state root, got %s", roots[1]) + } +} + +func TestQuarkChainMainnetGenesisStateRoots(t *testing.T) { + path := filepath.Join(quarkChainTestRepoRoot(t), "quarkchain", "mainnet", "cluster_config_template.json") + file, err := os.Open(path) + if err != nil { + t.Fatal(err) + } + defer file.Close() + + config, err := ReadQuarkChainClusterGenesisConfig(file) + if err != nil { + t.Fatal(err) + } + roots, err := config.BuildGenesisStateRoots(nil) + if err != nil { + t.Fatal(err) + } + expected := map[uint32]common.Hash{ + 1: common.HexToHash("0x699737e3597ea304b7d2e2f4ecbf8ab6348688287c59cec8599cf7a4f7c82153"), + 65537: common.HexToHash("0x5014e24903eca74b3f357010227aa1c70f089eba88f9edfacee8b660eddb6739"), + 131073: common.HexToHash("0x767dc124493f23fe4294c5394dce509e747beabea3039dfcc2430abd54631a95"), + 196609: common.HexToHash("0x5bb72e83b4dfff0b219384137c3dc512189f4e54ccd5eec01c4764e3bd48069a"), + 262145: common.HexToHash("0xc53143c313d3a9aa38076291dbfa28d7abf0280927ca0b14a1cfa503943f4e56"), + 327681: common.HexToHash("0x2ad203363969a0d85a459738ef556b0d6a81491f6d97240a223ea3d0d4bd109a"), + 393217: common.HexToHash("0xc6e88e3068e38a7655c399ecdcd94a6aaa21a53b60711879e817cd86294be88d"), + 458753: common.HexToHash("0xfa40aaa834a306686782f062da703499246d91748bbdc8b1386e559abccb7744"), + } + if len(roots) != len(expected) { + t.Fatalf("wrong root count: got %d want %d", len(roots), len(expected)) + } + for fullShardID, want := range expected { + if got, ok := roots[fullShardID]; !ok { + t.Fatalf("missing root for full shard %d", fullShardID) + } else if got != want { + t.Fatalf("wrong root for full shard %d: got %s want %s", fullShardID, got, want) + } + } +} + +func quarkChainTestRepoRoot(t *testing.T) string { + t.Helper() + + _, file, _, ok := runtime.Caller(0) + if !ok { + t.Fatal("failed to locate quarkchain genesis test file") + } + return filepath.Clean(filepath.Join(filepath.Dir(file), "..", "..")) +} diff --git a/quarkchain/mainnet/cluster_config_template.json b/quarkchain/mainnet/cluster_config_template.json new file mode 100644 index 000000000000..dc33a7831369 --- /dev/null +++ b/quarkchain/mainnet/cluster_config_template.json @@ -0,0 +1,517 @@ +{ + "P2P_PORT": 38291, + "JSON_RPC_PORT": 38391, + "JSON_RPC_HOST": "127.0.0.1", + "PRIVATE_JSON_RPC_PORT": 38491, + "PRIVATE_JSON_RPC_HOST": "127.0.0.1", + "ENABLE_TRANSACTION_HISTORY": false, + "DB_PATH_ROOT": "./qkc-data/mainnet", + "LOG_LEVEL": "info", + "START_SIMULATED_MINING": false, + "CLEAN": false, + "GENESIS_DIR": null, + "QUARKCHAIN": { + "CHAIN_SIZE": 8, + "BASE_ETH_CHAIN_ID": 100000, + "MAX_NEIGHBORS": 32, + "NETWORK_ID": 1, + "TRANSACTION_QUEUE_SIZE_LIMIT_PER_SHARD": 10000, + "BLOCK_EXTRA_DATA_SIZE_LIMIT": 1024, + "GUARDIAN_PUBLIC_KEY": "6f9ed23452ffb7902345ca8dc53292480274a22cc4625f783e84dd3a6e7082d3e17901c7dc1ba3286fbd1fbd295c17c0722c89e7693220e00587b0d96dd64647", + "ROOT_SIGNER_PRIVATE_KEY": null, + "P2P_PROTOCOL_VERSION": 0, + "P2P_COMMAND_SIZE_LIMIT": 134217728, + "SKIP_ROOT_DIFFICULTY_CHECK": false, + "SKIP_MINOR_DIFFICULTY_CHECK": false, + "GENESIS_TOKEN": "QKC", + "ENABLE_TX_TIMESTAMP": 1561791600, + "ENABLE_EVM_TIMESTAMP": 1569567600, + "ENABLE_QKCHASHX_HEIGHT": 1480000, + "ENABLE_EIP155_SIGNER_TIMESTAMP": 1631577600, + "ENABLE_NON_RESERVED_NATIVE_TOKEN_TIMESTAMP": 1588291200, + "ENABLE_GENERAL_NATIVE_TOKEN_TIMESTAMP": 1588291200, + "ENABLE_POSW_STAKING_DECAY_TIMESTAMP": 1588291200, + "TX_WHITELIST_SENDERS": [ + "b8C082828F51343299c9A4deEb2503AaC3bA074f", + "3391A1796cB98D79A2Fde326F375DF900C959Ed0", + "f9c9B4991A885cB7889074FA91E04AFe7d36b856", + "cd91b2C43D5943ad83e682eF723001F6B9Ec35F2", + "3A324dc1fb617eeA9b1EBc41408d87Fc44FbDd4a", + "bde653eFF19dE913AF8eA6AC4287a8Ec2c1f1e24", + "b505DBb1153449df0863cf72ace1a2a1898B7Bba", + "70FC830E4bCC9a4Dd15dE3faE7a8DF0a29b43321", + "13A83b461d7c612f5C120979cEf16335806d6EAc", + "62d4971dB0133dAC13dF915Be1D11FB9d0909a8B", + "7963EAcDC3FdD481db6018673A41a633636a3b69", + "Bb9bd7d3937712405cf74d044EcD1733eb8763d3", + "db7FD07891697f74A7E5102Cc2cC522c25dc06e9", + "248Dc97675f46Cb2AeCa53006F647ED94eF5B502", + "A3eBA5dBeC29f813171A1C4861B98DaBB12641C1", + "Ccf9296ed0e118BF3815f4aC27a0544ECE7E731F", + "Be4B98ABE5982AC6E453307c81CB47A316b78d89", + "9Cb0b9B9e707054C862272FC5f61B3F2E8d88BE9", + "26E2Fa524B85072eD7fb5D5d9237804dc6c0A140", + "75980402beDF9dca4d745fd8F2B06aeAAE97A8CB", + "bE9473d4Fd4FeD5F20DF43328da93d6dF4104E09", + "60aE18CedCdDd524Fc3448b5AA76634189625699", + "32c53C6c2B57B2026a51C87aDD0695F5AeEd3f2e", + "7DeB90eF2097D8A9e423516e199b9D95EB2b4D97", + "2b7aCC42b0dc2a1562601e2ed9957eAdff7A1347", + "F923ac88fc61837662BACe7E94720C7a071997E6", + "c4fbA3740f95d25B2196C9437fDb005359296D36", + "1f231b489a2d5A1Eb374D363D3Ac851c25Db8626", + "eeb4bBff983536039eda281A0ACCFAe360AeF1fA", + "b9385cA98F102Bd6B180cB76AA0ca8c1615053e7" + ], + "ROOT": { + "MAX_STALE_ROOT_BLOCK_HEIGHT_DIFF": 22500, + "CONSENSUS_TYPE": "POW_ETHASH", + "CONSENSUS_CONFIG": { + "TARGET_BLOCK_TIME": 60, + "REMOTE_MINE": true + }, + "GENESIS": { + "VERSION": 0, + "HEIGHT": 0, + "HASH_PREV_BLOCK": "0000000000000000000000000000000000000000000000000000000000000000", + "HASH_MERKLE_ROOT": "0000000000000000000000000000000000000000000000000000000000000000", + "TIMESTAMP": 1556639999, + "DIFFICULTY": 10000000000000, + "NONCE": 0 + }, + "COINBASE_ADDRESS": "000000000000000000000000000000000000000000000000", + "COINBASE_AMOUNT": 156000000000000000000, + "DIFFICULTY_ADJUSTMENT_CUTOFF_TIME": 40, + "DIFFICULTY_ADJUSTMENT_FACTOR": 1024, + "EPOCH_INTERVAL": 525600, + "POSW_CONFIG": { + "ENABLED": true, + "ENABLE_TIMESTAMP": 1569567600, + "DIFF_DIVIDER": 10000, + "WINDOW_SIZE": 512, + "TOTAL_STAKE_PER_BLOCK": 1000000000000000000000000, + "BOOST_TIMESTAMP": 1649736000, + "BOOST_MULTIPLIER_PER_STEP": 2, + "BOOST_STEPS": 13, + "BOOST_STEP_INTERVAL": 86400 + } + }, + "CHAINS": [ + { + "CHAIN_ID": 0, + "SHARD_SIZE": 1, + "DEFAULT_CHAIN_TOKEN": "QKC", + "CONSENSUS_TYPE": "POW_ETHASH", + "CONSENSUS_CONFIG": { + "TARGET_BLOCK_TIME": 10, + "REMOTE_MINE": true + }, + "GENESIS": { + "ROOT_HEIGHT": 0, + "VERSION": 0, + "HEIGHT": 0, + "HASH_PREV_MINOR_BLOCK": "0000000000000000000000000000000000000000000000000000000000000000", + "HASH_MERKLE_ROOT": "0000000000000000000000000000000000000000000000000000000000000000", + "EXTRA_DATA": "497420776173207468652062657374206f662074696d65732c206974207761732074686520776f727374206f662074696d65732c202e2e2e202d20436861726c6573204469636b656e73", + "TIMESTAMP": 1556639999, + "DIFFICULTY": 5000000000, + "GAS_LIMIT": 12000000, + "NONCE": 0, + "ALLOC": { + "32c53C6c2B57B2026a51C87aDD0695F5AeEd3f2e000075b2": { + "QKC": 600000000000000000000000000 + }, + "7DeB90eF2097D8A9e423516e199b9D95EB2b4D97000075b2": { + "QKC": 100000000000000000000000000 + }, + "c4fbA3740f95d25B2196C9437fDb005359296D36000075b2": { + "QKC": 50000000000000000000000000 + } + } + }, + "COINBASE_ADDRESS": "000000000000000000000000000000000000000000000000", + "COINBASE_AMOUNT": 6500000000000000000, + "DIFFICULTY_ADJUSTMENT_CUTOFF_TIME": 7, + "DIFFICULTY_ADJUSTMENT_FACTOR": 512, + "EXTRA_SHARD_BLOCKS_IN_ROOT_BLOCK": 12, + "POSW_CONFIG": { + "ENABLED": false, + "DIFF_DIVIDER": 20, + "WINDOW_SIZE": 256, + "TOTAL_STAKE_PER_BLOCK": 0 + }, + "EPOCH_INTERVAL": 3153600 + }, + { + "CHAIN_ID": 1, + "SHARD_SIZE": 1, + "DEFAULT_CHAIN_TOKEN": "QKC", + "CONSENSUS_TYPE": "POW_ETHASH", + "CONSENSUS_CONFIG": { + "TARGET_BLOCK_TIME": 10, + "REMOTE_MINE": true + }, + "GENESIS": { + "ROOT_HEIGHT": 0, + "VERSION": 0, + "HEIGHT": 0, + "HASH_PREV_MINOR_BLOCK": "0000000000000000000000000000000000000000000000000000000000000000", + "HASH_MERKLE_ROOT": "0000000000000000000000000000000000000000000000000000000000000000", + "EXTRA_DATA": "497420776173207468652062657374206f662074696d65732c206974207761732074686520776f727374206f662074696d65732c202e2e2e202d20436861726c6573204469636b656e73", + "TIMESTAMP": 1556639999, + "DIFFICULTY": 5000000000, + "GAS_LIMIT": 12000000, + "NONCE": 0, + "ALLOC": { + "32c53C6c2B57B2026a51C87aDD0695F5AeEd3f2e000175b2": { + "QKC": 600000000000000000000000000 + }, + "7DeB90eF2097D8A9e423516e199b9D95EB2b4D97000175b2": { + "QKC": 100000000000000000000000000 + }, + "c4fbA3740f95d25B2196C9437fDb005359296D36000175b2": { + "QKC": 50000000000000000000000000 + } + } + }, + "COINBASE_ADDRESS": "000000000000000000000000000000000000000000000000", + "COINBASE_AMOUNT": 6500000000000000000, + "DIFFICULTY_ADJUSTMENT_CUTOFF_TIME": 7, + "DIFFICULTY_ADJUSTMENT_FACTOR": 512, + "EXTRA_SHARD_BLOCKS_IN_ROOT_BLOCK": 12, + "POSW_CONFIG": { + "ENABLED": true, + "DIFF_DIVIDER": 20, + "WINDOW_SIZE": 256, + "TOTAL_STAKE_PER_BLOCK": 20000000000000000000000 + }, + "EPOCH_INTERVAL": 3153600 + }, + { + "CHAIN_ID": 2, + "SHARD_SIZE": 1, + "DEFAULT_CHAIN_TOKEN": "QKC", + "CONSENSUS_TYPE": "POW_ETHASH", + "CONSENSUS_CONFIG": { + "TARGET_BLOCK_TIME": 10, + "REMOTE_MINE": true + }, + "GENESIS": { + "ROOT_HEIGHT": 0, + "VERSION": 0, + "HEIGHT": 0, + "HASH_PREV_MINOR_BLOCK": "0000000000000000000000000000000000000000000000000000000000000000", + "HASH_MERKLE_ROOT": "0000000000000000000000000000000000000000000000000000000000000000", + "EXTRA_DATA": "497420776173207468652062657374206f662074696d65732c206974207761732074686520776f727374206f662074696d65732c202e2e2e202d20436861726c6573204469636b656e73", + "TIMESTAMP": 1556639999, + "DIFFICULTY": 5000000000, + "GAS_LIMIT": 12000000, + "NONCE": 0, + "ALLOC": { + "32c53C6c2B57B2026a51C87aDD0695F5AeEd3f2e000275b2": { + "QKC": 600000000000000000000000000 + }, + "7DeB90eF2097D8A9e423516e199b9D95EB2b4D97000275b2": { + "QKC": 100000000000000000000000000 + }, + "c4fbA3740f95d25B2196C9437fDb005359296D36000275b2": { + "QKC": 50000000000000000000000000 + } + } + }, + "COINBASE_ADDRESS": "000000000000000000000000000000000000000000000000", + "COINBASE_AMOUNT": 6500000000000000000, + "DIFFICULTY_ADJUSTMENT_CUTOFF_TIME": 7, + "DIFFICULTY_ADJUSTMENT_FACTOR": 512, + "EXTRA_SHARD_BLOCKS_IN_ROOT_BLOCK": 12, + "POSW_CONFIG": { + "ENABLED": true, + "DIFF_DIVIDER": 20, + "WINDOW_SIZE": 256, + "TOTAL_STAKE_PER_BLOCK": 40000000000000000000000 + }, + "EPOCH_INTERVAL": 3153600 + }, + { + "CHAIN_ID": 3, + "SHARD_SIZE": 1, + "DEFAULT_CHAIN_TOKEN": "QKC", + "CONSENSUS_TYPE": "POW_ETHASH", + "CONSENSUS_CONFIG": { + "TARGET_BLOCK_TIME": 10, + "REMOTE_MINE": true + }, + "GENESIS": { + "ROOT_HEIGHT": 0, + "VERSION": 0, + "HEIGHT": 0, + "HASH_PREV_MINOR_BLOCK": "0000000000000000000000000000000000000000000000000000000000000000", + "HASH_MERKLE_ROOT": "0000000000000000000000000000000000000000000000000000000000000000", + "EXTRA_DATA": "497420776173207468652062657374206f662074696d65732c206974207761732074686520776f727374206f662074696d65732c202e2e2e202d20436861726c6573204469636b656e73", + "TIMESTAMP": 1556639999, + "DIFFICULTY": 5000000000, + "GAS_LIMIT": 12000000, + "NONCE": 0, + "ALLOC": { + "32c53C6c2B57B2026a51C87aDD0695F5AeEd3f2e000375b2": { + "QKC": 600000000000000000000000000 + }, + "7DeB90eF2097D8A9e423516e199b9D95EB2b4D97000375b2": { + "QKC": 100000000000000000000000000 + }, + "c4fbA3740f95d25B2196C9437fDb005359296D36000375b2": { + "QKC": 50000000000000000000000000 + } + } + }, + "COINBASE_ADDRESS": "000000000000000000000000000000000000000000000000", + "COINBASE_AMOUNT": 6500000000000000000, + "DIFFICULTY_ADJUSTMENT_CUTOFF_TIME": 7, + "DIFFICULTY_ADJUSTMENT_FACTOR": 512, + "EXTRA_SHARD_BLOCKS_IN_ROOT_BLOCK": 12, + "POSW_CONFIG": { + "ENABLED": true, + "DIFF_DIVIDER": 20, + "WINDOW_SIZE": 256, + "TOTAL_STAKE_PER_BLOCK": 80000000000000000000000 + }, + "EPOCH_INTERVAL": 3153600 + }, + { + "CHAIN_ID": 4, + "SHARD_SIZE": 1, + "DEFAULT_CHAIN_TOKEN": "QKC", + "CONSENSUS_TYPE": "POW_ETHASH", + "CONSENSUS_CONFIG": { + "TARGET_BLOCK_TIME": 10, + "REMOTE_MINE": true + }, + "GENESIS": { + "ROOT_HEIGHT": 0, + "VERSION": 0, + "HEIGHT": 0, + "HASH_PREV_MINOR_BLOCK": "0000000000000000000000000000000000000000000000000000000000000000", + "HASH_MERKLE_ROOT": "0000000000000000000000000000000000000000000000000000000000000000", + "EXTRA_DATA": "497420776173207468652062657374206f662074696d65732c206974207761732074686520776f727374206f662074696d65732c202e2e2e202d20436861726c6573204469636b656e73", + "TIMESTAMP": 1556639999, + "DIFFICULTY": 5000000000, + "GAS_LIMIT": 12000000, + "NONCE": 0, + "ALLOC": { + "32c53C6c2B57B2026a51C87aDD0695F5AeEd3f2e000475b2": { + "QKC": 600000000000000000000000000 + }, + "7DeB90eF2097D8A9e423516e199b9D95EB2b4D97000475b2": { + "QKC": 100000000000000000000000000 + }, + "c4fbA3740f95d25B2196C9437fDb005359296D36000475b2": { + "QKC": 50000000000000000000000000 + } + } + }, + "COINBASE_ADDRESS": "000000000000000000000000000000000000000000000000", + "COINBASE_AMOUNT": 6500000000000000000, + "DIFFICULTY_ADJUSTMENT_CUTOFF_TIME": 7, + "DIFFICULTY_ADJUSTMENT_FACTOR": 512, + "EXTRA_SHARD_BLOCKS_IN_ROOT_BLOCK": 12, + "POSW_CONFIG": { + "ENABLED": true, + "DIFF_DIVIDER": 20, + "WINDOW_SIZE": 256, + "TOTAL_STAKE_PER_BLOCK": 160000000000000000000000 + }, + "EPOCH_INTERVAL": 3153600 + }, + { + "CHAIN_ID": 5, + "SHARD_SIZE": 1, + "DEFAULT_CHAIN_TOKEN": "QKC", + "CONSENSUS_TYPE": "POW_ETHASH", + "CONSENSUS_CONFIG": { + "TARGET_BLOCK_TIME": 10, + "REMOTE_MINE": true + }, + "GENESIS": { + "ROOT_HEIGHT": 0, + "VERSION": 0, + "HEIGHT": 0, + "HASH_PREV_MINOR_BLOCK": "0000000000000000000000000000000000000000000000000000000000000000", + "HASH_MERKLE_ROOT": "0000000000000000000000000000000000000000000000000000000000000000", + "EXTRA_DATA": "497420776173207468652062657374206f662074696d65732c206974207761732074686520776f727374206f662074696d65732c202e2e2e202d20436861726c6573204469636b656e73", + "TIMESTAMP": 1556639999, + "DIFFICULTY": 5000000000, + "GAS_LIMIT": 12000000, + "NONCE": 0, + "ALLOC": { + "32c53C6c2B57B2026a51C87aDD0695F5AeEd3f2e000575b2": { + "QKC": 600000000000000000000000000 + }, + "7DeB90eF2097D8A9e423516e199b9D95EB2b4D97000575b2": { + "QKC": 100000000000000000000000000 + }, + "c4fbA3740f95d25B2196C9437fDb005359296D36000575b2": { + "QKC": 50000000000000000000000000 + } + } + }, + "COINBASE_ADDRESS": "000000000000000000000000000000000000000000000000", + "COINBASE_AMOUNT": 6500000000000000000, + "DIFFICULTY_ADJUSTMENT_CUTOFF_TIME": 7, + "DIFFICULTY_ADJUSTMENT_FACTOR": 512, + "EXTRA_SHARD_BLOCKS_IN_ROOT_BLOCK": 12, + "POSW_CONFIG": { + "ENABLED": true, + "DIFF_DIVIDER": 20, + "WINDOW_SIZE": 256, + "TOTAL_STAKE_PER_BLOCK": 320000000000000000000000 + }, + "EPOCH_INTERVAL": 3153600 + }, + { + "CHAIN_ID": 6, + "SHARD_SIZE": 1, + "DEFAULT_CHAIN_TOKEN": "QKC", + "CONSENSUS_TYPE": "POW_QKCHASH", + "CONSENSUS_CONFIG": { + "TARGET_BLOCK_TIME": 10, + "REMOTE_MINE": true + }, + "GENESIS": { + "ROOT_HEIGHT": 0, + "VERSION": 0, + "HEIGHT": 0, + "HASH_PREV_MINOR_BLOCK": "0000000000000000000000000000000000000000000000000000000000000000", + "HASH_MERKLE_ROOT": "0000000000000000000000000000000000000000000000000000000000000000", + "EXTRA_DATA": "497420776173207468652062657374206f662074696d65732c206974207761732074686520776f727374206f662074696d65732c202e2e2e202d20436861726c6573204469636b656e73", + "TIMESTAMP": 1556639999, + "DIFFICULTY": 120000, + "GAS_LIMIT": 12000000, + "NONCE": 0, + "ALLOC": { + "32c53C6c2B57B2026a51C87aDD0695F5AeEd3f2e000675b2": { + "QKC": 600000000000000000000000000 + }, + "7DeB90eF2097D8A9e423516e199b9D95EB2b4D97000675b2": { + "QKC": 100000000000000000000000000 + }, + "c4fbA3740f95d25B2196C9437fDb005359296D36000675b2": { + "QKC": 50000000000000000000000000 + } + } + }, + "COINBASE_ADDRESS": "000000000000000000000000000000000000000000000000", + "COINBASE_AMOUNT": 6500000000000000000, + "DIFFICULTY_ADJUSTMENT_CUTOFF_TIME": 7, + "DIFFICULTY_ADJUSTMENT_FACTOR": 512, + "EXTRA_SHARD_BLOCKS_IN_ROOT_BLOCK": 12, + "POSW_CONFIG": { + "ENABLED": true, + "DIFF_DIVIDER": 20, + "WINDOW_SIZE": 256, + "TOTAL_STAKE_PER_BLOCK": 40000000000000000000000 + }, + "EPOCH_INTERVAL": 3153600 + }, + { + "CHAIN_ID": 7, + "SHARD_SIZE": 1, + "DEFAULT_CHAIN_TOKEN": "QKC", + "CONSENSUS_TYPE": "POW_QKCHASH", + "CONSENSUS_CONFIG": { + "TARGET_BLOCK_TIME": 10, + "REMOTE_MINE": true + }, + "GENESIS": { + "ROOT_HEIGHT": 0, + "VERSION": 0, + "HEIGHT": 0, + "HASH_PREV_MINOR_BLOCK": "0000000000000000000000000000000000000000000000000000000000000000", + "HASH_MERKLE_ROOT": "0000000000000000000000000000000000000000000000000000000000000000", + "EXTRA_DATA": "497420776173207468652062657374206f662074696d65732c206974207761732074686520776f727374206f662074696d65732c202e2e2e202d20436861726c6573204469636b656e73", + "TIMESTAMP": 1556639999, + "DIFFICULTY": 120000, + "GAS_LIMIT": 12000000, + "NONCE": 0, + "ALLOC": { + "32c53C6c2B57B2026a51C87aDD0695F5AeEd3f2e000775b2": { + "QKC": 600000000000000000000000000 + }, + "7DeB90eF2097D8A9e423516e199b9D95EB2b4D97000775b2": { + "QKC": 100000000000000000000000000 + }, + "c4fbA3740f95d25B2196C9437fDb005359296D36000775b2": { + "QKC": 50000000000000000000000000 + } + } + }, + "COINBASE_ADDRESS": "000000000000000000000000000000000000000000000000", + "COINBASE_AMOUNT": 6500000000000000000, + "DIFFICULTY_ADJUSTMENT_CUTOFF_TIME": 7, + "DIFFICULTY_ADJUSTMENT_FACTOR": 512, + "EXTRA_SHARD_BLOCKS_IN_ROOT_BLOCK": 12, + "POSW_CONFIG": { + "ENABLED": true, + "DIFF_DIVIDER": 20, + "WINDOW_SIZE": 256, + "TOTAL_STAKE_PER_BLOCK": 160000000000000000000000 + }, + "EPOCH_INTERVAL": 3153600 + } + ], + "REWARD_TAX_RATE": 0.5, + "BLOCK_REWARD_DECAY_FACTOR": 0.88, + "ROOT_CHAIN_POSW_CONTRACT_BYTECODE_HASH": "ee90e568da573f251d63256e843add8bd7a27cec1f4c2a06ef20380be68df0a3" + }, + "MASTER": { + "MASTER_TO_SLAVE_CONNECT_RETRY_DELAY": 1.0 + }, + "SLAVE_LIST": [ + { + "HOST": "127.0.0.1", + "PORT": 38000, + "ID": "S0", + "FULL_SHARD_ID_LIST": [ + "0x1", "0x00040001" + ] + }, + { + "HOST": "127.0.0.1", + "PORT": 38001, + "ID": "S1", + "FULL_SHARD_ID_LIST": [ + "0x00010001", "0x00050001" + ] + }, + { + "HOST": "127.0.0.1", + "PORT": 38002, + "ID": "S2", + "FULL_SHARD_ID_LIST": [ + "0x00020001", "0x00060001" + ] + }, + { + "HOST": "127.0.0.1", + "PORT": 38003, + "ID": "S3", + "FULL_SHARD_ID_LIST": [ + "0x00030001", "0x00070001" + ] + } + ], + "P2P": { + "NEW_MODULE": true, + "MAX_PEERS": 30, + "BOOT_NODES": "enode://438d9a2349037e231ae7975f646a32c5b3d2032190a067762b35b8a039568fbb81981e4e2e43f1923a113834a4675919ed27fad68ca48203b4001fee049a9276@35.243.210.122:38291,enode://c093dee29400c0d114c3af600df80a7ad285e8b430f6768749600d55726d4b1562f624526c596b968e4520eaeadaa0d8be98940da065ac710a76d8fac58d5c00@35.246.213.180:38291,enode://48e1af232c290add043118edca45608589ef305f19a2d6d8a6126677ba573c1d5984c15962e8593b32e6ae2f1a89237f9691178e527cba0b07818ad5b01a13dc@52.34.48.64:38291,enode://69a887846c4f6540958c20d654b191b08c39e5624b93d8e94ec8e37da2ae7c0572c0741775e34cd17affa7e68532910e152e16361d22198f04aae2cceb105a03@13.124.15.123:38291,enode://5f81aac576814cac04701d418d9f127903cf75ed26c0433b9b1b35774efe7e2630e377baae156023d2448055e32678a472f6a25f5ead8a690f8cec1e7c9176e6@68.183.247.182:38291,enode://80a7f0960732dae69fa470cf950be636352166b9f016605b79bae4286f06a3fb0361f1293d6ea43df9b87f6e6397498e82c23d913464e2724e5c3adcce796e0d@165.227.240.113:38291", + "PRIV_KEY": "", + "UPNP": true + }, + "MONITORING": { + "NETWORK_NAME": "", + "CLUSTER_ID": "127.0.0.1", + "KAFKA_REST_ADDRESS": "", + "MINER_TOPIC": "qkc_miner", + "PROPAGATION_TOPIC": "block_propagation", + "ERRORS": "error" + } +}