aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJimmy Hu <jimmy.hu@dexon.org>2018-11-20 08:40:16 +0800
committerGitHub <noreply@github.com>2018-11-20 08:40:16 +0800
commitc04e6890b601fdca3e30a716121689562cfca5d7 (patch)
treebcb1255864aae8a88ddff0901f2047abbd15ab74
parent245e5aca956c840246d81ccd49ca5e70a96cd128 (diff)
downloaddexon-consensus-c04e6890b601fdca3e30a716121689562cfca5d7.tar
dexon-consensus-c04e6890b601fdca3e30a716121689562cfca5d7.tar.gz
dexon-consensus-c04e6890b601fdca3e30a716121689562cfca5d7.tar.bz2
dexon-consensus-c04e6890b601fdca3e30a716121689562cfca5d7.tar.lz
dexon-consensus-c04e6890b601fdca3e30a716121689562cfca5d7.tar.xz
dexon-consensus-c04e6890b601fdca3e30a716121689562cfca5d7.tar.zst
dexon-consensus-c04e6890b601fdca3e30a716121689562cfca5d7.zip
core: Block randomness will be processed again in finalized block (#338)
* Process randomness result in finalized block if missed * Add test for missing block randomness
-rw-r--r--core/compaction-chain.go71
-rw-r--r--core/compaction-chain_test.go148
-rw-r--r--core/consensus.go24
3 files changed, 165 insertions, 78 deletions
diff --git a/core/compaction-chain.go b/core/compaction-chain.go
index 4a5ba36..f6bc014 100644
--- a/core/compaction-chain.go
+++ b/core/compaction-chain.go
@@ -33,6 +33,10 @@ var (
"block not registered")
ErrNotInitiazlied = fmt.Errorf(
"not initialized")
+ ErrTSigNotReady = fmt.Errorf(
+ "tsig not ready")
+ ErrIncorrectBlockRandomnessResult = fmt.Errorf(
+ "incorrect block randomness result")
)
type finalizedBlockHeap = types.ByFinalizationHeight
@@ -154,6 +158,24 @@ func (cc *compactionChain) extractBlocks() []*types.Block {
return deliveringBlocks
}
+func (cc *compactionChain) verifyRandomness(
+ blockHash common.Hash, round uint64, randomness []byte) (bool, error) {
+ if round == 0 {
+ return len(randomness) == 0, nil
+ }
+ // Randomness is not available at round 0.
+ v, ok, err := cc.tsigVerifier.UpdateAndGet(round)
+ if err != nil {
+ return false, err
+ }
+ if !ok {
+ return false, ErrTSigNotReady
+ }
+ return v.VerifySignature(blockHash, crypto.Signature{
+ Type: "bls",
+ Signature: randomness}), nil
+}
+
func (cc *compactionChain) processFinalizedBlock(block *types.Block) {
if block.Finalization.Height <= cc.lastBlock().Finalization.Height {
return
@@ -166,6 +188,19 @@ func (cc *compactionChain) processFinalizedBlock(block *types.Block) {
cc.lock.Lock()
defer cc.lock.Unlock()
+ // The randomness result is missed previously.
+ if cc.blockRegisteredNoLock(block.Hash) {
+ ok, err := cc.verifyRandomness(
+ block.Hash, block.Position.Round, block.Finalization.Randomness)
+ if err != nil {
+ panic(err)
+ }
+ if ok {
+ cc.blockRandomness[block.Hash] = block.Finalization.Randomness
+ }
+ return
+ }
+
heap.Push(cc.pendingFinalizedBlocks, block)
return
@@ -206,22 +241,14 @@ func (cc *compactionChain) extractFinalizedBlocks() []*types.Block {
b.Finalization.Height == prevBlock.Finalization.Height {
continue
}
- round := b.Position.Round
- if round != 0 {
- // Randomness is not available at round 0.
- v, ok, err := cc.tsigVerifier.UpdateAndGet(round)
- if err != nil {
- continue
- }
- if !ok {
- toPending = append(toPending, b)
- continue
- }
- if ok := v.VerifySignature(b.Hash, crypto.Signature{
- Type: "bls",
- Signature: b.Finalization.Randomness}); !ok {
- continue
- }
+ ok, err := cc.verifyRandomness(
+ b.Hash, b.Position.Round, b.Finalization.Randomness)
+ if err != nil {
+ toPending = append(toPending, b)
+ continue
+ }
+ if !ok {
+ continue
}
// Fork resolution: choose block with smaller hash.
if prevBlock.Finalization.Height == b.Finalization.Height {
@@ -261,11 +288,19 @@ func (cc *compactionChain) processBlockRandomnessResult(
rand *types.BlockRandomnessResult) error {
cc.lock.Lock()
defer cc.lock.Unlock()
- // TODO(jimmy-dexon): the result should not be discarded here. Blocks may
- // be registered later.
if !cc.blockRegisteredNoLock(rand.BlockHash) {
+ // If the randomness result is discarded here, it'll later be processed by
+ //finalized block
return ErrBlockNotRegistered
}
+ ok, err := cc.verifyRandomness(
+ rand.BlockHash, rand.Position.Round, rand.Randomness)
+ if err != nil {
+ return err
+ }
+ if !ok {
+ return ErrIncorrectBlockRandomnessResult
+ }
cc.blockRandomness[rand.BlockHash] = rand.Randomness
return nil
}
diff --git a/core/compaction-chain_test.go b/core/compaction-chain_test.go
index 2767d2d..586c89c 100644
--- a/core/compaction-chain_test.go
+++ b/core/compaction-chain_test.go
@@ -35,7 +35,28 @@ type CompactionChainTestSuite struct {
func (s *CompactionChainTestSuite) SetupTest() {
}
-func (s *CompactionChainTestSuite) newCompactionChain() *compactionChain {
+type mockTSigVerifier struct {
+ defaultRet bool
+ ret map[common.Hash]bool
+}
+
+func newMockTSigVerifier(defaultRet bool) *mockTSigVerifier {
+ return &mockTSigVerifier{
+ defaultRet: defaultRet,
+ ret: make(map[common.Hash]bool),
+ }
+}
+
+func (m *mockTSigVerifier) VerifySignature(
+ hash common.Hash, _ crypto.Signature) bool {
+ if ret, exist := m.ret[hash]; exist {
+ return ret
+ }
+ return m.defaultRet
+}
+
+func (s *CompactionChainTestSuite) newCompactionChain() (
+ *compactionChain, *mockTSigVerifier) {
_, pubKeys, err := test.NewKeys(4)
s.Require().NoError(err)
gov, err := test.NewGovernance(
@@ -43,7 +64,13 @@ func (s *CompactionChainTestSuite) newCompactionChain() *compactionChain {
s.Require().NoError(err)
cc := newCompactionChain(gov)
cc.init(&types.Block{})
- return cc
+
+ mock := newMockTSigVerifier(true)
+ for i := 0; i < cc.tsigVerifier.cacheSize; i++ {
+ cc.tsigVerifier.verifier[uint64(i)] = mock
+ }
+
+ return cc, mock
}
func (s *CompactionChainTestSuite) generateBlocks(
@@ -67,7 +94,7 @@ func (s *CompactionChainTestSuite) generateBlocks(
}
func (s *CompactionChainTestSuite) TestProcessBlock() {
- cc := s.newCompactionChain()
+ cc, _ := s.newCompactionChain()
now := time.Now().UTC()
blocks := make([]*types.Block, 10)
for idx := range blocks {
@@ -86,7 +113,7 @@ func (s *CompactionChainTestSuite) TestProcessBlock() {
}
func (s *CompactionChainTestSuite) TestExtractBlocks() {
- cc := s.newCompactionChain()
+ cc, _ := s.newCompactionChain()
s.Require().Equal(uint32(4), cc.gov.Configuration(uint64(0)).NumChains)
blocks := make([]*types.Block, 10)
for idx := range blocks {
@@ -108,6 +135,7 @@ func (s *CompactionChainTestSuite) TestExtractBlocks() {
s.Require().NoError(cc.processBlockRandomnessResult(
&types.BlockRandomnessResult{
BlockHash: blocks[i].Hash,
+ Position: blocks[i].Position,
Randomness: h[:],
}))
}
@@ -128,6 +156,7 @@ func (s *CompactionChainTestSuite) TestExtractBlocks() {
s.Require().NoError(cc.processBlockRandomnessResult(
&types.BlockRandomnessResult{
BlockHash: blocks[i].Hash,
+ Position: blocks[i].Position,
Randomness: h[:],
}))
}
@@ -144,6 +173,7 @@ func (s *CompactionChainTestSuite) TestExtractBlocks() {
s.Require().NoError(cc.processBlockRandomnessResult(
&types.BlockRandomnessResult{
BlockHash: blocks[i].Hash,
+ Position: blocks[i].Position,
Randomness: h[:],
}))
}
@@ -156,6 +186,7 @@ func (s *CompactionChainTestSuite) TestExtractBlocks() {
s.Require().NoError(cc.processBlockRandomnessResult(
&types.BlockRandomnessResult{
BlockHash: blocks[i].Hash,
+ Position: blocks[i].Position,
Randomness: h[:],
}))
}
@@ -174,8 +205,77 @@ func (s *CompactionChainTestSuite) TestExtractBlocks() {
}
}
+func (s *CompactionChainTestSuite) TestMissedRandomness() {
+ cc, _ := s.newCompactionChain()
+ s.Require().Equal(uint32(4), cc.gov.Configuration(uint64(0)).NumChains)
+ blocks := make([]*types.Block, 10)
+ for idx := range blocks {
+ blocks[idx] = &types.Block{
+ Hash: common.NewRandomHash(),
+ Position: types.Position{
+ Round: 1,
+ Height: uint64(idx / 4),
+ ChainID: uint32(idx % 4),
+ },
+ }
+ s.Require().False(cc.blockRegistered(blocks[idx].Hash))
+ cc.registerBlock(blocks[idx])
+ s.Require().True(cc.blockRegistered(blocks[idx].Hash))
+ }
+ for i := range blocks {
+ s.Require().NoError(cc.processBlock(blocks[i]))
+ if i >= 4 && i < 6 {
+ h := common.NewRandomHash()
+ s.Require().NoError(cc.processBlockRandomnessResult(
+ &types.BlockRandomnessResult{
+ BlockHash: blocks[i].Hash,
+ Position: blocks[i].Position,
+ Randomness: h[:],
+ }))
+ }
+ }
+ s.Require().Len(cc.extractBlocks(), 0)
+
+ for i := range blocks {
+ if i >= 4 {
+ break
+ }
+ block := blocks[i].Clone()
+ h := common.NewRandomHash()
+ block.Finalization.Randomness = h[:]
+ block.Finalization.Height = uint64(i + 1)
+ cc.processFinalizedBlock(block)
+ }
+ delivered := cc.extractBlocks()
+ s.Require().Len(delivered, 6)
+
+ for i := 6; i < 10; i++ {
+ h := common.NewRandomHash()
+ s.Require().NoError(cc.processBlockRandomnessResult(
+ &types.BlockRandomnessResult{
+ BlockHash: blocks[i].Hash,
+ Position: blocks[i].Position,
+ Randomness: h[:],
+ }))
+ }
+
+ delivered = append(delivered, cc.extractBlocks()...)
+ s.Require().Len(delivered, 10)
+
+ // The delivered order should be the same as processing order.
+ for i, block := range delivered {
+ if i > 1 {
+ s.Equal(delivered[i-1].Finalization.Height+1,
+ delivered[i].Finalization.Height)
+ s.Equal(delivered[i-1].Hash,
+ delivered[i].Finalization.ParentHash)
+ }
+ s.Equal(block.Hash, blocks[i].Hash)
+ }
+}
+
func (s *CompactionChainTestSuite) TestExtractBlocksRound0() {
- cc := s.newCompactionChain()
+ cc, _ := s.newCompactionChain()
s.Require().Equal(uint32(4), cc.gov.Configuration(uint64(0)).NumChains)
blocks := make([]*types.Block, 10)
for idx := range blocks {
@@ -209,32 +309,8 @@ func (s *CompactionChainTestSuite) TestExtractBlocksRound0() {
}
}
-type mockTSigVerifier struct {
- defaultRet bool
- ret map[common.Hash]bool
-}
-
-func newMockTSigVerifier(defaultRet bool) *mockTSigVerifier {
- return &mockTSigVerifier{
- defaultRet: defaultRet,
- ret: make(map[common.Hash]bool),
- }
-}
-
-func (m *mockTSigVerifier) VerifySignature(
- hash common.Hash, _ crypto.Signature) bool {
- if ret, exist := m.ret[hash]; exist {
- return ret
- }
- return m.defaultRet
-}
-
func (s *CompactionChainTestSuite) TestSyncFinalizedBlock() {
- cc := s.newCompactionChain()
- mock := newMockTSigVerifier(true)
- for i := 0; i < cc.tsigVerifier.cacheSize; i++ {
- cc.tsigVerifier.verifier[uint64(i)] = mock
- }
+ cc, mock := s.newCompactionChain()
now := time.Now().UTC()
blocks := make([]*types.Block, 10)
for idx := range blocks {
@@ -326,11 +402,7 @@ func (s *CompactionChainTestSuite) TestSyncFinalizedBlock() {
}
func (s *CompactionChainTestSuite) TestSync() {
- cc := s.newCompactionChain()
- mock := newMockTSigVerifier(true)
- for i := 0; i < cc.tsigVerifier.cacheSize; i++ {
- cc.tsigVerifier.verifier[uint64(i)] = mock
- }
+ cc, _ := s.newCompactionChain()
now := time.Now().UTC()
blocks := make([]*types.Block, 20)
for idx := range blocks {
@@ -384,13 +456,9 @@ func (s *CompactionChainTestSuite) TestSync() {
}
func (s *CompactionChainTestSuite) TestBootstrapSync() {
- cc := s.newCompactionChain()
+ cc, _ := s.newCompactionChain()
numChains := cc.gov.Configuration(uint64(0)).NumChains
s.Require().Equal(uint32(4), numChains)
- mock := newMockTSigVerifier(true)
- for i := 0; i < cc.tsigVerifier.cacheSize; i++ {
- cc.tsigVerifier.verifier[uint64(i)] = mock
- }
now := time.Now().UTC()
blocks := make([]*types.Block, 20)
for idx := range blocks {
diff --git a/core/consensus.go b/core/consensus.go
index 215b9f7..e09ee25 100644
--- a/core/consensus.go
+++ b/core/consensus.go
@@ -56,8 +56,6 @@ var (
"incorrect vote position")
ErrIncorrectVoteProposer = fmt.Errorf(
"incorrect vote proposer")
- ErrIncorrectBlockRandomnessResult = fmt.Errorf(
- "incorrect block randomness result")
)
// consensusBAReceiver implements agreementReceiver.
@@ -936,31 +934,17 @@ func (con *Consensus) ProcessBlockRandomnessResult(
if rand.Position.Round == 0 {
return nil
}
- if !con.ccModule.blockRegistered(rand.BlockHash) {
- return nil
- }
- round := rand.Position.Round
- v, ok, err := con.ccModule.tsigVerifier.UpdateAndGet(round)
- if err != nil {
+ if err := con.ccModule.processBlockRandomnessResult(rand); err != nil {
+ if err == ErrBlockNotRegistered {
+ err = nil
+ }
return err
}
- if !ok {
- return nil
- }
- if !v.VerifySignature(
- rand.BlockHash, crypto.Signature{Signature: rand.Randomness}) {
- return ErrIncorrectBlockRandomnessResult
- }
con.logger.Debug("Calling Network.BroadcastRandomnessResult",
"hash", rand.BlockHash,
"position", &rand.Position,
"randomness", hex.EncodeToString(rand.Randomness))
con.network.BroadcastRandomnessResult(rand)
- if err := con.ccModule.processBlockRandomnessResult(rand); err != nil {
- if err != ErrBlockNotRegistered {
- return err
- }
- }
return nil
}