Skip to content

Commit

Permalink
refactor a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
zhiqiangxu committed Mar 1, 2022
1 parent 5b08678 commit 14cca39
Show file tree
Hide file tree
Showing 8 changed files with 615 additions and 355 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,17 @@ So **bihs** doesn't have the requirement to know leader for future block, it's a

```golang
var (
genesis Block
store StateDB
p2p P2P
conf Config
)

# all you need to do is to provide the implementations for Block/StateDB/P2P interfaces

genesis = ...
store = ...
p2p = ...

hs := New(genesis , store , p2p , conf)
hs := New(store , p2p , conf)
hs.Start()

# after this, you can propose blocks when it's your turn
Expand All @@ -37,4 +35,7 @@ hs.Wait(context.Background(), height)
## Demo
[Here](https://github.com/zhiqiangxu/bihs/blob/master/bihs_test.go#L135)
[Here](https://github.com/zhiqiangxu/bihs/blob/master/bihs_test.go#L216)
## `geth` integration
[Here](https://github.com/zhiqiangxu/go-ethereum/blob/web3q_bihs/start_bihs.md)
110 changes: 48 additions & 62 deletions bihs.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package bihs

import (
"bytes"
"context"
"fmt"
"io/ioutil"
Expand All @@ -17,25 +16,20 @@ import (

// HotStuff ...
type HotStuff struct {
view uint64
height uint64
status int32
idx int
proposeCh chan Block
candidateBlk Block
lockQC *QC
hasVoted bool
votes1 map[int][]byte
votes2 map[int][]byte
voted []ID
p2p P2P
store StateDB
conf Config
proposeRelayTimer *time.Timer
nvInterrupt *time.Timer
waiter *wm.Offset
wg sync.WaitGroup
waitBlock func(ID, *Msg)
ConsensusState
status int32
proposeCh chan Block
closeCh chan struct{}
heightChangeCh chan struct{}
p2p P2P
store StateDB
conf Config
relayTimer *time.Timer
nvInterrupt *time.Timer
waiter *wm.Offset
wg sync.WaitGroup
waitBlock func(ID, *Msg)
lastBlockTime uint64

createMsg func(mt MsgType, justify *QC, node *BlockOrHash) *Msg
sign func([]byte) []byte
Expand All @@ -45,30 +39,21 @@ type HotStuff struct {
generateBitmap func(votes map[int][]byte) []byte
}

func New(genesis Block, store StateDB, p2p P2P, conf Config) *HotStuff {
b0 := store.GetBlock(0)
if b0 == nil {
err := store.StoreBlock(genesis, nil, nil)
if err != nil {
panic(fmt.Sprintf("StateDB.StoreNode failed:%v", err))
}
} else {
if !bytes.Equal(b0.Hash(), genesis.Hash()) {
panic("stored genesis doesn't match")
}
}
func New(store StateDB, p2p P2P, conf Config) *HotStuff {

err := conf.validate()
if err != nil {
panic(fmt.Sprintf("Config.Validate failed:%v", err))
}

hs := &HotStuff{
proposeCh: make(chan Block),
p2p: p2p,
store: store,
conf: conf,
waiter: wm.NewOffset(),
proposeCh: make(chan Block),
closeCh: make(chan struct{}),
heightChangeCh: make(chan struct{}, 1),
p2p: p2p,
store: store,
conf: conf,
waiter: wm.NewOffset(),
}

if conf.BlsSigner != nil {
Expand Down Expand Up @@ -98,6 +83,7 @@ func (hs *HotStuff) Start() (err error) {
return
}

hs.store.SubscribeHeightChange(hs)
err = hs.initConsensusState()
if err != nil {
return
Expand All @@ -111,10 +97,12 @@ func (hs *HotStuff) Start() (err error) {
const consensusFile = "/consensus.dat"

func (hs *HotStuff) initConsensusState() (err error) {
fullPath := hs.conf.WalPath + consensusFile

fullPath := hs.conf.DataDir + consensusFile
if _, err = os.Stat(fullPath); os.IsNotExist(err) {
err = nil
hs.applyState(&ConsensusState{Height: hs.store.Height() + 1})
hs.advanceToHeight(hs.store.Height()+1, hs)
hs.conf.Logger.Infof("initConsensusState height=%d view=0", hs.store.Height()+1)
return
}

Expand All @@ -123,47 +111,43 @@ func (hs *HotStuff) initConsensusState() (err error) {
hs.conf.Logger.Error("initConsensusState failed:%v", err)
return
}
source := common.NewZeroCopySource(data)
cs := &ConsensusState{}
err = cs.Deserialize(source)

cs := ConsensusState{}
err = cs.Deserialize(common.NewZeroCopySource(data))
if err != nil {
hs.conf.Logger.Error("initConsensusState failed:%v", err)
hs.conf.Logger.Error("initConsensusState cs.Deserialize failed:%v", err)
return
}

if cs.Height > hs.store.Height()+1 {
if cs.height > hs.store.Height()+1 {
err = fmt.Errorf("inconsistent state found")
return
}
if cs.Height < hs.store.Height()+1 {
cs = &ConsensusState{Height: hs.store.Height() + 1}
if cs.height < hs.store.Height()+1 {
hs.advanceToHeight(hs.store.Height()+1, hs)
hs.conf.Logger.Infof("initConsensusState height=%d view=0", hs.store.Height()+1)
return
}
hs.ConsensusState = cs
hs.conf.Logger.Infof("initConsensusState height=%d view=%d", hs.height, hs.view)

hs.applyState(cs)
return
}

func (hs *HotStuff) applyState(cs *ConsensusState) {
hs.height = cs.Height
hs.view = cs.View
hs.hasVoted = cs.HasVoted
hs.candidateBlk = cs.CandidateBlk
hs.lockQC = cs.LockQC
hs.prepareState(hs.height)
}

func (hs *HotStuff) restoreConsensusState() {
cs := ConsensusState{Height: hs.height, View: hs.view, CandidateBlk: hs.candidateBlk, LockQC: hs.lockQC, HasVoted: hs.hasVoted}
cs := hs.ConsensusState

sink := common.NewZeroCopySink(nil)
cs.Serialize(sink)
csBytes := sink.Bytes()

util.TryUntilSuccess(func() bool {
err := ioutil.WriteFile(hs.conf.WalPath+consensusFile, sink.Bytes(), 0777)
err := ioutil.WriteFile(hs.conf.DataDir+consensusFile, csBytes, 0777)
if err != nil {
hs.conf.Logger.Error("restoreConsensusState failed:%v", err)
return false
}
hs.conf.Logger.Infof("consensus state stored at %s #len %d", hs.conf.DataDir+consensusFile, len(csBytes))
return true
}, time.Second)

Expand All @@ -185,13 +169,14 @@ func (hs *HotStuff) Stop() (err error) {
return
}

close(hs.proposeCh)
close(hs.closeCh)
hs.store.UnSubscribeHeightChange(hs)
hs.wg.Wait()
return
}

func (hs *HotStuff) Propose(ctx context.Context, blk Block) (err error) {
err = blk.Validate()
err = hs.store.Validate(blk)
if err != nil {
return
}
Expand All @@ -207,6 +192,7 @@ func (hs *HotStuff) Propose(ctx context.Context, blk Block) (err error) {
select {
case hs.proposeCh <- blk:
case <-ctx.Done():
case <-hs.closeCh:
}

}()
Expand All @@ -218,10 +204,10 @@ func (hs *HotStuff) Wait(ctx context.Context, height uint64) error {
return hs.waiter.Wait(ctx, int64(height))
}

func (hs *HotStuff) Height() uint64 {
func (hs *HotStuff) ConsensusHeight() uint64 {
return atomic.LoadUint64(&hs.height)
}

func (hs *HotStuff) View() uint64 {
func (hs *HotStuff) ConsensusView() uint64 {
return atomic.LoadUint64(&hs.view)
}
Loading

0 comments on commit 14cca39

Please sign in to comment.