Skip to content

Commit

Permalink
GenesisInit
Browse files Browse the repository at this point in the history
  • Loading branch information
charithabandi committed Dec 6, 2024
1 parent a697f02 commit 3da372a
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 19 deletions.
6 changes: 6 additions & 0 deletions app/node/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@ func buildConsensusEngine(_ context.Context, d *coreDependencies, db *pg.DB, acc
failBuild(err, "failed to parse leader public key")
}

genHash, err := d.genesisCfg.ComputeGenesisHash()
if err != nil {
failBuild(err, "failed to compute genesis hash")
}

ceCfg := &consensus.Config{
PrivateKey: d.privKey,
Leader: leaderPubKey,
Expand All @@ -245,6 +250,7 @@ func buildConsensusEngine(_ context.Context, d *coreDependencies, db *pg.DB, acc
MaxVotesPerTx: d.genesisCfg.MaxVotesPerTx,
},
},
GenesisHash: genHash,
DB: db,
Accounts: accounts,
BlockStore: bs,
Expand Down
24 changes: 24 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package config

import (
"encoding/binary"
"encoding/json"
"os"
"sort"
"time"

"github.com/kwilteam/kwil-db/core/log"
Expand Down Expand Up @@ -80,6 +82,28 @@ func LoadGenesisConfig(filename string) (*GenesisConfig, error) {
return &nc, nil
}

func (gc *GenesisConfig) ComputeGenesisHash() (types.Hash, error) {
hasher := ktypes.NewHasher()

hasher.Write([]byte(gc.ChainID))
hasher.Write(gc.Leader)

// sort the validators by public key
sort.Sort(ktypes.ByPubKey(gc.Validators))
for _, v := range gc.Validators {
hasher.Write(v.PubKey)
binary.Write(hasher, binary.BigEndian, v.Power)
}

binary.Write(hasher, binary.BigEndian, gc.MaxBlockSize)
binary.Write(hasher, binary.BigEndian, gc.JoinExpiry)
binary.Write(hasher, binary.BigEndian, gc.VoteExpiry)
binary.Write(hasher, binary.BigEndian, gc.DisabledGasCosts)
binary.Write(hasher, binary.BigEndian, gc.MaxVotesPerTx)

return hasher.Sum(nil), nil
}

// const (
// defaultUserRPCPort = 8484
// defaultAdminRPCPort = 8584
Expand Down
7 changes: 7 additions & 0 deletions core/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ type Validator struct {
Power int64 `json:"power"`
}

// ByPubKey implements sort.Interface based on the PubKey field.
type ByPubKey []Validator

func (a ByPubKey) Len() int { return len(a) }
func (a ByPubKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByPubKey) Less(i, j int) bool { return a[i].PubKey.String() < a[j].PubKey.String() }

// ValidatorRemoveProposal is a proposal from an existing validator (remover) to
// remove a validator (the target) from the validator set.
type ValidatorRemoveProposal struct {
Expand Down
8 changes: 4 additions & 4 deletions node/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,10 +415,10 @@ func (n *Node) startDiscoveryRequestGossip(ctx context.Context, ps *pubsub.PubSu
return
}

// We're only interested if we are the validator.
if n.ce.Role() != types.RoleValidator {
continue // discard, we are just relaying to leader
}
// // We're only interested if we are the validator.
// if n.ce.Role() != types.RoleValidator {
// continue // discard, we are just relaying to leader
// }

if peer.ID(discMsg.From) == me {
continue
Expand Down
6 changes: 4 additions & 2 deletions node/consensus/blocksync.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import (
// caught up with the network, before it can start proposing blocks.
// Else it can propose a block at a height that is already finalized
// leading to a fork.

func (ce *ConsensusEngine) doBlockSync(ctx context.Context) error {
if ce.role.Load() == types.RoleLeader {
if len(ce.validators.GetValidators()) == 1 {
// TODO: which validator set we should use here? whatever we have in the state?
// what if the current validators are not the same as the ones in the state?
// and they don't respond to the status request?
if len(ce.validatorSet) == 1 {
return nil // we are the network
}
// figure out the best height to sync with the network
Expand Down
80 changes: 70 additions & 10 deletions node/consensus/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ type ConsensusEngine struct {

proposeTimeout time.Duration

networkHeight atomic.Int64
validatorSet map[string]ktypes.Validator
networkHeight atomic.Int64
validatorSet map[string]ktypes.Validator
genesisAppHash types.Hash

// stores state machine state for the consensus engine
state state
Expand Down Expand Up @@ -196,6 +197,7 @@ type Config struct {
// Leader is the public key of the leader.
Leader crypto.PublicKey

GenesisHash types.Hash
GenesisParams *GenesisParams // *config.GenesisConfig

DB *pg.DB
Expand Down Expand Up @@ -298,13 +300,14 @@ func New(cfg *Config) *ConsensusEngine {
resetChan: make(chan int64, 1),
bestHeightCh: make(chan *discoveryMsg, 1),
// interfaces
mempool: cfg.Mempool,
blockStore: cfg.BlockStore,
txapp: cfg.TxApp,
accounts: cfg.Accounts,
validators: cfg.ValidatorStore,
snapshotter: cfg.Snapshots,
log: logger,
mempool: cfg.Mempool,
blockStore: cfg.BlockStore,
txapp: cfg.TxApp,
accounts: cfg.Accounts,
validators: cfg.ValidatorStore,
snapshotter: cfg.Snapshots,
log: logger,
genesisAppHash: cfg.GenesisHash,
}

ce.role.Store(role)
Expand All @@ -313,6 +316,8 @@ func New(cfg *Config) *ConsensusEngine {
return ce
}

var initialHeight int64 = 0 // TODO: get it from genesis?

func (ce *ConsensusEngine) Start(ctx context.Context, proposerBroadcaster ProposalBroadcaster,
blkAnnouncer BlkAnnouncer, ackBroadcaster AckBroadcaster, blkRequester BlkRequester, stateResetter ResetStateBroadcaster,
discoveryReqBroadcaster DiscoveryReqBroadcaster) error {
Expand All @@ -337,6 +342,52 @@ func (ce *ConsensusEngine) Start(ctx context.Context, proposerBroadcaster Propos
return ce.runConsensusEventLoop(ctx)
}

// GenesisInit initializes the node with the genesis state. This included initializing the
// votestore with the genesis validators, accounts with the genesis allocations and the
// chain meta store with the genesis network parameters.
// This is called only once when the node is bootstrapping for the first time.
func (ce *ConsensusEngine) GenesisInit(ctx context.Context) error {
genesisTx, err := ce.db.BeginPreparedTx(ctx)
if err != nil {
return err
}
defer genesisTx.Rollback(ctx)

// TODO: genesis allocs

// genesis validators
genVals := make([]*ktypes.Validator, 0, len(ce.validatorSet))
for _, v := range ce.validatorSet {
genVals = append(genVals, &ktypes.Validator{
PubKey: v.PubKey,
Power: v.Power,
})
}

startParams := *ce.chainCtx.NetworkParameters

if err := ce.txapp.GenesisInit(ctx, genesisTx, genVals, nil, initialHeight, ce.chainCtx); err != nil {
return err
}

if err := meta.SetChainState(ctx, genesisTx, initialHeight, ce.genesisAppHash[:], false); err != nil {
return fmt.Errorf("error storing the genesis state: %w", err)
}

if err := meta.StoreDiff(ctx, genesisTx, &startParams, ce.chainCtx.NetworkParameters); err != nil {
return fmt.Errorf("error storing the genesis consensus params: %w", err)
}

// TODO: Genesis hash and what are the mechanics for producing the first block (genesis block)?
ce.txapp.Commit()

ce.state.lc.appHash = ce.genesisAppHash
ce.state.lc.height = initialHeight

ce.log.Info("Initialized chain", "height", initialHeight, "appHash", ce.state.lc.appHash.String())
return genesisTx.Commit(ctx)
}

// runEventLoop starts the event loop for the consensus engine.
// Below are the event triggers that nodes can receive depending on their role:
// Leader:
Expand Down Expand Up @@ -497,13 +548,22 @@ func (ce *ConsensusEngine) catchup(ctx context.Context) error {
ce.setLastCommitInfo(appHeight, blkHash, types.Hash(appHash))
}

if appHeight == -1 {
// This is the first time the node is bootstrapping
// initialize the db with the genesis state
if err := ce.GenesisInit(ctx); err != nil {
return fmt.Errorf("error initializing the genesis state: %w", err)
}
}

// Replay the blocks from the blockstore if the app hasn't played all the blocks yet.
if appHeight < storeHeight {
// Replay the blocks from the blockstore
if err := ce.replayFromBlockStore(appHeight+1, storeHeight); err != nil {
return err
}
}

// Sync with the network using the blocksync
if err := ce.doBlockSync(ctx); err != nil {
return err
}
Expand Down
9 changes: 6 additions & 3 deletions node/consensus/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ func TestValidatorStateMachine(t *testing.T) {
return false
}
return true
}, 2*time.Second, 100*time.Millisecond)
}, 6*time.Second, 100*time.Millisecond)
}
})
}
Expand Down Expand Up @@ -658,7 +658,7 @@ func TestCELeaderTwoNodesMajorityAcks(t *testing.T) {
height := n1.lastCommitHeight()
fmt.Printf("Height: %d\n", height)
return height == 1
}, 2*time.Second, 100*time.Millisecond)
}, 6*time.Second, 100*time.Millisecond)
}

