mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 10:27:26 +08:00
1125 lines
28 KiB
Go
1125 lines
28 KiB
Go
package global
|
||
|
||
import (
|
||
"bytes"
|
||
_ "embed"
|
||
"encoding/base64"
|
||
"encoding/binary"
|
||
"encoding/hex"
|
||
"encoding/json"
|
||
"errors"
|
||
"math/big"
|
||
"slices"
|
||
"time"
|
||
|
||
"github.com/iden3/go-iden3-crypto/poseidon"
|
||
pcrypto "github.com/libp2p/go-libp2p/core/crypto"
|
||
"github.com/libp2p/go-libp2p/core/peer"
|
||
"github.com/mr-tron/base58"
|
||
"go.uber.org/zap"
|
||
"source.quilibrium.com/quilibrium/monorepo/config"
|
||
"source.quilibrium.com/quilibrium/monorepo/consensus"
|
||
"source.quilibrium.com/quilibrium/monorepo/consensus/models"
|
||
hgcrdt "source.quilibrium.com/quilibrium/monorepo/hypergraph"
|
||
globalintrinsics "source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/global"
|
||
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/global/compat"
|
||
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/token"
|
||
hgstate "source.quilibrium.com/quilibrium/monorepo/node/execution/state/hypergraph"
|
||
"source.quilibrium.com/quilibrium/monorepo/protobufs"
|
||
typesconsensus "source.quilibrium.com/quilibrium/monorepo/types/consensus"
|
||
"source.quilibrium.com/quilibrium/monorepo/types/crypto"
|
||
"source.quilibrium.com/quilibrium/monorepo/types/execution/intrinsics"
|
||
"source.quilibrium.com/quilibrium/monorepo/types/hypergraph"
|
||
typeskeys "source.quilibrium.com/quilibrium/monorepo/types/keys"
|
||
"source.quilibrium.com/quilibrium/monorepo/types/schema"
|
||
"source.quilibrium.com/quilibrium/monorepo/types/store"
|
||
"source.quilibrium.com/quilibrium/monorepo/types/tries"
|
||
up2p "source.quilibrium.com/quilibrium/monorepo/utils/p2p"
|
||
)
|
||
|
||
// GenesisJson represents the structure of the mainnet genesis JSON
|
||
type GenesisJson struct {
|
||
FrameNumber uint64 `json:"frame_number"`
|
||
Timestamp int64 `json:"timestamp"`
|
||
Difficulty uint32 `json:"difficulty"`
|
||
ParentSelector string `json:"parent_selector"`
|
||
InitialCommitments map[string]string `json:"initial_commitments"`
|
||
Output string `json:"output"`
|
||
BeaconEd448Key string `json:"beacon_ed448_key"`
|
||
BeaconBLS48581Key string `json:"beacon_bls48581_key"`
|
||
ArchivePeers map[string]string `json:"archive_peers"`
|
||
}
|
||
|
||
//go:embed mainnet_genesis.json
|
||
var mainnetGenesisJSON []byte
|
||
|
||
func (e *GlobalConsensusEngine) getMainnetGenesisJSON() *GenesisJson {
|
||
genesisData := &GenesisJson{}
|
||
if err := json.Unmarshal(mainnetGenesisJSON, genesisData); err != nil {
|
||
e.logger.Error("failed to parse embedded genesis data", zap.Error(err))
|
||
return nil
|
||
}
|
||
return genesisData
|
||
}
|
||
|
||
// ExpectedGenesisFrameNumber returns the frame number the node should treat as
|
||
// genesis for the provided configuration (mainnet vs. dev/test).
|
||
func ExpectedGenesisFrameNumber(
|
||
cfg *config.Config,
|
||
logger *zap.Logger,
|
||
) uint64 {
|
||
if cfg != nil && cfg.P2P.Network == 0 {
|
||
genesisData := &GenesisJson{}
|
||
if err := json.Unmarshal(mainnetGenesisJSON, genesisData); err != nil {
|
||
if logger != nil {
|
||
logger.Error(
|
||
"failed to parse embedded genesis data",
|
||
zap.Error(err),
|
||
)
|
||
}
|
||
return 0
|
||
}
|
||
if genesisData.FrameNumber > 0 {
|
||
return genesisData.FrameNumber
|
||
}
|
||
}
|
||
return 0
|
||
}
|
||
|
||
// TODO[2.1.1+]: Refactor out direct hypergraph access
|
||
func (e *GlobalConsensusEngine) initializeGenesis() (
|
||
*protobufs.GlobalFrame,
|
||
*protobufs.QuorumCertificate,
|
||
) {
|
||
e.logger.Info("initializing genesis frame for global consensus")
|
||
|
||
var genesisFrame *protobufs.GlobalFrame
|
||
|
||
// If on mainnet, load from release
|
||
if e.config.P2P.Network == 0 {
|
||
genesisData := e.getMainnetGenesisJSON()
|
||
if genesisData == nil {
|
||
return nil, nil
|
||
}
|
||
|
||
// Decode base64 encoded fields
|
||
parentSelector, err := base64.StdEncoding.DecodeString(
|
||
genesisData.ParentSelector,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to decode parent selector", zap.Error(err))
|
||
return nil, nil
|
||
}
|
||
|
||
output, err := base64.StdEncoding.DecodeString(genesisData.Output)
|
||
if err != nil {
|
||
e.logger.Error("failed to decode output", zap.Error(err))
|
||
return nil, nil
|
||
}
|
||
|
||
// Create genesis header with actual data
|
||
genesisHeader := &protobufs.GlobalFrameHeader{
|
||
FrameNumber: genesisData.FrameNumber,
|
||
ParentSelector: parentSelector,
|
||
Timestamp: genesisData.Timestamp,
|
||
Difficulty: genesisData.Difficulty,
|
||
GlobalCommitments: make([][]byte, 256),
|
||
ProverTreeCommitment: make([]byte, 64),
|
||
Output: output,
|
||
}
|
||
|
||
// Initialize all commitments with empty values first
|
||
for i := range 256 {
|
||
genesisHeader.GlobalCommitments[i] = make([]byte, 64)
|
||
}
|
||
|
||
commitments := make([]*tries.VectorCommitmentTree, 256)
|
||
for i := range 256 {
|
||
commitments[i] = &tries.VectorCommitmentTree{}
|
||
}
|
||
|
||
var proverRoot []byte
|
||
|
||
// Parse and set initial commitments from JSON
|
||
for hexKey, base64Value := range genesisData.InitialCommitments {
|
||
// Decode hex key to get index
|
||
keyBytes, err := hex.DecodeString(hexKey)
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to decode commitment key",
|
||
zap.String("key", hexKey),
|
||
zap.Error(err),
|
||
)
|
||
continue
|
||
}
|
||
|
||
commitmentValue, err := base64.StdEncoding.DecodeString(base64Value)
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to decode commitment value",
|
||
zap.String("value", base64Value),
|
||
zap.Error(err),
|
||
)
|
||
return nil, nil
|
||
}
|
||
|
||
l1 := up2p.GetBloomFilterIndices(keyBytes, 256, 3)
|
||
|
||
txn, err := e.clockStore.NewTransaction(false)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
for i := 0; i < 64; i++ {
|
||
for j := 0; j < 64; j++ {
|
||
err = e.shardsStore.PutAppShard(txn, store.ShardInfo{
|
||
L1: l1,
|
||
L2: keyBytes,
|
||
Path: []uint32{uint32(i), uint32(j)},
|
||
})
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.String("value", base64Value),
|
||
zap.Error(err),
|
||
)
|
||
txn.Abort()
|
||
return nil, nil
|
||
}
|
||
}
|
||
}
|
||
|
||
if err := txn.Commit(); err != nil {
|
||
txn.Abort()
|
||
panic(err)
|
||
}
|
||
|
||
for i := 0; i < 3; i++ {
|
||
commitments[l1[i]].Insert(
|
||
keyBytes,
|
||
commitmentValue,
|
||
nil,
|
||
big.NewInt(int64(len(commitmentValue))),
|
||
)
|
||
commitments[l1[i]].Commit(e.inclusionProver, false)
|
||
}
|
||
}
|
||
|
||
state := hgstate.NewHypergraphState(e.hypergraph)
|
||
|
||
err = e.establishMainnetGenesisProvers(state, genesisData)
|
||
if err != nil {
|
||
e.logger.Error("failed to establish provers", zap.Error(err))
|
||
return nil, nil
|
||
}
|
||
|
||
err = state.Commit()
|
||
if err != nil {
|
||
e.logger.Error("failed to commit", zap.Error(err))
|
||
return nil, nil
|
||
}
|
||
|
||
roots, err := e.hypergraph.Commit(0)
|
||
if err != nil {
|
||
e.logger.Error("could not commit", zap.Error(err))
|
||
return nil, nil
|
||
}
|
||
|
||
proverRoots := roots[tries.ShardKey{
|
||
L1: [3]byte{},
|
||
L2: intrinsics.GLOBAL_INTRINSIC_ADDRESS,
|
||
}]
|
||
|
||
proverRoot = proverRoots[0]
|
||
|
||
genesisHeader.ProverTreeCommitment = proverRoot
|
||
|
||
for i := 0; i < 256; i++ {
|
||
genesisHeader.GlobalCommitments[i] = commitments[i].Commit(
|
||
e.inclusionProver,
|
||
false,
|
||
)
|
||
}
|
||
|
||
// Establish an empty signature payload – this avoids panics on broken
|
||
// header readers
|
||
genesisHeader.PublicKeySignatureBls48581 =
|
||
&protobufs.BLS48581AggregateSignature{
|
||
Signature: make([]byte, 0),
|
||
PublicKey: &protobufs.BLS48581G2PublicKey{
|
||
KeyValue: make([]byte, 0),
|
||
},
|
||
Bitmask: make([]byte, 0),
|
||
}
|
||
|
||
genesisFrame = &protobufs.GlobalFrame{
|
||
Header: genesisHeader,
|
||
Requests: []*protobufs.MessageBundle{},
|
||
}
|
||
} else {
|
||
// For non-mainnet, use stub genesis
|
||
genesisFrame = e.createStubGenesis()
|
||
txn, err := e.clockStore.NewTransaction(false)
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.Error(err),
|
||
)
|
||
return nil, nil
|
||
}
|
||
|
||
l1 := up2p.GetBloomFilterIndices(token.QUIL_TOKEN_ADDRESS, 256, 3)
|
||
err = e.hypergraph.AddVertex(txn, hgcrdt.NewVertex(
|
||
[32]byte(token.QUIL_TOKEN_ADDRESS),
|
||
[32]byte{0b00000000},
|
||
make([]byte, 64),
|
||
big.NewInt(100),
|
||
))
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.Error(err),
|
||
)
|
||
txn.Abort()
|
||
return nil, nil
|
||
}
|
||
err = e.hypergraph.AddVertex(txn, hgcrdt.NewVertex(
|
||
[32]byte(token.QUIL_TOKEN_ADDRESS),
|
||
[32]byte{0b00000001},
|
||
make([]byte, 64),
|
||
big.NewInt(100),
|
||
))
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.Error(err),
|
||
)
|
||
txn.Abort()
|
||
return nil, nil
|
||
}
|
||
err = e.hypergraph.AddVertex(txn, hgcrdt.NewVertex(
|
||
[32]byte(token.QUIL_TOKEN_ADDRESS),
|
||
[32]byte{0b00000010},
|
||
make([]byte, 64),
|
||
big.NewInt(100),
|
||
))
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.Error(err),
|
||
)
|
||
txn.Abort()
|
||
return nil, nil
|
||
}
|
||
err = e.hypergraph.AddVertex(txn, hgcrdt.NewVertex(
|
||
[32]byte(token.QUIL_TOKEN_ADDRESS),
|
||
[32]byte{0b00000011},
|
||
make([]byte, 64),
|
||
big.NewInt(100),
|
||
))
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.Error(err),
|
||
)
|
||
txn.Abort()
|
||
return nil, nil
|
||
}
|
||
err = e.hypergraph.AddVertex(txn, hgcrdt.NewVertex(
|
||
[32]byte(token.QUIL_TOKEN_ADDRESS),
|
||
[32]byte{0b00000100},
|
||
make([]byte, 64),
|
||
big.NewInt(100),
|
||
))
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.Error(err),
|
||
)
|
||
txn.Abort()
|
||
return nil, nil
|
||
}
|
||
err = e.hypergraph.AddVertex(txn, hgcrdt.NewVertex(
|
||
[32]byte(token.QUIL_TOKEN_ADDRESS),
|
||
[32]byte{0b00000101},
|
||
make([]byte, 64),
|
||
big.NewInt(100),
|
||
))
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.Error(err),
|
||
)
|
||
txn.Abort()
|
||
return nil, nil
|
||
}
|
||
err = e.shardsStore.PutAppShard(txn, store.ShardInfo{
|
||
L1: l1,
|
||
L2: token.QUIL_TOKEN_ADDRESS,
|
||
Path: []uint32{0},
|
||
})
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.Error(err),
|
||
)
|
||
txn.Abort()
|
||
return nil, nil
|
||
}
|
||
err = e.shardsStore.PutAppShard(txn, store.ShardInfo{
|
||
L1: l1,
|
||
L2: token.QUIL_TOKEN_ADDRESS,
|
||
Path: []uint32{1},
|
||
})
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.Error(err),
|
||
)
|
||
txn.Abort()
|
||
return nil, nil
|
||
}
|
||
err = e.shardsStore.PutAppShard(txn, store.ShardInfo{
|
||
L1: l1,
|
||
L2: token.QUIL_TOKEN_ADDRESS,
|
||
Path: []uint32{2},
|
||
})
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.Error(err),
|
||
)
|
||
txn.Abort()
|
||
return nil, nil
|
||
}
|
||
err = e.shardsStore.PutAppShard(txn, store.ShardInfo{
|
||
L1: l1,
|
||
L2: token.QUIL_TOKEN_ADDRESS,
|
||
Path: []uint32{3},
|
||
})
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.Error(err),
|
||
)
|
||
txn.Abort()
|
||
return nil, nil
|
||
}
|
||
err = e.shardsStore.PutAppShard(txn, store.ShardInfo{
|
||
L1: l1,
|
||
L2: token.QUIL_TOKEN_ADDRESS,
|
||
Path: []uint32{4},
|
||
})
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.Error(err),
|
||
)
|
||
txn.Abort()
|
||
return nil, nil
|
||
}
|
||
err = e.shardsStore.PutAppShard(txn, store.ShardInfo{
|
||
L1: l1,
|
||
L2: token.QUIL_TOKEN_ADDRESS,
|
||
Path: []uint32{5},
|
||
})
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.Error(err),
|
||
)
|
||
txn.Abort()
|
||
return nil, nil
|
||
}
|
||
if err = txn.Commit(); err != nil {
|
||
e.logger.Error(
|
||
"failed to place app shard",
|
||
zap.Error(err),
|
||
)
|
||
txn.Abort()
|
||
return nil, nil
|
||
}
|
||
}
|
||
|
||
// Compute frame ID and store the full frame
|
||
frameIDBI, _ := poseidon.HashBytes(genesisFrame.Header.Output)
|
||
frameID := frameIDBI.FillBytes(make([]byte, 32))
|
||
e.frameStoreMu.Lock()
|
||
e.frameStore[string(frameID)] = genesisFrame
|
||
e.frameStoreMu.Unlock()
|
||
|
||
// Add to time reel
|
||
txn, err := e.clockStore.NewTransaction(false)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
if err := e.clockStore.PutGlobalClockFrame(genesisFrame, txn); err != nil {
|
||
txn.Abort()
|
||
e.logger.Error("could not add frame", zap.Error(err))
|
||
return nil, nil
|
||
}
|
||
genesisQC := &protobufs.QuorumCertificate{
|
||
Rank: 0,
|
||
Filter: []byte{},
|
||
FrameNumber: genesisFrame.Header.FrameNumber,
|
||
Selector: []byte(genesisFrame.Identity()),
|
||
Timestamp: 0,
|
||
AggregateSignature: &protobufs.BLS48581AggregateSignature{
|
||
PublicKey: &protobufs.BLS48581G2PublicKey{
|
||
KeyValue: make([]byte, 585),
|
||
},
|
||
Signature: make([]byte, 74),
|
||
Bitmask: bytes.Repeat([]byte{0xff}, 32),
|
||
},
|
||
}
|
||
if err := e.clockStore.PutQuorumCertificate(genesisQC, txn); err != nil {
|
||
txn.Abort()
|
||
e.logger.Error("could not add quorum certificate", zap.Error(err))
|
||
return nil, nil
|
||
}
|
||
if err := txn.Commit(); err != nil {
|
||
txn.Abort()
|
||
e.logger.Error("could not add frame", zap.Error(err))
|
||
return nil, nil
|
||
}
|
||
if err = e.consensusStore.PutLivenessState(
|
||
&models.LivenessState{
|
||
CurrentRank: 1,
|
||
LatestQuorumCertificate: genesisQC,
|
||
},
|
||
); err != nil {
|
||
e.logger.Error("could not add liveness state", zap.Error(err))
|
||
return nil, nil
|
||
}
|
||
if err = e.consensusStore.PutConsensusState(
|
||
&models.ConsensusState[*protobufs.ProposalVote]{
|
||
FinalizedRank: 0,
|
||
LatestAcknowledgedRank: 0,
|
||
},
|
||
); err != nil {
|
||
e.logger.Error("could not add consensus state", zap.Error(err))
|
||
return nil, nil
|
||
}
|
||
|
||
e.proverRegistry.Refresh()
|
||
|
||
e.logger.Info("initialized genesis frame for global consensus")
|
||
return genesisFrame, genesisQC
|
||
}
|
||
|
||
// createStubGenesis creates a stub genesis frame for non-mainnet networks
|
||
func (e *GlobalConsensusEngine) createStubGenesis() *protobufs.GlobalFrame {
|
||
e.logger.Warn("CREATING STUB GENESIS FOR TEST NETWORK")
|
||
// Create a stub genesis frame
|
||
genesisHeader := &protobufs.GlobalFrameHeader{
|
||
FrameNumber: 0,
|
||
ParentSelector: make([]byte, 32),
|
||
Timestamp: time.Now().UnixMilli(),
|
||
Difficulty: e.config.Engine.Difficulty,
|
||
GlobalCommitments: make([][]byte, 256),
|
||
ProverTreeCommitment: make([]byte, 64),
|
||
Output: make([]byte, 516),
|
||
}
|
||
|
||
// Initialize all commitments with empty values first
|
||
for i := range 256 {
|
||
genesisHeader.GlobalCommitments[i] = make([]byte, 64)
|
||
}
|
||
|
||
commitments := make([]*tries.VectorCommitmentTree, 256)
|
||
for i := range 256 {
|
||
commitments[i] = &tries.VectorCommitmentTree{}
|
||
}
|
||
|
||
var proverPubKeys [][]byte
|
||
var err error
|
||
if e.config.P2P.Network != 99 && e.config.Engine != nil &&
|
||
e.config.Engine.GenesisSeed != "" {
|
||
proverPubKeyBytes, err := hex.DecodeString(e.config.Engine.GenesisSeed)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
if len(proverPubKeyBytes)%585 != 0 {
|
||
panic("invalid genesis seed for testnet seeding")
|
||
}
|
||
for i := 0; i < len(proverPubKeyBytes)/585; i++ {
|
||
proverPubKeys = append(proverPubKeys, proverPubKeyBytes[i*585:(i+1)*585])
|
||
}
|
||
} else {
|
||
proverKey, err := e.keyManager.GetSigningKey("q-prover-key")
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to obtain prover bls48-581 key value",
|
||
zap.Error(err),
|
||
)
|
||
return nil
|
||
}
|
||
proverPubKeys = [][]byte{proverKey.Public().([]byte)}
|
||
}
|
||
|
||
state := hgstate.NewHypergraphState(e.hypergraph)
|
||
|
||
rdfMultiprover := schema.NewRDFMultiprover(
|
||
&schema.TurtleRDFParser{},
|
||
e.inclusionProver,
|
||
)
|
||
|
||
for _, prover := range proverPubKeys {
|
||
proverAddrBI, err := poseidon.HashBytes(prover)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
addrbi, err := poseidon.HashBytes(slices.Concat(
|
||
token.QUIL_TOKEN_ADDRESS,
|
||
proverAddrBI.FillBytes(make([]byte, 32)),
|
||
))
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
// Create ProverReward entry in QUIL token address with 10000 balance
|
||
rewardTree := &tries.VectorCommitmentTree{}
|
||
|
||
err = rdfMultiprover.Set(
|
||
globalintrinsics.GLOBAL_RDF_SCHEMA,
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
"reward:ProverReward",
|
||
"DelegateAddress",
|
||
addrbi.FillBytes(make([]byte, 32)),
|
||
rewardTree,
|
||
)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
// Set 10000 balance
|
||
balance := make([]byte, 32)
|
||
balanceBI := big.NewInt(10000 * 8000000000)
|
||
balance = balanceBI.FillBytes(balance)
|
||
err = rdfMultiprover.Set(
|
||
globalintrinsics.GLOBAL_RDF_SCHEMA,
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
"reward:ProverReward",
|
||
"Balance",
|
||
balance,
|
||
rewardTree,
|
||
)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
// Create reward vertex in QUIL token address
|
||
rewardVertex := state.NewVertexAddMaterializedState(
|
||
[32]byte(token.QUIL_TOKEN_ADDRESS),
|
||
[32]byte(addrbi.FillBytes(make([]byte, 32))),
|
||
0,
|
||
nil,
|
||
rewardTree,
|
||
)
|
||
|
||
err = state.Set(
|
||
token.QUIL_TOKEN_ADDRESS,
|
||
addrbi.FillBytes(make([]byte, 32)),
|
||
hgstate.VertexAddsDiscriminator,
|
||
0,
|
||
rewardVertex,
|
||
)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
}
|
||
|
||
if err := state.Commit(); err != nil {
|
||
e.logger.Error("failed to commit", zap.Error(err))
|
||
return nil
|
||
}
|
||
|
||
state = hgstate.NewHypergraphState(e.hypergraph)
|
||
|
||
for _, pubkey := range proverPubKeys {
|
||
err = e.addGenesisProver(rdfMultiprover, state, pubkey, 1000, 0)
|
||
if err != nil {
|
||
e.logger.Error("error adding prover", zap.Error(err))
|
||
return nil
|
||
}
|
||
}
|
||
|
||
err = state.Commit()
|
||
if err != nil {
|
||
e.logger.Error("failed to commit", zap.Error(err))
|
||
return nil
|
||
}
|
||
|
||
roots, err := e.hypergraph.Commit(0)
|
||
if err != nil {
|
||
e.logger.Error("could not commit", zap.Error(err))
|
||
return nil
|
||
}
|
||
|
||
// Parse and set initial commitments from JSON
|
||
for shardKey, commits := range roots {
|
||
for i := 0; i < 3; i++ {
|
||
commitments[shardKey.L1[i]].Insert(
|
||
shardKey.L2[:],
|
||
commits[0],
|
||
nil,
|
||
big.NewInt(int64(len(commits[0]))),
|
||
)
|
||
commitments[shardKey.L1[i]].Commit(e.inclusionProver, false)
|
||
}
|
||
}
|
||
|
||
proverRoots := roots[tries.ShardKey{
|
||
L1: [3]byte{},
|
||
L2: intrinsics.GLOBAL_INTRINSIC_ADDRESS,
|
||
}]
|
||
|
||
proverRoot := proverRoots[0]
|
||
|
||
genesisHeader.ProverTreeCommitment = proverRoot
|
||
|
||
for i := 0; i < 256; i++ {
|
||
genesisHeader.GlobalCommitments[i] = commitments[i].Commit(
|
||
e.inclusionProver,
|
||
false,
|
||
)
|
||
}
|
||
|
||
// Establish an empty signature payload – this avoids panics on broken
|
||
// header readers
|
||
genesisHeader.PublicKeySignatureBls48581 =
|
||
&protobufs.BLS48581AggregateSignature{
|
||
Signature: make([]byte, 0),
|
||
PublicKey: &protobufs.BLS48581G2PublicKey{
|
||
KeyValue: make([]byte, 0),
|
||
},
|
||
Bitmask: make([]byte, 0),
|
||
}
|
||
|
||
genesisFrame := &protobufs.GlobalFrame{
|
||
Header: genesisHeader,
|
||
}
|
||
|
||
// Compute frame ID and store the full frame
|
||
frameIDBI, _ := poseidon.HashBytes(genesisHeader.Output)
|
||
frameID := frameIDBI.FillBytes(make([]byte, 32))
|
||
e.frameStoreMu.Lock()
|
||
e.frameStore[string(frameID)] = genesisFrame
|
||
e.frameStoreMu.Unlock()
|
||
|
||
// Add to time reel
|
||
txn, err := e.clockStore.NewTransaction(false)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
if err := e.clockStore.PutGlobalClockFrame(genesisFrame, txn); err != nil {
|
||
txn.Abort()
|
||
e.logger.Error("could not add frame", zap.Error(err))
|
||
return nil
|
||
}
|
||
if err := txn.Commit(); err != nil {
|
||
txn.Abort()
|
||
e.logger.Error("could not add frame", zap.Error(err))
|
||
return nil
|
||
}
|
||
|
||
return genesisFrame
|
||
}
|
||
|
||
// InitializeGenesisState ensures the global genesis frame and QC exist using the
|
||
// provided stores and executors. It is primarily used by components that need
|
||
// the genesis state (like app consensus engines) without instantiating the full
|
||
// global consensus engine.
|
||
func InitializeGenesisState(
|
||
logger *zap.Logger,
|
||
cfg *config.Config,
|
||
clockStore store.ClockStore,
|
||
shardsStore store.ShardsStore,
|
||
hypergraph hypergraph.Hypergraph,
|
||
consensusStore consensus.ConsensusStore[*protobufs.ProposalVote],
|
||
inclusionProver crypto.InclusionProver,
|
||
keyManager typeskeys.KeyManager,
|
||
proverRegistry typesconsensus.ProverRegistry,
|
||
) (*protobufs.GlobalFrame, *protobufs.QuorumCertificate, error) {
|
||
engine := &GlobalConsensusEngine{
|
||
logger: logger,
|
||
config: cfg,
|
||
clockStore: clockStore,
|
||
shardsStore: shardsStore,
|
||
hypergraph: hypergraph,
|
||
consensusStore: consensusStore,
|
||
inclusionProver: inclusionProver,
|
||
keyManager: keyManager,
|
||
proverRegistry: proverRegistry,
|
||
frameStore: make(map[string]*protobufs.GlobalFrame),
|
||
}
|
||
|
||
frame, qc := engine.initializeGenesis()
|
||
if frame == nil || qc == nil {
|
||
return nil, nil, errors.New("failed to initialize global genesis")
|
||
}
|
||
return frame, qc, nil
|
||
}
|
||
|
||
func (e *GlobalConsensusEngine) establishMainnetGenesisProvers(
|
||
state *hgstate.HypergraphState,
|
||
genesisData *GenesisJson,
|
||
) error {
|
||
rdfMultiprover := schema.NewRDFMultiprover(
|
||
&schema.TurtleRDFParser{},
|
||
e.inclusionProver,
|
||
)
|
||
|
||
// old to peer id, get seniority
|
||
beaconEd448Key, err := base64.StdEncoding.DecodeString(
|
||
genesisData.BeaconEd448Key,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to decode beacon ed448 key value",
|
||
zap.String("value", genesisData.BeaconEd448Key),
|
||
zap.Error(err),
|
||
)
|
||
return err
|
||
}
|
||
|
||
pk, err := pcrypto.UnmarshalEd448PublicKey(beaconEd448Key)
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to unmarshal beacon ed448 key value",
|
||
zap.String("value", genesisData.BeaconEd448Key),
|
||
zap.Error(err),
|
||
)
|
||
return err
|
||
}
|
||
|
||
peerId, err := peer.IDFromPublicKey(pk)
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to construct peer id from ed448 key value",
|
||
zap.String("value", genesisData.BeaconEd448Key),
|
||
zap.Error(err),
|
||
)
|
||
return err
|
||
}
|
||
|
||
seniority := compat.GetAggregatedSeniority([]string{peerId.String()})
|
||
e.logger.Debug(
|
||
"establishing seniority for beacon from aggregated records",
|
||
zap.String("seniority", seniority.String()),
|
||
)
|
||
|
||
publicKey, err := base64.StdEncoding.DecodeString(
|
||
genesisData.BeaconBLS48581Key,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error(
|
||
"failed to decode beacon bls48-581 key value",
|
||
zap.String("value", genesisData.BeaconBLS48581Key),
|
||
zap.Error(err),
|
||
)
|
||
return err
|
||
}
|
||
|
||
if err := e.addGenesisProver(
|
||
rdfMultiprover,
|
||
state,
|
||
publicKey,
|
||
seniority.Uint64(),
|
||
genesisData.FrameNumber,
|
||
); err != nil {
|
||
return err
|
||
}
|
||
|
||
for peerid, pubkeyhex := range genesisData.ArchivePeers {
|
||
_, err := base58.Decode(peerid)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
pubkey, err := hex.DecodeString(pubkeyhex)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
if err := e.addGenesisProver(
|
||
rdfMultiprover,
|
||
state,
|
||
pubkey,
|
||
seniority.Uint64(),
|
||
genesisData.FrameNumber,
|
||
); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (e *GlobalConsensusEngine) addGenesisProver(
|
||
rdfMultiprover *schema.RDFMultiprover,
|
||
state *hgstate.HypergraphState,
|
||
pubkey []byte,
|
||
seniority uint64,
|
||
frameNumber uint64,
|
||
) error {
|
||
proverAddressBI, err := poseidon.HashBytes(pubkey)
|
||
if err != nil || proverAddressBI == nil {
|
||
e.logger.Error(
|
||
"failed to calculate address value",
|
||
zap.String("value", hex.EncodeToString(pubkey)),
|
||
zap.Error(err),
|
||
)
|
||
return err
|
||
}
|
||
proverAddress := proverAddressBI.FillBytes(make([]byte, 32))
|
||
|
||
// Full address for the prover entry
|
||
proverFullAddress := [64]byte{}
|
||
copy(proverFullAddress[:32], intrinsics.GLOBAL_INTRINSIC_ADDRESS[:])
|
||
copy(proverFullAddress[32:], proverAddress)
|
||
|
||
// Create new prover entry
|
||
proverTree := &tries.VectorCommitmentTree{}
|
||
// Store the public key
|
||
err = rdfMultiprover.Set(
|
||
globalintrinsics.GLOBAL_RDF_SCHEMA,
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
"prover:Prover",
|
||
"PublicKey",
|
||
pubkey,
|
||
proverTree,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to set rdf value", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
// Store status
|
||
err = rdfMultiprover.Set(
|
||
globalintrinsics.GLOBAL_RDF_SCHEMA,
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
"prover:Prover",
|
||
"Status",
|
||
[]byte{1},
|
||
proverTree,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to set rdf value", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
// Store available storage (initially 0)
|
||
availableStorageBytes := make([]byte, 8)
|
||
binary.BigEndian.PutUint64(availableStorageBytes, 0)
|
||
err = rdfMultiprover.Set(
|
||
globalintrinsics.GLOBAL_RDF_SCHEMA,
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
"prover:Prover",
|
||
"AvailableStorage",
|
||
availableStorageBytes,
|
||
proverTree,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to set rdf value", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
// Store seniority
|
||
seniorityBytes := make([]byte, 8)
|
||
binary.BigEndian.PutUint64(seniorityBytes, seniority)
|
||
err = rdfMultiprover.Set(
|
||
globalintrinsics.GLOBAL_RDF_SCHEMA,
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
"prover:Prover",
|
||
"Seniority",
|
||
seniorityBytes,
|
||
proverTree,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to set rdf value", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
// Create prover vertex
|
||
proverVertex := state.NewVertexAddMaterializedState(
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS,
|
||
[32]byte(proverAddress),
|
||
frameNumber,
|
||
nil,
|
||
proverTree,
|
||
)
|
||
|
||
err = state.Set(
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
proverAddress,
|
||
hgstate.VertexAddsDiscriminator,
|
||
frameNumber,
|
||
proverVertex,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to set state value", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
// Create hyperedge for this prover
|
||
hyperedgeAddress := [32]byte(proverAddress)
|
||
hyperedge := hgcrdt.NewHyperedge(
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS,
|
||
hyperedgeAddress,
|
||
)
|
||
|
||
// Create ProverAllocation entry for global
|
||
|
||
// Calculate allocation address: poseidon.Hash(publicKey || filter)
|
||
allocationAddressBI, err := poseidon.HashBytes(
|
||
slices.Concat([]byte("PROVER_ALLOCATION"), pubkey, nil),
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to calculate allocation address", zap.Error(err))
|
||
return err
|
||
}
|
||
allocationAddress := allocationAddressBI.FillBytes(make([]byte, 32))
|
||
|
||
// Create allocation tree
|
||
allocationTree := &tries.VectorCommitmentTree{}
|
||
|
||
// Store prover reference (using the prover vertex)
|
||
err = rdfMultiprover.Set(
|
||
globalintrinsics.GLOBAL_RDF_SCHEMA,
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
"allocation:ProverAllocation",
|
||
"Prover",
|
||
proverAddress,
|
||
allocationTree,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to set state value", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
// Store allocation status
|
||
err = rdfMultiprover.Set(
|
||
globalintrinsics.GLOBAL_RDF_SCHEMA,
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
"allocation:ProverAllocation",
|
||
"Status",
|
||
[]byte{1},
|
||
allocationTree,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to set state value", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
// Store confirmation filter
|
||
err = rdfMultiprover.Set(
|
||
globalintrinsics.GLOBAL_RDF_SCHEMA,
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
"allocation:ProverAllocation",
|
||
"ConfirmationFilter",
|
||
nil,
|
||
allocationTree,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to set state value", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
// Store join frame number
|
||
frameNumberBytes := make([]byte, 8)
|
||
binary.BigEndian.PutUint64(frameNumberBytes, 0)
|
||
err = rdfMultiprover.Set(
|
||
globalintrinsics.GLOBAL_RDF_SCHEMA,
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
"allocation:ProverAllocation",
|
||
"JoinFrameNumber",
|
||
frameNumberBytes,
|
||
allocationTree,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to set state value", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
// Store join confirm frame number
|
||
err = rdfMultiprover.Set(
|
||
globalintrinsics.GLOBAL_RDF_SCHEMA,
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
"allocation:ProverAllocation",
|
||
"JoinConfirmFrameNumber",
|
||
frameNumberBytes,
|
||
allocationTree,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to set state value", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
// Store last active frame number
|
||
lastActiveFrameNumberBytes := make([]byte, 8)
|
||
binary.BigEndian.PutUint64(lastActiveFrameNumberBytes, frameNumber)
|
||
err = rdfMultiprover.Set(
|
||
globalintrinsics.GLOBAL_RDF_SCHEMA,
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
"allocation:ProverAllocation",
|
||
"LastActiveFrameNumber",
|
||
lastActiveFrameNumberBytes,
|
||
allocationTree,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to set state value", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
// Create allocation vertex
|
||
allocationVertex := state.NewVertexAddMaterializedState(
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS,
|
||
[32]byte(allocationAddress),
|
||
frameNumber,
|
||
nil,
|
||
allocationTree,
|
||
)
|
||
|
||
err = state.Set(
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
allocationAddress,
|
||
hgstate.VertexAddsDiscriminator,
|
||
frameNumber,
|
||
allocationVertex,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to set state value", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
// Add allocation vertex to hyperedge
|
||
allocationAtom := hgcrdt.NewVertex(
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS,
|
||
[32]byte(allocationAddress),
|
||
allocationTree.Commit(e.inclusionProver, false),
|
||
allocationTree.GetSize(),
|
||
)
|
||
hyperedge.AddExtrinsic(allocationAtom)
|
||
|
||
// Update hyperedge
|
||
hyperedgeState := state.NewHyperedgeAddMaterializedState(
|
||
frameNumber,
|
||
nil,
|
||
hyperedge,
|
||
)
|
||
err = state.Set(
|
||
intrinsics.GLOBAL_INTRINSIC_ADDRESS[:],
|
||
hyperedgeAddress[:],
|
||
hgstate.HyperedgeAddsDiscriminator,
|
||
frameNumber,
|
||
hyperedgeState,
|
||
)
|
||
if err != nil {
|
||
e.logger.Error("failed to set state value", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|