ceremonyclient/node/consensus/global/genesis.go

861 lines
21 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package global
import (
"bytes"
_ "embed"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"math/big"
"net/http"
"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"
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"
"source.quilibrium.com/quilibrium/monorepo/types/execution/intrinsics"
"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"`
}
// TODO[2.1.1+]: Refactor out direct hypergraph access
func (e *GlobalConsensusEngine) initializeGenesis() *protobufs.GlobalFrame {
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 {
var lastErr error
var genesisData GenesisJson = GenesisJson{}
for attempt := 1; attempt <= 5; attempt++ {
if err := func() error {
resp, err := http.Get("https://releases.quilibrium.com/genesis.json")
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("http status %d", resp.StatusCode)
}
buf := bytes.NewBuffer(nil)
_, err = io.Copy(buf, resp.Body)
if err != nil {
return err
}
err = json.Unmarshal(buf.Bytes(), &genesisData)
if err != nil {
return err
}
return nil
}(); err != nil {
lastErr = err
// simple backoff: 200ms * attempt
time.Sleep(time.Duration(200*attempt) * time.Millisecond)
continue
}
lastErr = nil
break
}
if lastErr != nil {
e.logger.Error("failed to download genesis", zap.Error(lastErr))
return 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
}
output, err := base64.StdEncoding.DecodeString(genesisData.Output)
if err != nil {
e.logger.Error("failed to decode output", zap.Error(err))
return 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{}
}
proverRoot := make([]byte, 64)
// 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
}
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
}
}
}
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
}
err = state.Commit()
if err != nil {
e.logger.Error("failed to commit", zap.Error(err))
return nil
}
roots := e.hypergraph.Commit()
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
}
l1 := up2p.GetBloomFilterIndices(token.QUIL_TOKEN_ADDRESS, 256, 3)
err = e.shardsStore.PutAppShard(txn, store.ShardInfo{
L1: l1,
L2: token.QUIL_TOKEN_ADDRESS,
Path: []uint32{},
})
if err != nil {
e.logger.Error(
"failed to place app shard",
zap.Error(err),
)
txn.Abort()
return nil
}
if err = txn.Commit(); err != nil {
e.logger.Error(
"failed to place app shard",
zap.Error(err),
)
txn.Abort()
return 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
if err := e.globalTimeReel.Insert(e.ctx, genesisFrame); err != nil {
e.logger.Error("failed to add genesis frame to time reel", zap.Error(err))
// Clean up on error
e.frameStoreMu.Lock()
delete(e.frameStore, string(frameID))
e.frameStoreMu.Unlock()
}
e.proverRegistry.Refresh()
e.logger.Info("initialized genesis frame for global consensus")
return genesisFrame
}
// 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 {
addrbi, err := poseidon.HashBytes(prover)
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,
token.QUIL_TOKEN_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,
token.QUIL_TOKEN_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
}
roots := e.hypergraph.Commit()
// 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)
}
}
state = hgstate.NewHypergraphState(e.hypergraph)
for _, pubkey := range proverPubKeys {
err = e.addGenesisProver(rdfMultiprover, state, pubkey, 0, 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 = e.hypergraph.Commit()
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
if err := e.globalTimeReel.Insert(e.ctx, genesisFrame); err != nil {
e.logger.Error("failed to add genesis frame to time reel", zap.Error(err))
// Clean up on error
e.frameStoreMu.Lock()
delete(e.frameStore, string(frameID))
e.frameStoreMu.Unlock()
}
return genesisFrame
}
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
}