func TestCELeaderTwoNodesMajorityNacks(t *testing.T) {
Expand Down Expand Up @@ -689,7 +689,7 @@ func TestCELeaderTwoNodesMajorityNacks(t *testing.T) {
require.Eventually(t, func() bool {
blockRes := n1.blockResult()
return blockRes != nil && !blockRes.appHash.IsZero()
}, 2*time.Second, 100*time.Millisecond)
}, 6*time.Second, 100*time.Millisecond)

_, _, b := n1.info()
assert.NotNil(t, b)
Expand Down Expand Up @@ -777,6 +777,9 @@ func (d *dummyTxApp) Price(ctx context.Context, dbTx sql.DB, tx *ktypes.Transact
func (d *dummyTxApp) Commit() error {
return nil
}
func (d *dummyTxApp) GenesisInit(ctx context.Context, db sql.DB, validators []*ktypes.Validator, genesisAccounts []*ktypes.Account, initialHeight int64, chain *common.ChainContext) error {
return nil
}

type validatorStore struct {
valSet []*ktypes.Validator
Expand Down
1 change: 1 addition & 0 deletions node/consensus/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type TxApp interface {
Execute(ctx *common.TxContext, db sql.DB, tx *ktypes.Transaction) *txapp.TxResponse
Finalize(ctx context.Context, db sql.DB, block *common.BlockContext) (finalValidators []*ktypes.Validator, err error)
Commit() error
GenesisInit(ctx context.Context, db sql.DB, validators []*ktypes.Validator, genesisAccounts []*ktypes.Account, initialHeight int64, chain *common.ChainContext) error

Price(ctx context.Context, dbTx sql.DB, tx *ktypes.Transaction, chainContext *common.ChainContext) (*big.Int, error)
}
Expand Down
4 changes: 4 additions & 0 deletions node/node_live_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,10 @@ func (d *dummyTxApp) Commit() error {
return nil
}

func (d *dummyTxApp) GenesisInit(ctx context.Context, db sql.DB, validators []*ktypes.Validator, genesisAccounts []*ktypes.Account, initialHeight int64, chain *common.ChainContext) error {
return nil
}

type validatorStore struct {
valSet []*ktypes.Validator
}
Expand Down

0 comments on commit 3da372a

Please sign in to comment.