aboutsummaryrefslogtreecommitdiffstats
path: root/core/reliable-broadcast.go
diff options
context:
space:
mode:
Diffstat (limited to 'core/reliable-broadcast.go')
-rw-r--r--core/reliable-broadcast.go83
1 files changed, 80 insertions, 3 deletions
diff --git a/core/reliable-broadcast.go b/core/reliable-broadcast.go
index dd57241..7db8212 100644
--- a/core/reliable-broadcast.go
+++ b/core/reliable-broadcast.go
@@ -19,6 +19,7 @@ package core
import (
"fmt"
+ "time"
"github.com/dexon-foundation/dexon-consensus-core/common"
"github.com/dexon-foundation/dexon-consensus-core/core/types"
@@ -66,6 +67,7 @@ var (
ErrNotAckParent = fmt.Errorf("not ack parent")
ErrDoubleAck = fmt.Errorf("double ack")
ErrInvalidBlockHeight = fmt.Errorf("invalid block height")
+ ErrAlreadyInLattice = fmt.Errorf("block already in lattice")
)
// newReliableBroadcast creates a new reliableBroadcast struct.
@@ -89,6 +91,7 @@ func (rb *reliableBroadcast) sanityCheck(b *types.Block) error {
if b.Hash != bInLattice.Hash {
return ErrForkBlock
}
+ return ErrAlreadyInLattice
}
// Check non-genesis blocks if it acks its parent.
@@ -136,9 +139,9 @@ func (rb *reliableBroadcast) areAllAcksInLattice(b *types.Block) bool {
// processBlock processes block, it does sanity check, inserts block into
// lattice, handles strong acking and deletes blocks which will not be used.
-func (rb *reliableBroadcast) processBlock(block *types.Block) {
+func (rb *reliableBroadcast) processBlock(block *types.Block) (err error) {
// If a block does not pass sanity check, discard this block.
- if err := rb.sanityCheck(block); err != nil {
+ if err = rb.sanityCheck(block); err != nil {
return
}
rb.blocks[block.Hash] = block
@@ -166,10 +169,11 @@ func (rb *reliableBroadcast) processBlock(block *types.Block) {
// B C Block B and C both ack A and are valid. B, C received first
// \ / (added in receivedBlocks), and A comes, if sanity check is
// A not being executed here, B and C will both be added in lattice
- if err := rb.sanityCheck(b); err != nil {
+ if err = rb.sanityCheck(b); err != nil {
delete(rb.blocks, b.Hash)
delete(rb.receivedBlocks, b.Hash)
continue
+ // TODO(mission): how to return for multiple errors?
}
rb.lattice[b.ProposerID].blocks[b.Height] = b
delete(rb.receivedBlocks, b.Hash)
@@ -246,6 +250,7 @@ func (rb *reliableBroadcast) processBlock(block *types.Block) {
min--
}
}
+ return
}
// extractBlocks returns all blocks that can be inserted into total ordering's
@@ -290,6 +295,78 @@ func (rb *reliableBroadcast) extractBlocks() []*types.Block {
return ret
}
+// prepareBlock helps to setup fields of block based on its ProposerID,
+// including:
+// - Set 'Acks' and 'Timestamps' for the highest block of each validator not
+// acked by this proposer before.
+// - Set 'ParentHash' and 'Height' from parent block, if we can't find a
+// parent, these fields would be setup like a genesis block.
+func (rb *reliableBroadcast) prepareBlock(block *types.Block) {
+ // Reset fields to make sure we got these information from parent block.
+ block.Height = 0
+ // TODO(mission): make all genesis block would contain zero ParentHash.
+ block.ParentHash = common.Hash{}
+ // The helper function to accumulate timestamps.
+ accumulateTimestamps := func(
+ times map[types.ValidatorID]time.Time, b *types.Block) {
+
+ // Update timestamps with the block's proposer time.
+ // TODO (mission): make epslon configurable.
+ times[b.ProposerID] = b.Timestamps[b.ProposerID].Add(
+ 1 * time.Millisecond)
+
+ // Update timestamps from the block if it's later than
+ // current cached ones.
+ for vID, t := range b.Timestamps {
+ cachedTime, exists := times[vID]
+ if !exists {
+ // This means the block contains timestamps from
+ // removed validators.
+ continue
+ }
+ if cachedTime.After(t) {
+ continue
+ }
+ times[vID] = t
+ }
+ return
+ }
+ // Initial timestamps with current validator set.
+ times := make(map[types.ValidatorID]time.Time)
+ for vID := range rb.lattice {
+ times[vID] = time.Time{}
+ }
+ acks := make(map[common.Hash]struct{})
+ for vID := range rb.lattice {
+ // find height of the latest block for that validator.
+ var (
+ curBlock *types.Block
+ nextHeight = rb.lattice[block.ProposerID].nextAck[vID]
+ )
+
+ for {
+ tmpBlock, exists := rb.lattice[vID].blocks[nextHeight]
+ if !exists {
+ break
+ }
+ curBlock = tmpBlock
+ nextHeight++
+ }
+ if curBlock == nil {
+ continue
+ }
+ acks[curBlock.Hash] = struct{}{}
+ accumulateTimestamps(times, curBlock)
+ if vID == block.ProposerID {
+ block.ParentHash = curBlock.Hash
+ block.Height = curBlock.Height + 1
+ }
+ }
+ block.Timestamps = times
+ block.Acks = acks
+ return
+}
+
// addValidator adds validator in the validator set.
func (rb *reliableBroadcast) addValidator(h types.ValidatorID) {
rb.lattice[h] = &ackingValidatorStatus{