From aaeaebaf5dec3a9a17d0a626cb19e76834f9e4cb Mon Sep 17 00:00:00 2001 From: Haoping Ku Date: Mon, 30 Jul 2018 11:40:16 +0800 Subject: Add acking module (#13) * Refactor and add acking module Extract acking module for unit testing. This commit splits functions into small pieces for better understanding and easy unit testing. --- core/acking.go | 308 +++++++++++++++++++++++++++++++++ core/acking_test.go | 426 ++++++++++++++++++++++++++++++++++++++++++++++ core/blocklattice.go | 24 +-- core/blocklattice_test.go | 12 +- core/types/block.go | 13 +- 5 files changed, 759 insertions(+), 24 deletions(-) create mode 100644 core/acking.go create mode 100644 core/acking_test.go (limited to 'core') diff --git a/core/acking.go b/core/acking.go new file mode 100644 index 0000000..b244831 --- /dev/null +++ b/core/acking.go @@ -0,0 +1,308 @@ +// Copyright 2018 The dexon-consensus-core Authors +// This file is part of the dexon-consensus-core library. +// +// The dexon-consensus-core library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus-core library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus-core library. If not, see +// . + +package core + +import ( + "fmt" + + "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/types" +) + +// Acking is for acking module. +type Acking struct { + // lattice stores blocks by its validator ID and height. + lattice map[types.ValidatorID]*ackingValidatorStatus + + // blocks stores the hash to block map. + blocks map[common.Hash]*types.Block + + // receivedBlocks stores blocks which is received but its acks are not all + // in lattice. + receivedBlocks map[common.Hash]*types.Block + + // ackedBlocks stores blocks in status types.BlockStatusAcked, which are + // strongly acked but not yet being output to total ordering module. + ackedBlocks map[common.Hash]*types.Block +} + +type ackingValidatorStatus struct { + // blocks stores blocks proposed by specified validator in map which key is + // the height of the block. + blocks map[uint64]*types.Block + + // nextAck stores the height of next height that should be acked, i.e. last + // acked height + 1. Initialized to 0, when genesis blocks are still not + // being acked. For example, a.lattice[vid1].NextAck[vid2] - 1 is the last + // acked height by vid1 acking vid2. + nextAck map[types.ValidatorID]uint64 + + // nextOutput is the next output height of block, default to 0. + nextOutput uint64 + + // restricted is the flag of a validator is in restricted mode or not. + restricted bool +} + +// Errors for sanity check error. +var ( + ErrInvalidProposerID = fmt.Errorf("invalid_proposer_id") + ErrForkBlock = fmt.Errorf("fork_block") + ErrNotAckParent = fmt.Errorf("not_ack_parent") + ErrDoubleAck = fmt.Errorf("double_ack") + ErrInvalidBlockHeight = fmt.Errorf("invalid_block_height") +) + +// NewAcking creates a new Acking struct. +func NewAcking() *Acking { + return &Acking{ + lattice: make(map[types.ValidatorID]*ackingValidatorStatus), + blocks: make(map[common.Hash]*types.Block), + receivedBlocks: make(map[common.Hash]*types.Block), + ackedBlocks: make(map[common.Hash]*types.Block), + } +} + +func (a *Acking) sanityCheck(b *types.Block) error { + // Check if its proposer is in validator set. + if _, exist := a.lattice[b.ProposerID]; !exist { + return ErrInvalidProposerID + } + + // Check if it forks. + if bInLattice, exist := a.lattice[b.ProposerID].blocks[b.Height]; exist { + if b.Hash != bInLattice.Hash { + return ErrForkBlock + } + } + + // Check non-genesis blocks if it acks its parent. + if b.Height > 0 { + if _, exist := b.Acks[b.ParentHash]; !exist { + return ErrNotAckParent + } + bParent := a.blocks[b.ParentHash] + if bParent.Height != b.Height-1 { + return ErrInvalidBlockHeight + } + } + + // Check if it acks older blocks. + for hash := range b.Acks { + if bAck, exist := a.blocks[hash]; exist { + if bAck.Height < a.lattice[b.ProposerID].nextAck[bAck.ProposerID] { + return ErrDoubleAck + } + } + } + + // TODO(haoping): application layer check of block's content + + return nil +} + +// areAllAcksReceived checks if all ack blocks of a block are all in lattice. +func (a *Acking) areAllAcksInLattice(b *types.Block) bool { + for h := range b.Acks { + bAck, exist := a.blocks[h] + if !exist { + return false + } + if bAckInLattice, exist := a.lattice[bAck.ProposerID].blocks[bAck.Height]; !exist { + if bAckInLattice.Hash != bAck.Hash { + panic("areAllAcksInLattice: Acking.lattice has corrupted") + } + return false + } + } + return true +} + +// ProcessBlock processes block, it does sanity check, inserts block into +// lattice, handles strong acking and deletes blocks which will not be used. +func (a *Acking) ProcessBlock(block *types.Block) { + // If a block does not pass sanity check, discard this block. + if err := a.sanityCheck(block); err != nil { + return + } + a.blocks[block.Hash] = block + block.AckedValidators = make(map[types.ValidatorID]struct{}) + a.receivedBlocks[block.Hash] = block + + // Check blocks in receivedBlocks if its acks are all in lattice. If a block's + // acking blocks are all in lattice, execute sanity check and add the block + // into lattice. + blocksToAcked := map[common.Hash]*types.Block{} + for { + blocksToLattice := map[common.Hash]*types.Block{} + for _, b := range a.receivedBlocks { + if a.areAllAcksInLattice(b) { + blocksToLattice[b.Hash] = b + } + } + if len(blocksToLattice) == 0 { + break + } + for _, b := range blocksToLattice { + // Sanity check must been executed again here for the case that several + // valid blocks with different content being added into blocksToLattice + // in the same time. For example + // 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 := a.sanityCheck(b); err != nil { + delete(a.blocks, b.Hash) + delete(a.receivedBlocks, b.Hash) + continue + } + a.lattice[b.ProposerID].blocks[b.Height] = b + delete(a.receivedBlocks, b.Hash) + for h := range b.Acks { + bAck := a.blocks[h] + // Update nextAck only when bAck.Height + 1 is greater. A block might + // ack blocks proposed by same validator with different height. + if a.lattice[b.ProposerID].nextAck[bAck.ProposerID] < bAck.Height+1 { + a.lattice[b.ProposerID].nextAck[bAck.ProposerID] = bAck.Height + 1 + } + // Update AckedValidators for each ack blocks and its parents. + for { + if _, exist := bAck.AckedValidators[b.ProposerID]; exist { + break + } + if bAck.Status > types.BlockStatusInit { + break + } + bAck.AckedValidators[b.ProposerID] = struct{}{} + // A block is strongly acked if it is acked by more than + // 2 * (maximum number of byzatine validators) unique validators. + if len(bAck.AckedValidators) > 2*((len(a.lattice)-1)/3) { + blocksToAcked[bAck.Hash] = bAck + } + if bAck.Height == 0 { + break + } + bAck = a.blocks[bAck.ParentHash] + } + } + } + } + + for _, b := range blocksToAcked { + a.ackedBlocks[b.Hash] = b + b.Status = types.BlockStatusAcked + } + + // TODO(haoping): delete blocks in received array when it is received a long + // time ago + + // Delete old blocks in "lattice" and "blocks" for release memory space. + // First, find the height that blocks below it can be deleted. This height + // is defined by finding minimum of validator's nextOutput and last acking + // heights from other validators, i.e. a.lattice[v_other].nextAck[this_vid]. + // This works because blocks of height below this minimum are not going to be + // acked anymore, the ackings of these blocks are illegal. + for vid := range a.lattice { + // Find the minimum height that heights lesser can be deleted. + min := a.lattice[vid].nextOutput + for vid2 := range a.lattice { + if a.lattice[vid2].nextAck[vid] < min { + min = a.lattice[vid2].nextAck[vid] + } + } + // "min" is the height of "next" last acked, min - 1 is the last height. + // Delete blocks from min - 2 which will never be acked. + if min < 3 { + continue + } + min -= 2 + for { + b, exist := a.lattice[vid].blocks[min] + if !exist { + break + } + if b.Status >= types.BlockStatusOrdering { + delete(a.lattice[vid].blocks, b.Height) + delete(a.blocks, b.Hash) + } + if min == 0 { + break + } + min-- + } + } +} + +// ExtractBlocks returns all blocks that can be inserted into total ordering's +// DAG. This function changes the status of blocks from types.BlockStatusAcked +// to types.BlockStatusOrdering. +func (a *Acking) ExtractBlocks() []*types.Block { + ret := []*types.Block{} + for { + updated := false + for vid := range a.lattice { + b, exist := a.lattice[vid].blocks[a.lattice[vid].nextOutput] + if !exist || b.Status < types.BlockStatusAcked { + continue + } + allAcksInOrderingStatus := true + // Check if all acks are in ordering or above status. If a block of an ack + // does not exist means that it deleted but its status is definitely Acked + // or ordering. + for ackHash := range b.Acks { + bAck, exist := a.blocks[ackHash] + if !exist { + continue + } + if bAck.Status < types.BlockStatusOrdering { + allAcksInOrderingStatus = false + break + } + } + if !allAcksInOrderingStatus { + continue + } + updated = true + b.Status = types.BlockStatusOrdering + delete(a.ackedBlocks, b.Hash) + ret = append(ret, b) + a.lattice[vid].nextOutput++ + } + if !updated { + break + } + } + return ret +} + +// AddValidator adds validator in the validator set. +func (a *Acking) AddValidator(h types.ValidatorID) { + a.lattice[h] = &ackingValidatorStatus{ + blocks: make(map[uint64]*types.Block), + nextAck: make(map[types.ValidatorID]uint64), + nextOutput: 0, + restricted: false, + } +} + +// DeleteValidator deletes validator in validator set. +func (a *Acking) DeleteValidator(h types.ValidatorID) { + for h := range a.lattice { + delete(a.lattice[h].nextAck, h) + } + delete(a.lattice, h) +} diff --git a/core/acking_test.go b/core/acking_test.go new file mode 100644 index 0000000..5c89d24 --- /dev/null +++ b/core/acking_test.go @@ -0,0 +1,426 @@ +// Copyright 2018 The dexon-consensus-core Authors +// This file is part of the dexon-consensus-core library. +// +// The dexon-consensus-core library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the License, +// or (at your option) any later version. +// +// The dexon-consensus-core library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the dexon-consensus-core library. If not, see +// . + +package core + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/dexon-foundation/dexon-consensus-core/common" + "github.com/dexon-foundation/dexon-consensus-core/core/types" +) + +type AckingTest struct { + suite.Suite +} + +func (s *AckingTest) SetupSuite() { + +} + +func (s *AckingTest) SetupTest() { + +} + +// genTestCase1 generates test case 1, +// 3 +// | +// 2 +// | \ +// 1 | 1 +// | | | +// 0 0 0 0 (block height) +// 0 1 2 3 (validator) +func genTestCase1(s *AckingTest, a *Acking) []types.ValidatorID { + // Create new Acking instance with 4 validators + var b *types.Block + var h common.Hash + vids := []types.ValidatorID{} + for i := 0; i < 4; i++ { + vid := types.ValidatorID{Hash: common.NewRandomHash()} + a.AddValidator(vid) + vids = append(vids, vid) + } + // Add genesis blocks. + for i := 0; i < 4; i++ { + h = common.NewRandomHash() + b = &types.Block{ + ProposerID: vids[i], + ParentHash: h, + Hash: h, + Height: 0, + Acks: map[common.Hash]struct{}{}, + } + a.ProcessBlock(b) + } + + // Add block 0-1 which acks 0-0. + h = a.lattice[vids[0]].blocks[0].Hash + b = &types.Block{ + ProposerID: vids[0], + ParentHash: h, + Hash: common.NewRandomHash(), + Height: 1, + Acks: map[common.Hash]struct{}{ + h: struct{}{}, + }, + } + a.ProcessBlock(b) + s.NotNil(a.lattice[vids[0]].blocks[1]) + + // Add block 0-2 which acks 0-1 and 1-0. + h = a.lattice[vids[0]].blocks[1].Hash + b = &types.Block{ + ProposerID: vids[0], + ParentHash: h, + Hash: common.NewRandomHash(), + Height: 2, + Acks: map[common.Hash]struct{}{ + h: struct{}{}, + a.lattice[vids[1]].blocks[0].Hash: struct{}{}, + }, + } + a.ProcessBlock(b) + s.NotNil(a.lattice[vids[0]].blocks[2]) + + // Add block 0-3 which acks 0-2. + h = a.lattice[vids[0]].blocks[2].Hash + b = &types.Block{ + ProposerID: vids[0], + ParentHash: h, + Hash: common.NewRandomHash(), + Height: 3, + Acks: map[common.Hash]struct{}{ + h: struct{}{}, + }, + } + a.ProcessBlock(b) + s.NotNil(a.lattice[vids[0]].blocks[3]) + + // Add block 3-1 which acks 3-0. + h = a.lattice[vids[3]].blocks[0].Hash + b = &types.Block{ + ProposerID: vids[3], + ParentHash: h, + Hash: common.NewRandomHash(), + Height: 1, + Acks: map[common.Hash]struct{}{ + h: struct{}{}, + }, + } + a.ProcessBlock(b) + s.NotNil(a.lattice[vids[3]].blocks[0]) + + return vids +} + +func (s *AckingTest) TestAddValidator() { + a := NewAcking() + s.Equal(len(a.lattice), 0) + genTestCase1(s, a) + s.Equal(len(a.lattice), 4) +} + +func (s *AckingTest) TestSanityCheck() { + var b *types.Block + var h common.Hash + var vids []types.ValidatorID + var err error + a := NewAcking() + vids = genTestCase1(s, a) + + // Non-genesis block with no ack, should get error. + b = &types.Block{ + ProposerID: vids[0], + ParentHash: common.NewRandomHash(), + Height: 10, + Acks: make(map[common.Hash]struct{}), + } + err = a.sanityCheck(b) + s.NotNil(err) + s.Equal(ErrNotAckParent.Error(), err.Error()) + + // Non-genesis block which does not ack its parent. + b = &types.Block{ + ProposerID: vids[1], + ParentHash: common.NewRandomHash(), + Height: 1, + Acks: map[common.Hash]struct{}{ + a.lattice[vids[2]].blocks[0].Hash: struct{}{}, + }, + } + err = a.sanityCheck(b) + s.NotNil(err) + s.Equal(ErrNotAckParent.Error(), err.Error()) + + // Non-genesis block which acks its parent but the height is invalid. + h = a.lattice[vids[1]].blocks[0].Hash + b = &types.Block{ + ProposerID: vids[1], + ParentHash: h, + Height: 2, + Acks: map[common.Hash]struct{}{ + h: struct{}{}, + }, + } + err = a.sanityCheck(b) + s.NotNil(err) + s.Equal(ErrInvalidBlockHeight.Error(), err.Error()) + + // Invalid proposer ID. + h = a.lattice[vids[1]].blocks[0].Hash + b = &types.Block{ + ProposerID: types.ValidatorID{Hash: common.NewRandomHash()}, + ParentHash: h, + Height: 1, + Acks: map[common.Hash]struct{}{ + h: struct{}{}, + }, + } + err = a.sanityCheck(b) + s.NotNil(err) + s.Equal(ErrInvalidProposerID.Error(), err.Error()) + + // Fork block. + h = a.lattice[vids[0]].blocks[0].Hash + b = &types.Block{ + ProposerID: vids[0], + ParentHash: h, + Height: 1, + Acks: map[common.Hash]struct{}{ + h: struct{}{}, + }, + } + err = a.sanityCheck(b) + s.NotNil(err) + s.Equal(ErrForkBlock.Error(), err.Error()) + + // Replicated ack. + h = a.lattice[vids[0]].blocks[3].Hash + b = &types.Block{ + ProposerID: vids[0], + ParentHash: h, + Height: 4, + Acks: map[common.Hash]struct{}{ + h: struct{}{}, + a.lattice[vids[1]].blocks[0].Hash: struct{}{}, + }, + } + err = a.sanityCheck(b) + s.NotNil(err) + s.Equal(ErrDoubleAck.Error(), err.Error()) + + // Normal block. + h = a.lattice[vids[1]].blocks[0].Hash + b = &types.Block{ + ProposerID: vids[1], + ParentHash: h, + Height: 1, + Acks: map[common.Hash]struct{}{ + h: struct{}{}, + common.NewRandomHash(): struct{}{}, + }, + } + err = a.sanityCheck(b) + s.Nil(err) +} + +func (s *AckingTest) TestAreAllAcksInLattice() { + var b *types.Block + var vids []types.ValidatorID + a := NewAcking() + vids = genTestCase1(s, a) + + // Empty ack should get true, although won't pass sanity check. + b = &types.Block{ + Acks: map[common.Hash]struct{}{}, + } + s.True(a.areAllAcksInLattice(b)) + + // Acks blocks in lattice + b = &types.Block{ + Acks: map[common.Hash]struct{}{ + a.lattice[vids[0]].blocks[0].Hash: struct{}{}, + a.lattice[vids[0]].blocks[1].Hash: struct{}{}, + }, + } + s.True(a.areAllAcksInLattice(b)) + + // Acks random block hash. + b = &types.Block{ + Acks: map[common.Hash]struct{}{ + common.NewRandomHash(): struct{}{}, + }, + } + s.False(a.areAllAcksInLattice(b)) +} + +func (s *AckingTest) TestStrongAck() { + var b *types.Block + var vids []types.ValidatorID + a := NewAcking() + vids = genTestCase1(s, a) + + // Check block 0-0 to 0-3 before adding 1-1 and 2-1. + for i := uint64(0); i < 4; i++ { + s.Equal(types.BlockStatusInit, a.lattice[vids[0]].blocks[i].Status) + } + + // Add block 1-1 which acks 1-0 and 0-2, and block 0-0 to 0-3 are still + // in BlockStatusInit, because they are not strongly acked. + b = &types.Block{ + ProposerID: vids[1], + ParentHash: a.lattice[vids[1]].blocks[0].Hash, + Hash: common.NewRandomHash(), + Height: 1, + Acks: map[common.Hash]struct{}{ + a.lattice[vids[0]].blocks[2].Hash: struct{}{}, + a.lattice[vids[1]].blocks[0].Hash: struct{}{}, + }, + } + a.ProcessBlock(b) + s.NotNil(a.lattice[vids[1]].blocks[1]) + for i := uint64(0); i < 4; i++ { + s.Equal(types.BlockStatusInit, a.lattice[vids[0]].blocks[i].Status) + } + + // Add block 2-1 which acks 0-2 and 2-0, block 0-0 to 0-2 are strongly acked but + // 0-3 is still not. + b = &types.Block{ + ProposerID: vids[2], + ParentHash: a.lattice[vids[2]].blocks[0].Hash, + Hash: common.NewRandomHash(), + Height: 1, + Acks: map[common.Hash]struct{}{ + a.lattice[vids[0]].blocks[2].Hash: struct{}{}, + a.lattice[vids[2]].blocks[0].Hash: struct{}{}, + }, + } + a.ProcessBlock(b) + s.Equal(types.BlockStatusAcked, a.lattice[vids[0]].blocks[0].Status) + s.Equal(types.BlockStatusAcked, a.lattice[vids[0]].blocks[1].Status) + s.Equal(types.BlockStatusAcked, a.lattice[vids[0]].blocks[2].Status) + s.Equal(types.BlockStatusInit, a.lattice[vids[0]].blocks[3].Status) +} + +func (s *AckingTest) TestExtractBlocks() { + var b *types.Block + a := NewAcking() + vids := genTestCase1(s, a) + + // Add block 1-1 which acks 1-0, 0-2, 3-0. + b = &types.Block{ + ProposerID: vids[1], + ParentHash: a.lattice[vids[1]].blocks[0].Hash, + Hash: common.NewRandomHash(), + Height: 1, + Acks: map[common.Hash]struct{}{ + a.lattice[vids[0]].blocks[2].Hash: struct{}{}, + a.lattice[vids[1]].blocks[0].Hash: struct{}{}, + a.lattice[vids[3]].blocks[0].Hash: struct{}{}, + }, + } + a.ProcessBlock(b) + + // Add block 2-1 which acks 0-2, 2-0, 3-0. + b = &types.Block{ + ProposerID: vids[2], + ParentHash: a.lattice[vids[2]].blocks[0].Hash, + Hash: common.NewRandomHash(), + Height: 1, + Acks: map[common.Hash]struct{}{ + a.lattice[vids[0]].blocks[2].Hash: struct{}{}, + a.lattice[vids[2]].blocks[0].Hash: struct{}{}, + a.lattice[vids[3]].blocks[0].Hash: struct{}{}, + }, + } + a.ProcessBlock(b) + + hashs := []common.Hash{ + a.lattice[vids[0]].blocks[0].Hash, + a.lattice[vids[0]].blocks[1].Hash, + a.lattice[vids[3]].blocks[0].Hash, + } + hashExtracted := map[common.Hash]*types.Block{} + for _, b := range a.ExtractBlocks() { + hashExtracted[b.Hash] = b + s.Equal(types.BlockStatusOrdering, b.Status) + } + for _, h := range hashs { + _, exist := hashExtracted[h] + s.True(exist) + } +} + +func (s *AckingTest) TestRandomIntensiveAcking() { + a := NewAcking() + vids := []types.ValidatorID{} + heights := map[types.ValidatorID]uint64{} + extractedBlocks := []*types.Block{} + + // Generate validators and genesis blocks. + for i := 0; i < 4; i++ { + vid := types.ValidatorID{Hash: common.NewRandomHash()} + a.AddValidator(vid) + vids = append(vids, vid) + h := common.NewRandomHash() + b := &types.Block{ + Hash: h, + ParentHash: h, + Acks: map[common.Hash]struct{}{}, + Height: 0, + ProposerID: vid, + } + a.ProcessBlock(b) + heights[vid] = 1 + } + + for i := 0; i < 5000; i++ { + vid := vids[rand.Int()%len(vids)] + height := heights[vid] + heights[vid]++ + parentHash := a.lattice[vid].blocks[height-1].Hash + acks := map[common.Hash]struct{}{} + for _, vid2 := range vids { + if b, exist := a.lattice[vid2].blocks[a.lattice[vid].nextAck[vid2]]; exist { + acks[b.Hash] = struct{}{} + } + } + b := &types.Block{ + ProposerID: vid, + Hash: common.NewRandomHash(), + ParentHash: parentHash, + Height: height, + Acks: acks, + } + a.ProcessBlock(b) + extractedBlocks = append(extractedBlocks, a.ExtractBlocks()...) + } + + extractedBlocks = append(extractedBlocks, a.ExtractBlocks()...) + // The len of array extractedBlocks should be about 5000. + s.True(len(extractedBlocks) > 4500) + // The len of a.blocks should be small if deleting mechanism works. + s.True(len(a.blocks) < 500) +} + +func TestAcking(t *testing.T) { + suite.Run(t, new(AckingTest)) +} diff --git a/core/blocklattice.go b/core/blocklattice.go index 9b43b67..e4a76cc 100644 --- a/core/blocklattice.go +++ b/core/blocklattice.go @@ -41,7 +41,7 @@ const ( // BlockLattice represents the local view of a single validator. // // blockDB stores blocks that are final. blocks stores blocks that are in ToTo -// State. +// Status. type BlockLattice struct { owner types.ValidatorID ValidatorSet map[types.ValidatorID]struct{} @@ -98,7 +98,7 @@ func (l *BlockLattice) AddValidator( l.phi = 2*l.fmax + 1 // TODO: We should not make genesis blocks 'final' directly. - genesis.State = types.BlockStatusFinal + genesis.Status = types.BlockStatusFinal l.blockDB.Put(*genesis) } @@ -144,7 +144,7 @@ func (l *BlockLattice) processAcks(b *types.Block) { ackedBlock.Ackeds[b.Hash] = struct{}{} bp := ackedBlock - for bp != nil && bp.State < types.BlockStatusAcked { + for bp != nil && bp.Status < types.BlockStatusAcked { if bp.Ackeds == nil { bp.Ackeds = make(map[common.Hash]struct{}) } @@ -160,7 +160,7 @@ func (l *BlockLattice) processAcks(b *types.Block) { } if len(ackedByNodes) > 2*l.fmax { - bp.State = types.BlockStatusAcked + bp.Status = types.BlockStatusAcked l.stronglyAckedSet[bp.Hash] = bp } bp = l.getBlock(bp.ParentHash) @@ -170,7 +170,7 @@ func (l *BlockLattice) processAcks(b *types.Block) { populateAckBy = func(bx, target *types.Block) { for ab := range bx.Acks { abb := l.getBlock(ab) - if abb.State < types.BlockStatusFinal { + if abb.Status < types.BlockStatusFinal { if abb.Ackeds == nil { abb.Ackeds = make(map[common.Hash]struct{}) } @@ -220,7 +220,7 @@ func (l *BlockLattice) isValidAckCandidate(b *types.Block) bool { if bx == nil { return false } - if bx.State == types.BlockStatusFinal { + if bx.Status == types.BlockStatusFinal { return true } } @@ -281,11 +281,11 @@ IterateStronglyAckedSet: for bpHash, bp := range l.stronglyAckedSet { for ackBlockHash := range bp.Acks { bx := l.getBlock(ackBlockHash) - if bx == nil || bx.State < types.BlockStatusAcked { + if bx == nil || bx.Status < types.BlockStatusAcked { break IterateStronglyAckedSet } } - bp.State = types.BlockStatusToTo + bp.Status = types.BlockStatusOrdering l.pendingSet[bp.Hash] = bp delete(l.stronglyAckedSet, bpHash) @@ -340,7 +340,7 @@ func (l *BlockLattice) calculateABSofBlock(b *types.Block) { calculateABSRecursive = func(target *types.Block) { for hash := range target.Ackeds { ackedByBlock := l.getBlock(hash) - if ackedByBlock.State != types.BlockStatusToTo { + if ackedByBlock.Status != types.BlockStatusOrdering { continue } v, exists := l.ABS[b.Hash][ackedByBlock.ProposerID] @@ -398,7 +398,7 @@ func (l *BlockLattice) totalOrdering(b *types.Block) { acksOnlyFinal := true for ackedBlockHash := range b.Acks { bp := l.getBlock(ackedBlockHash) - if bp.State != types.BlockStatusFinal { + if bp.Status != types.BlockStatusFinal { acksOnlyFinal = false break } @@ -515,7 +515,7 @@ func (l *BlockLattice) totalOrdering(b *types.Block) { var output []*types.Block for hash, x := range precedingSet { output = append(output, x) - x.State = types.BlockStatusFinal + x.Status = types.BlockStatusFinal // Remove from pending set and candidate set. delete(l.pendingSet, hash) @@ -543,7 +543,7 @@ func (l *BlockLattice) totalOrdering(b *types.Block) { acksOnlyFinal := true for ackedBlockHash := range block.Acks { bp := l.getBlock(ackedBlockHash) - if bp.State != types.BlockStatusFinal { + if bp.Status != types.BlockStatusFinal { acksOnlyFinal = false break } diff --git a/core/blocklattice_test.go b/core/blocklattice_test.go index be332db..3dd5a3b 100644 --- a/core/blocklattice_test.go +++ b/core/blocklattice_test.go @@ -244,7 +244,7 @@ func (s *BlockLatticeTest) SetupTest() { Debugf("\n") } -func (s *BlockLatticeTest) TestAckAndStateTransition() { +func (s *BlockLatticeTest) TestAckAndStatusTransition() { // Recieve Order: // B01 -> B12 -> B11 -> B21 -> B31 -> B02 -> B32 -> B22 -> B13 -> B33 // -> B03 -> B23 @@ -311,7 +311,7 @@ func (s *BlockLatticeTest) TestAckAndStateTransition() { s.Require().Equal(1, len(lattice.pendingSet)) s.Require().NotNil(lattice.pendingSet[b01.Hash]) - s.Require().Equal(types.BlockStatusToTo, b01.State) + s.Require().Equal(types.BlockStatusOrdering, b01.Status) // b01 is acked. s.Require().Equal(4, len(b01.Ackeds)) @@ -390,10 +390,10 @@ func (s *BlockLatticeTest) TestAckAndStateTransition() { s.Require().NotNil(lattice.pendingSet[b11.Hash]) s.Require().NotNil(lattice.pendingSet[b21.Hash]) s.Require().NotNil(lattice.pendingSet[b31.Hash]) - s.Require().Equal(types.BlockStatusToTo, b01.State) - s.Require().Equal(types.BlockStatusToTo, b11.State) - s.Require().Equal(types.BlockStatusToTo, b21.State) - s.Require().Equal(types.BlockStatusToTo, b31.State) + s.Require().Equal(types.BlockStatusOrdering, b01.Status) + s.Require().Equal(types.BlockStatusOrdering, b11.Status) + s.Require().Equal(types.BlockStatusOrdering, b21.Status) + s.Require().Equal(types.BlockStatusOrdering, b31.Status) // b02 is acked. s.Require().Equal(2, len(b02.Ackeds)) diff --git a/core/types/block.go b/core/types/block.go index 6762f14..217ea73 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -25,14 +25,14 @@ import ( "github.com/dexon-foundation/dexon-consensus-core/common" ) -// State represents the block process state. -type State int +// Status represents the block process state. +type Status int // Block Status. const ( - BlockStatusInit State = iota + BlockStatusInit Status = iota BlockStatusAcked - BlockStatusToTo + BlockStatusOrdering BlockStatusFinal ) @@ -45,8 +45,9 @@ type Block struct { Timestamps map[ValidatorID]time.Time `json:"timestamps"` Acks map[common.Hash]struct{} `json:"acks"` - Ackeds map[common.Hash]struct{} `json:"-"` - State State `json:"-"` + Ackeds map[common.Hash]struct{} `json:"-"` + AckedValidators map[ValidatorID]struct{} `json:"-"` + Status Status `json:"-"` } func (b *Block) String() string { -- cgit v1.2.3