Skip to content

Commit

Permalink
backport: v0.7.1 into v0.8-dev (#361)
Browse files Browse the repository at this point in the history
* Update golangci-lint-action and golang-ci versions.

Also specify Go toolchain version in actions (now required).

(cherry picked from commit 14461339f43029e1cfedf799fa8a60ffcf1f5b69)

* fix(consensus): network stuck due to outdated proposal block (#327)

* fix(consensus): network stuck due to outdated proposal block

* refactor(consensus): cleanup  TestStateProposalTime

* fix(xchacha20poly1305_test): (*testing.common).Errorf does not support error-wrapping directive %w

* fix(consensus): fix race condition

* chore(consensus): apply peer review feedback

* fix: don't process WAL logs for old rounds (#331)

* feat: add the implementation of WAL iterator with a feature skipping messages till the final round

* feat: change order and name of walIter fields

* feat: stop iterating if reader returns error

* feat: remove checking on allowed message types

* feat: improve was_iter_test.go

* refactor: modify by go-lint feedback

* refactor: remove useless expected length

* chore: disable lint for a non-critical line in a test

* chore(release): update changelog and bump version to 0.7.1-dev.1 (#330)

* chore(release): update changelog generation config

* chore(release): update changelog and version to 0.7.1-dev.1

* build(release): implement full release workflow in the release script (#332) (#345)

* chore(release): update changelog generation config

* build(release): improve version release script

* feat(release): release script now creates GitHub release

* Update golangci-lint-action and golang-ci versions.

Also specify Go toolchain version in actions (now required).

(cherry picked from commit 14461339f43029e1cfedf799fa8a60ffcf1f5b69)

* fix(consensus): network stuck due to outdated proposal block (#327)

* fix(consensus): network stuck due to outdated proposal block

* refactor(consensus): cleanup  TestStateProposalTime

* fix(xchacha20poly1305_test): (*testing.common).Errorf does not support error-wrapping directive %w

* fix(consensus): fix race condition

* chore(consensus): apply peer review feedback

* fix: don't process WAL logs for old rounds (#331)

* feat: add the implementation of WAL iterator with a feature skipping messages till the final round

* feat: change order and name of walIter fields

* feat: stop iterating if reader returns error

* feat: remove checking on allowed message types

* feat: improve was_iter_test.go

* refactor: modify by go-lint feedback

* refactor: remove useless expected length

* chore: disable lint for a non-critical line in a test

* chore(release): update changelog and bump version to 0.7.1-dev.1 (#330)

* chore(release): update changelog generation config

* chore(release): update changelog and version to 0.7.1-dev.1

* build(release): implement full release workflow in the release script (#332) (#345)

* chore(release): update changelog generation config

* build(release): improve version release script

* feat(release): release script now creates GitHub release

* chore(release): update changelog and version to 0.7.1

* chore: if the tenderdash source code is not tracked by git then cloning "develop_0.1" branch as fallback scenario to build a project (#355)

* fix: remove duplicate method BlockID

Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
Co-authored-by: lklimek <842586+lklimek@users.noreply.github.com>
  • Loading branch information
3 people committed Apr 22, 2022
1 parent 5faeef9 commit c58b60d
Show file tree
Hide file tree
Showing 9 changed files with 427 additions and 30 deletions.
19 changes: 17 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,21 @@
- Bump docker/login-action from 1.13.0 to 1.14.1
- Bump golangci/golangci-lint-action from 2.5.2 to 3.1.0

## [0.7.1] - 2022-04-13

### Build

- Implement full release workflow in the release script (#332) (#345)

### Bug Fixes

- Network stuck due to outdated proposal block (#327)
- Don't process WAL logs for old rounds (#331)

### Miscellaneous Tasks

- Update changelog generation config

## [0.7.0] - 2022-01-24

### Miscellaneous Tasks
Expand Down Expand Up @@ -1681,7 +1696,7 @@
- Only run when applicable (#4752)
- Check git diff on each job (#4770)
- Checkout code before git diff check (#4779)
- Add paths
- Add paths
- Bump the timeout for test_coverage (#4864)
- Migrate localnet to github actions (#4878)
- Add timeouts (#4912)
Expand Down Expand Up @@ -3451,7 +3466,7 @@
### Swagger

- Update swagger port (#4498)
- Remove duplicate blockID
- Remove duplicate blockID
- Define version (#4952)
- Update (#5257)

Expand Down
8 changes: 4 additions & 4 deletions crypto/xchacha20poly1305/xchachapoly_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ func TestRandom(t *testing.T) {
plaintext := make([]byte, pl)
_, err := crand.Read(key[:])
if err != nil {
t.Errorf("error on read: %v", err)
t.Errorf("error on read: %s", err)
}
_, err = crand.Read(nonce[:])
if err != nil {
t.Errorf("error on read: %v", err)
t.Errorf("error on read: %s", err)
}
_, err = crand.Read(ad)
if err != nil {
t.Errorf("error on read: %v", err)
t.Errorf("error on read: %s", err)
}
_, err = crand.Read(plaintext)
if err != nil {
t.Errorf("error on read: %v", err)
t.Errorf("error on read: %s", err)
}

aead, err := New(key[:])
Expand Down
27 changes: 10 additions & 17 deletions internal/consensus/replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,29 +157,22 @@ func (cs *State) catchupReplay(csHeight int64) error {

cs.Logger.Info("Catchup by replaying consensus messages", "height", csHeight)

var msg *TimedWALMessage
dec := WALDecoder{gr}

LOOP:
for {
msg, err = dec.Decode()
switch {
case err == io.EOF:
break LOOP
case IsDataCorruptionError(err):
cs.Logger.Error("data has been corrupted in last height of consensus WAL", "err", err, "height", csHeight)
return err
case err != nil:
return err
}

iter := newWalIter(&WALDecoder{gr})
for iter.Next() {
// NOTE: since the priv key is set when the msgs are received
// it will attempt to eg double sign but we can just ignore it
// since the votes will be replayed and we'll get to the next step
if err := cs.readReplayMessage(msg, nil); err != nil {
if err := cs.readReplayMessage(iter.Value(), nil); err != nil {
return err
}
}
err = iter.Err()
if err != nil {
if IsDataCorruptionError(err) {
cs.Logger.Error("data has been corrupted in last height of consensus WAL", "err", err, "height", csHeight)
}
return err
}
cs.Logger.Info("Replay: Done")
return nil
}
Expand Down
28 changes: 24 additions & 4 deletions internal/consensus/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,7 @@ func (cs *State) receiveRoutine(maxSteps int) {
}
}

rs := cs.RoundState
rs := cs.GetRoundState()
var mi msgInfo

select {
Expand Down Expand Up @@ -930,7 +930,7 @@ func (cs *State) receiveRoutine(maxSteps int) {

// if the timeout is relevant to the rs
// go to the next step
cs.handleTimeout(ti, rs)
cs.handleTimeout(ti, *rs)

case <-cs.Quit():
onExit(cs)
Expand Down Expand Up @@ -1302,12 +1302,31 @@ func (cs *State) isProposer(proTxHash crypto.ProTxHash) bool {
return bytes.Equal(cs.Validators.GetProposer().ProTxHash.Bytes(), proTxHash.Bytes())
}

// checkValidBlock returns true if cs.ValidBlock is set and still valid (not expired)
func (cs *State) checkValidBlock() bool {
if cs.ValidBlock == nil {
return false
}
if err := cs.blockExec.ValidateBlockTime(cs.config.ProposedBlockTimeWindow, cs.state, cs.ValidBlock); err != nil {
cs.Logger.Debug(
"proposal block is outdated",
"height", cs.Height,
"round", cs.Round,
"error", err,
"block", cs.ValidBlock)

return false
}

return true
}

func (cs *State) defaultDecideProposal(height int64, round int32) {
var block *types.Block
var blockParts *types.PartSet

// Decide on block
if cs.ValidBlock != nil {
if cs.checkValidBlock() {
// If there is valid block, choose that.
block, blockParts = cs.ValidBlock, cs.ValidBlockParts
} else {
Expand All @@ -1325,7 +1344,8 @@ func (cs *State) defaultDecideProposal(height int64, round int32) {
}

// Make proposal
propBlockID := types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()}
propBlockID := block.BlockID()
propBlockID.PartSetHeader = blockParts.Header()
proposedChainLockHeight := cs.state.LastCoreChainLockedBlockHeight
if cs.blockExec.NextCoreChainLock != nil && cs.blockExec.NextCoreChainLock.CoreBlockHeight > proposedChainLockHeight {
proposedChainLockHeight = cs.blockExec.NextCoreChainLock.CoreBlockHeight
Expand Down
76 changes: 76 additions & 0 deletions internal/consensus/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
"strconv"
"testing"
"time"

Expand Down Expand Up @@ -255,6 +256,81 @@ func TestStateBadProposal(t *testing.T) {
signAddVotes(config, cs1, tmproto.PrecommitType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
}

// TestStateProposalTime tries to sign and vote on proposal with invalid time.
func TestStateProposalTime(t *testing.T) {
config := configSetup(t)

cs1, _, _ := randState(config, 1)
height, round := cs1.Height, cs1.Round
cs1.config.ProposedBlockTimeWindow = 1 * time.Second
cs1.config.DontAutoPropose = true
cs1.config.CreateEmptyBlocksInterval = 0

newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)

cs1.mtx.Lock()
startTestRound(cs1, height, round)
cs1.mtx.Unlock()

// Wait for new round so proposer is set.
ensureNewRound(newRoundCh, height, round)

testCases := []struct {
blockTimeFunc func(*State) time.Time
sleep time.Duration
expectNewBlock bool
}{
{ // TEST 0: EVERYTHING GOES FINE
expectNewBlock: false,
},
{ // TEST 1: BLOCK TIME IS IN FUTURE
blockTimeFunc: func(s *State) time.Time { return time.Now().Add(s.config.ProposedBlockTimeWindow + 24*time.Hour) },
expectNewBlock: true,
},
{ // TEST 2: BLOCK TIME IS OLDER THAN PREVIOUS BLOCK TIME
blockTimeFunc: func(s *State) time.Time { return s.state.LastBlockTime.Add(-1 * time.Second) },
expectNewBlock: true,
},
{ // TEST 3: BLOCK TIME IS IN THE PAST, PROPOSAL IS NEW
blockTimeFunc: nil,
sleep: cs1.config.ProposedBlockTimeWindow + 1*time.Millisecond,
expectNewBlock: true,
},
}

for id, tc := range testCases {
t.Run(strconv.Itoa(id), func(t *testing.T) {
height, round = cs1.Height, cs1.Round
cs := cs1
// Generate proposal block
cs.mtx.Lock()
propBlock, propBlockParts := cs.createProposalBlock()
if tc.blockTimeFunc != nil {
propBlock.Time = tc.blockTimeFunc(cs)
}
blockID := propBlock.BlockID()

cs.ValidBlock = propBlock
cs.ValidBlockParts = propBlockParts

// sleep if needed
time.Sleep(tc.sleep)

// Wait for complete proposal.
cs.enterPropose(height, round)
cs.mtx.Unlock()

ensureNewRound(newRoundCh, height+1, 0)

if tc.expectNewBlock {
assert.NotEqual(t, blockID, cs.LastCommit.BlockID, "expected that block will be regenerated")
} else {
assert.Equal(t, blockID, cs.LastCommit.BlockID, "expected that block will not change")
}
})
}
}

func TestStateOversizedBlock(t *testing.T) {
config := configSetup(t)

Expand Down
105 changes: 105 additions & 0 deletions internal/consensus/wal_iter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package consensus

import (
"io"

"github.com/tendermint/tendermint/types"
)

type walReader interface {
Decode() (*TimedWALMessage, error)
}

type walIter struct {
reader walReader
curHeight int64
curRound int32
queue []*TimedWALMessage
hold []*TimedWALMessage
msg *TimedWALMessage
err error
}

func newWalIter(reader walReader) *walIter {
return &walIter{
reader: reader,
}
}

// Value takes a top element from a queue, otherwise returns nil if the queue is empty
func (i *walIter) Value() *TimedWALMessage {
if len(i.queue) == 0 {
return nil
}
msg := i.queue[0]
i.queue = i.queue[1:]
return msg
}

// Next reads a next message from WAL, every message holds until reach a next height
// if the read message is Propose with the "round" greater than previous, then held messages are flush
func (i *walIter) Next() bool {
if len(i.queue) > 0 {
return true
}
if i.err != nil {
return false
}
for len(i.queue) == 0 && i.readMsg() {
if !i.processMsg(i.msg) {
return false
}
i.hold = append(i.hold, i.msg)
}
if len(i.queue) == 0 {
i.queue = i.hold
}
return len(i.queue) > 0
}

// Err returns an error if got the error is not io.EOF otherwise returns nil
func (i *walIter) Err() error {
if i.err == io.EOF {
return nil
}
return i.err
}

func (i *walIter) readMsg() bool {
if i.err == io.EOF {
return false
}
i.msg, i.err = i.reader.Decode()
if i.err == io.EOF {
return false
}
if i.err != nil {
return false
}
return true
}

func (i *walIter) processMsg(msg *TimedWALMessage) bool {
m, ok := msg.Msg.(msgInfo)
if !ok {
return true
}
mi, ok := m.Msg.(*ProposalMessage)
if ok {
i.processProposal(mi.Proposal)
}
return true
}

func (i *walIter) processProposal(p *types.Proposal) {
if p.Height == i.curHeight && i.curRound < p.Round {
i.hold = nil
i.curRound = p.Round
}
if p.Height > i.curHeight {
i.curHeight = p.Height
i.curRound = p.Round
i.queue = i.hold
i.hold = nil
}
}

0 comments on commit c58b60d

Please sign in to comment.